summaryrefslogtreecommitdiff
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/Kconfig18
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/adi/Kconfig9
-rw-r--r--sound/soc/amd/Kconfig3
-rw-r--r--sound/soc/amd/acp-da7219-max98357a.c8
-rw-r--r--sound/soc/amd/acp-es8336.c4
-rw-r--r--sound/soc/amd/acp-rt5645.c6
-rw-r--r--sound/soc/amd/acp/Kconfig1
-rw-r--r--sound/soc/amd/acp/acp-i2s.c3
-rw-r--r--sound/soc/amd/acp/acp-legacy-common.c2
-rw-r--r--sound/soc/amd/acp/acp-mach-common.c1
-rw-r--r--sound/soc/amd/acp/acp-pci.c9
-rw-r--r--sound/soc/amd/acp/acp-platform.c2
-rw-r--r--sound/soc/amd/acp/acp-sdw-legacy-mach.c4
-rw-r--r--sound/soc/amd/acp/acp-sdw-sof-mach.c10
-rw-r--r--sound/soc/amd/acp/acp-sof-mach.c2
-rw-r--r--sound/soc/amd/acp/acp70.c1
-rw-r--r--sound/soc/amd/acp/acp_common.h1
-rw-r--r--sound/soc/amd/acp/amd-acp70-acpi-match.c12
-rw-r--r--sound/soc/amd/acp/amd-acpi-mach.c4
-rw-r--r--sound/soc/amd/acp/amd.h8
-rw-r--r--sound/soc/amd/mach-config.h1
-rw-r--r--sound/soc/amd/ps/acp63.h4
-rw-r--r--sound/soc/amd/ps/ps-common.c18
-rw-r--r--sound/soc/amd/yc/acp6x-mach.c65
-rw-r--r--sound/soc/apple/Kconfig5
-rw-r--r--sound/soc/apple/mca.c23
-rw-r--r--sound/soc/atmel/Kconfig11
-rw-r--r--sound/soc/atmel/mchp-spdifrx.c6
-rw-r--r--sound/soc/au1x/Kconfig4
-rw-r--r--sound/soc/bcm/Kconfig4
-rw-r--r--sound/soc/cirrus/Kconfig3
-rw-r--r--sound/soc/codecs/Kconfig57
-rw-r--r--sound/soc/codecs/Makefile14
-rw-r--r--sound/soc/codecs/ac97.c10
-rw-r--r--sound/soc/codecs/adau7118.c6
-rw-r--r--sound/soc/codecs/ak4458.c10
-rw-r--r--sound/soc/codecs/ak5386.c28
-rw-r--r--sound/soc/codecs/arizona-jack.c2
-rw-r--r--sound/soc/codecs/aw88166.c7
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.c7
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h21
-rw-r--r--sound/soc/codecs/aw88399.c447
-rw-r--r--sound/soc/codecs/aw88399.h34
-rw-r--r--sound/soc/codecs/cros_ec_codec.c30
-rw-r--r--sound/soc/codecs/cs-amp-lib-test.c70
-rw-r--r--sound/soc/codecs/cs-amp-lib.c3
-rw-r--r--sound/soc/codecs/cs35l36.c2
-rw-r--r--sound/soc/codecs/cs35l41.c2
-rw-r--r--sound/soc/codecs/cs35l45.c1
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c23
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c110
-rw-r--r--sound/soc/codecs/cs35l56-shared.c259
-rw-r--r--sound/soc/codecs/cs35l56-spi.c3
-rw-r--r--sound/soc/codecs/cs35l56.c121
-rw-r--r--sound/soc/codecs/cs35l56.h4
-rw-r--r--sound/soc/codecs/cs42l42.c1
-rw-r--r--sound/soc/codecs/cs42l43-jack.c5
-rw-r--r--sound/soc/codecs/cs42l43.c2
-rw-r--r--sound/soc/codecs/cs42l52.c112
-rw-r--r--sound/soc/codecs/cs42l56.c90
-rw-r--r--sound/soc/codecs/cs42l73.c70
-rw-r--r--sound/soc/codecs/cs48l32-tables.c540
-rw-r--r--sound/soc/codecs/cs48l32.c4076
-rw-r--r--sound/soc/codecs/cs48l32.h403
-rw-r--r--sound/soc/codecs/da7218.c2
-rw-r--r--sound/soc/codecs/da7219.c68
-rw-r--r--sound/soc/codecs/es8326.c3
-rw-r--r--sound/soc/codecs/es8375.c794
-rw-r--r--sound/soc/codecs/es8375.h123
-rw-r--r--sound/soc/codecs/es8389.c962
-rw-r--r--sound/soc/codecs/es8389.h140
-rw-r--r--sound/soc/codecs/hda.c24
-rw-r--r--sound/soc/codecs/hdac_hda.c29
-rw-r--r--sound/soc/codecs/hdac_hdmi.c328
-rw-r--r--sound/soc/codecs/hdac_hdmi.h10
-rw-r--r--sound/soc/codecs/hdmi-codec.c7
-rw-r--r--sound/soc/codecs/idt821034.c17
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c1
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c1
-rw-r--r--sound/soc/codecs/lpass-va-macro.c1
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c1
-rw-r--r--sound/soc/codecs/max98363.c3
-rw-r--r--sound/soc/codecs/max98373-i2c.c2
-rw-r--r--sound/soc/codecs/max98373-sdw.c3
-rw-r--r--sound/soc/codecs/max98388.c2
-rw-r--r--sound/soc/codecs/max98390.c2
-rw-r--r--sound/soc/codecs/max98396.c4
-rw-r--r--sound/soc/codecs/max98504.c2
-rw-r--r--sound/soc/codecs/max98520.c2
-rw-r--r--sound/soc/codecs/max98927.c2
-rw-r--r--sound/soc/codecs/pcm6240.c3
-rw-r--r--sound/soc/codecs/peb2466.c15
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c1
-rw-r--r--sound/soc/codecs/rt1308-sdw.c1
-rw-r--r--sound/soc/codecs/rt1316-sdw.c1
-rw-r--r--sound/soc/codecs/rt1318-sdw.c1
-rw-r--r--sound/soc/codecs/rt1320-sdw.c18
-rw-r--r--sound/soc/codecs/rt5640.c43
-rw-r--r--sound/soc/codecs/rt5640.h6
-rw-r--r--sound/soc/codecs/rt5645.c4
-rw-r--r--sound/soc/codecs/rt5660.c7
-rw-r--r--sound/soc/codecs/rt5665.c96
-rw-r--r--sound/soc/codecs/rt5665.h3
-rw-r--r--sound/soc/codecs/rt5668.c43
-rw-r--r--sound/soc/codecs/rt5668.h3
-rw-r--r--sound/soc/codecs/rt5677-spi.c4
-rw-r--r--sound/soc/codecs/rt5677.c7
-rw-r--r--sound/soc/codecs/rt5682-sdw.c1
-rw-r--r--sound/soc/codecs/rt5682.c26
-rw-r--r--sound/soc/codecs/rt5682s.c26
-rw-r--r--sound/soc/codecs/rt700.c4
-rw-r--r--sound/soc/codecs/rt711-sdca.c2
-rw-r--r--sound/soc/codecs/rt711.c2
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c9
-rw-r--r--sound/soc/codecs/rt712-sdca.c10
-rw-r--r--sound/soc/codecs/rt715-sdca.c1
-rw-r--r--sound/soc/codecs/rt715.c3
-rw-r--r--sound/soc/codecs/rt721-sdca.c25
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c22
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.h1
-rw-r--r--sound/soc/codecs/rt722-sdca.c324
-rw-r--r--sound/soc/codecs/rt722-sdca.h6
-rw-r--r--sound/soc/codecs/rt9123.c500
-rw-r--r--sound/soc/codecs/rt9123p.c171
-rw-r--r--sound/soc/codecs/rtq9124.c543
-rw-r--r--sound/soc/codecs/sma1307.c1
-rw-r--r--sound/soc/codecs/sta32x.c6
-rw-r--r--sound/soc/codecs/tas2552.c1
-rw-r--r--sound/soc/codecs/tas2764-quirks.h180
-rw-r--r--sound/soc/codecs/tas2764.c139
-rw-r--r--sound/soc/codecs/tas2764.h3
-rw-r--r--sound/soc/codecs/tas2770.c151
-rw-r--r--sound/soc/codecs/tas2770.h6
-rw-r--r--sound/soc/codecs/tas2781-comlib-i2c.c369
-rw-r--r--sound/soc/codecs/tas2781-comlib.c397
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c25
-rw-r--r--sound/soc/codecs/tas2781-i2c.c155
-rw-r--r--sound/soc/codecs/tas571x.c119
-rw-r--r--sound/soc/codecs/tas571x.h34
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c8
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c53
-rw-r--r--sound/soc/codecs/tlv320aic3x.c6
-rw-r--r--sound/soc/codecs/tpa6130a2.c54
-rw-r--r--sound/soc/codecs/twl4030.c79
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c4
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.h2
-rw-r--r--sound/soc/codecs/wcd9335.c45
-rw-r--r--sound/soc/codecs/wcd934x.c4
-rw-r--r--sound/soc/codecs/wcd937x.c45
-rw-r--r--sound/soc/codecs/wcd937x.h1
-rw-r--r--sound/soc/codecs/wcd938x-sdw.c2
-rw-r--r--sound/soc/codecs/wcd938x.c131
-rw-r--r--sound/soc/codecs/wcd939x.c77
-rw-r--r--sound/soc/codecs/wm5100.c10
-rw-r--r--sound/soc/codecs/wm8524.c55
-rw-r--r--sound/soc/codecs/wm8903.c12
-rw-r--r--sound/soc/codecs/wm8962.c11
-rw-r--r--sound/soc/codecs/wm8996.c10
-rw-r--r--sound/soc/codecs/wm_adsp.c55
-rw-r--r--sound/soc/codecs/wm_adsp.h2
-rw-r--r--sound/soc/codecs/wsa881x.c5
-rw-r--r--sound/soc/codecs/wsa883x.c5
-rw-r--r--sound/soc/codecs/wsa884x.c5
-rw-r--r--sound/soc/codecs/zl38060.c12
-rw-r--r--sound/soc/dwc/Kconfig3
-rw-r--r--sound/soc/fsl/Kconfig2
-rw-r--r--sound/soc/fsl/fsl-asoc-card.c13
-rw-r--r--sound/soc/fsl/fsl_asrc.c5
-rw-r--r--sound/soc/fsl/fsl_mqs.c11
-rw-r--r--sound/soc/fsl/fsl_rpmsg.c28
-rw-r--r--sound/soc/fsl/fsl_rpmsg.h2
-rw-r--r--sound/soc/fsl/fsl_sai.c108
-rw-r--r--sound/soc/fsl/fsl_sai.h6
-rw-r--r--sound/soc/fsl/fsl_xcvr.c2
-rw-r--r--sound/soc/fsl/imx-card.c20
-rw-r--r--sound/soc/fsl/imx-pcm-rpmsg.c23
-rw-r--r--sound/soc/generic/Kconfig4
-rw-r--r--sound/soc/generic/simple-card-utils.c23
-rw-r--r--sound/soc/generic/test-component.c11
-rw-r--r--sound/soc/google/Kconfig3
-rw-r--r--sound/soc/hisilicon/Kconfig4
-rw-r--r--sound/soc/img/Kconfig14
-rw-r--r--sound/soc/intel/Kconfig4
-rw-r--r--sound/soc/intel/atom/sst/sst.h6
-rw-r--r--sound/soc/intel/atom/sst/sst_drv_interface.c24
-rw-r--r--sound/soc/intel/atom/sst/sst_pci.c59
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c34
-rw-r--r--sound/soc/intel/avs/Makefile6
-rw-r--r--sound/soc/intel/avs/avs.h13
-rw-r--r--sound/soc/intel/avs/board_selection.c204
-rw-r--r--sound/soc/intel/avs/boards/Kconfig20
-rw-r--r--sound/soc/intel/avs/boards/Makefile2
-rw-r--r--sound/soc/intel/avs/boards/da7219.c11
-rw-r--r--sound/soc/intel/avs/boards/dmic.c12
-rw-r--r--sound/soc/intel/avs/boards/es8336.c11
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c25
-rw-r--r--sound/soc/intel/avs/boards/i2s_test.c15
-rw-r--r--sound/soc/intel/avs/boards/max98357a.c11
-rw-r--r--sound/soc/intel/avs/boards/max98373.c11
-rw-r--r--sound/soc/intel/avs/boards/max98927.c11
-rw-r--r--sound/soc/intel/avs/boards/nau8825.c11
-rw-r--r--sound/soc/intel/avs/boards/pcm3168a.c16
-rw-r--r--sound/soc/intel/avs/boards/probe.c5
-rw-r--r--sound/soc/intel/avs/boards/rt274.c11
-rw-r--r--sound/soc/intel/avs/boards/rt286.c11
-rw-r--r--sound/soc/intel/avs/boards/rt298.c11
-rw-r--r--sound/soc/intel/avs/boards/rt5514.c11
-rw-r--r--sound/soc/intel/avs/boards/rt5640.c270
-rw-r--r--sound/soc/intel/avs/boards/rt5663.c11
-rw-r--r--sound/soc/intel/avs/boards/rt5682.c11
-rw-r--r--sound/soc/intel/avs/boards/ssm4567.c11
-rw-r--r--sound/soc/intel/avs/core.c89
-rw-r--r--sound/soc/intel/avs/debugfs.c14
-rw-r--r--sound/soc/intel/avs/dsp.c2
-rw-r--r--sound/soc/intel/avs/ipc.c5
-rw-r--r--sound/soc/intel/avs/lnl.c27
-rw-r--r--sound/soc/intel/avs/loader.c12
-rw-r--r--sound/soc/intel/avs/messages.h34
-rw-r--r--sound/soc/intel/avs/mtl.c200
-rw-r--r--sound/soc/intel/avs/path.c61
-rw-r--r--sound/soc/intel/avs/pcm.c174
-rw-r--r--sound/soc/intel/avs/probes.c2
-rw-r--r--sound/soc/intel/avs/ptl.c98
-rw-r--r--sound/soc/intel/avs/registers.h40
-rw-r--r--sound/soc/intel/avs/topology.c4
-rw-r--r--sound/soc/intel/avs/topology.h2
-rw-r--r--sound/soc/intel/avs/utils.h16
-rw-r--r--sound/soc/intel/boards/Kconfig11
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5672.c3
-rw-r--r--sound/soc/intel/boards/skl_hda_dsp_generic.c13
-rw-r--r--sound/soc/intel/boards/sof_es8336.c10
-rw-r--r--sound/soc/intel/boards/sof_rt5682.c7
-rw-r--r--sound/soc/intel/boards/sof_sdw.c65
-rw-r--r--sound/soc/intel/catpt/pcm.c6
-rw-r--r--sound/soc/intel/catpt/sysfs.c1
-rw-r--r--sound/soc/intel/common/Makefile2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c44
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c49
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c49
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c175
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.c136
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.h15
-rw-r--r--sound/soc/jz4740/Kconfig4
-rw-r--r--sound/soc/kirkwood/Kconfig3
-rw-r--r--sound/soc/loongson/Kconfig12
-rw-r--r--sound/soc/loongson/Makefile2
-rw-r--r--sound/soc/loongson/loongson1_ac97.c398
-rw-r--r--sound/soc/loongson/loongson_i2s.c1
-rw-r--r--sound/soc/loongson/loongson_i2s_pci.c13
-rw-r--r--sound/soc/mediatek/Kconfig4
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c4
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h1
-rw-r--r--sound/soc/mediatek/common/mtk-soundcard-driver.c4
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c66
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c627
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c7
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-mt6359.c10
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-pcm.c7
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-mt6359.c45
-rw-r--r--sound/soc/mediatek/mt8365/mt8365-dai-i2s.c3
-rw-r--r--sound/soc/meson/Kconfig2
-rw-r--r--sound/soc/meson/axg-card.c3
-rw-r--r--sound/soc/meson/meson-card-utils.c18
-rw-r--r--sound/soc/mxs/Kconfig2
-rw-r--r--sound/soc/pxa/Kconfig4
-rw-r--r--sound/soc/qcom/Kconfig19
-rw-r--r--sound/soc/qcom/Makefile2
-rw-r--r--sound/soc/qcom/lpass-platform.c27
-rw-r--r--sound/soc/qcom/qdsp6/Makefile1
-rw-r--r--sound/soc/qcom/qdsp6/q6afe-dai.c60
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.c192
-rw-r--r--sound/soc/qcom/qdsp6/q6afe.h36
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c2
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c8
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c23
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h1
-rw-r--r--sound/soc/qcom/qdsp6/q6routing.c9
-rw-r--r--sound/soc/qcom/qdsp6/q6usb.c421
-rw-r--r--sound/soc/qcom/sc8280xp.c3
-rw-r--r--sound/soc/qcom/sdm845.c4
-rw-r--r--sound/soc/qcom/sm8250.c38
-rw-r--r--sound/soc/qcom/usb_offload_utils.c56
-rw-r--r--sound/soc/qcom/usb_offload_utils.h30
-rw-r--r--sound/soc/renesas/Kconfig9
-rw-r--r--sound/soc/renesas/rcar/Makefile3
-rw-r--r--sound/soc/renesas/rcar/adg.c32
-rw-r--r--sound/soc/renesas/rcar/core.c44
-rw-r--r--sound/soc/renesas/rcar/ctu.c8
-rw-r--r--sound/soc/renesas/rcar/dma.c4
-rw-r--r--sound/soc/renesas/rcar/dvc.c8
-rw-r--r--sound/soc/renesas/rcar/mix.c8
-rw-r--r--sound/soc/renesas/rcar/msiof.c528
-rw-r--r--sound/soc/renesas/rcar/src.c10
-rw-r--r--sound/soc/renesas/rcar/ssi.c18
-rw-r--r--sound/soc/renesas/rcar/ssiu.c7
-rw-r--r--sound/soc/rockchip/Kconfig31
-rw-r--r--sound/soc/rockchip/Makefile2
-rw-r--r--sound/soc/rockchip/rockchip_sai.c1529
-rw-r--r--sound/soc/rockchip/rockchip_sai.h251
-rw-r--r--sound/soc/samsung/Kconfig2
-rw-r--r--sound/soc/samsung/bells.c2
-rw-r--r--sound/soc/samsung/littlemill.c2
-rw-r--r--sound/soc/samsung/speyside.c2
-rw-r--r--sound/soc/samsung/tobermory.c2
-rw-r--r--sound/soc/sdca/Kconfig20
-rw-r--r--sound/soc/sdca/Makefile6
-rw-r--r--sound/soc/sdca/sdca_asoc.c1616
-rw-r--r--sound/soc/sdca/sdca_functions.c177
-rw-r--r--sound/soc/sdca/sdca_hid.c127
-rw-r--r--sound/soc/sdca/sdca_interrupts.c444
-rw-r--r--sound/soc/sdca/sdca_regmap.c3
-rw-r--r--sound/soc/sdw_utils/soc_sdw_cs_amp.c10
-rw-r--r--sound/soc/sdw_utils/soc_sdw_maxim.c9
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_amp.c8
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c7
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c242
-rw-r--r--sound/soc/soc-ac97.c15
-rw-r--r--sound/soc/soc-component.c1
-rw-r--r--sound/soc/soc-core.c155
-rw-r--r--sound/soc/soc-dai.c16
-rw-r--r--sound/soc/soc-dapm.c966
-rw-r--r--sound/soc/soc-devres.c7
-rw-r--r--sound/soc/soc-ops-test.c29
-rw-r--r--sound/soc/soc-ops.c26
-rw-r--r--sound/soc/soc-pcm.c23
-rw-r--r--sound/soc/soc-topology.c7
-rw-r--r--sound/soc/soc-usb.c322
-rw-r--r--sound/soc/soc-utils.c13
-rw-r--r--sound/soc/sof/Kconfig2
-rw-r--r--sound/soc/sof/amd/Kconfig7
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h10
-rw-r--r--sound/soc/sof/amd/acp.c133
-rw-r--r--sound/soc/sof/amd/acp.h7
-rw-r--r--sound/soc/sof/amd/pci-acp70.c11
-rw-r--r--sound/soc/sof/control.c1
-rw-r--r--sound/soc/sof/core.c10
-rw-r--r--sound/soc/sof/debug.c1
-rw-r--r--sound/soc/sof/imx/imx-common.c36
-rw-r--r--sound/soc/sof/imx/imx8.c39
-rw-r--r--sound/soc/sof/intel/hda-loader.c7
-rw-r--r--sound/soc/sof/intel/hda.c14
-rw-r--r--sound/soc/sof/intel/hda.h1
-rw-r--r--sound/soc/sof/intel/pci-ptl.c30
-rw-r--r--sound/soc/sof/intel/ptl.c24
-rw-r--r--sound/soc/sof/ipc3-dtrace.c1
-rw-r--r--sound/soc/sof/ipc4-loader.c7
-rw-r--r--sound/soc/sof/ipc4-pcm.c140
-rw-r--r--sound/soc/sof/ipc4-priv.h6
-rw-r--r--sound/soc/sof/ipc4-topology.c94
-rw-r--r--sound/soc/sof/ipc4-topology.h25
-rw-r--r--sound/soc/sof/ipc4.c39
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c11
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c11
-rw-r--r--sound/soc/sof/pcm.c27
-rw-r--r--sound/soc/sof/sof-client-ipc-flood-test.c1
-rw-r--r--sound/soc/sof/sof-client-ipc-kernel-injector.c1
-rw-r--r--sound/soc/sof/sof-client-ipc-msg-injector.c1
-rw-r--r--sound/soc/sof/sof-client-probes.c3
-rw-r--r--sound/soc/sof/sof-pci-dev.c16
-rw-r--r--sound/soc/sof/topology.c112
-rw-r--r--sound/soc/spear/Kconfig4
-rw-r--r--sound/soc/sprd/Kconfig4
-rw-r--r--sound/soc/starfive/Kconfig12
-rw-r--r--sound/soc/starfive/jh7110_tdm.c13
-rw-r--r--sound/soc/sti/Kconfig2
-rw-r--r--sound/soc/stm/Kconfig2
-rw-r--r--sound/soc/stm/stm32_i2s.c21
-rw-r--r--sound/soc/stm/stm32_sai.c18
-rw-r--r--sound/soc/stm/stm32_sai_sub.c14
-rw-r--r--sound/soc/sunxi/Kconfig2
-rw-r--r--sound/soc/sunxi/sun8i-codec.c13
-rw-r--r--sound/soc/tegra/Kconfig4
-rw-r--r--sound/soc/tegra/tegra186_asrc.c18
-rw-r--r--sound/soc/tegra/tegra186_asrc.h12
-rw-r--r--sound/soc/tegra/tegra210_admaif.c223
-rw-r--r--sound/soc/tegra/tegra210_admaif.h78
-rw-r--r--sound/soc/tegra/tegra210_adx.c229
-rw-r--r--sound/soc/tegra/tegra210_adx.h36
-rw-r--r--sound/soc/tegra/tegra210_ahub.c850
-rw-r--r--sound/soc/tegra/tegra210_ahub.h52
-rw-r--r--sound/soc/tegra/tegra210_amx.c229
-rw-r--r--sound/soc/tegra/tegra210_amx.h34
-rw-r--r--sound/soc/tegra/tegra210_i2s.c231
-rw-r--r--sound/soc/tegra/tegra210_i2s.h51
-rw-r--r--sound/soc/tegra/tegra_audio_graph_card.c14
-rw-r--r--sound/soc/tegra/tegra_cif.h30
-rw-r--r--sound/soc/tegra/tegra_isomgr_bw.c7
-rw-r--r--sound/soc/ti/Kconfig2
-rw-r--r--sound/soc/ti/davinci-mcasp.c8
-rw-r--r--sound/soc/ti/omap-hdmi.c7
-rw-r--r--sound/soc/uniphier/Kconfig14
-rw-r--r--sound/soc/ux500/Kconfig2
-rw-r--r--sound/soc/xilinx/Kconfig4
-rw-r--r--sound/soc/xtensa/Kconfig4
395 files changed, 23915 insertions, 4154 deletions
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 8b7d51266f81..bf362bfca456 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -91,6 +91,16 @@ config SND_SOC_OPS_KUNIT_TEST
config SND_SOC_ACPI
tristate
+config SND_SOC_USB
+ tristate "SoC based USB audio offloading"
+ depends on SND_USB_AUDIO
+ help
+ Enable this option if an ASoC platform card has support to handle
+ USB audio offloading. This enables the SoC USB layer, which will
+ notify the ASoC USB DPCM backend DAI link about available USB audio
+ devices. Based on the notifications, sequences to enable the audio
+ stream can be taken based on the design.
+
# All the supported SoCs
source "sound/soc/adi/Kconfig"
source "sound/soc/amd/Kconfig"
@@ -101,6 +111,7 @@ source "sound/soc/bcm/Kconfig"
source "sound/soc/cirrus/Kconfig"
source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig"
+source "sound/soc/generic/Kconfig"
source "sound/soc/google/Kconfig"
source "sound/soc/hisilicon/Kconfig"
source "sound/soc/jz4740/Kconfig"
@@ -117,7 +128,6 @@ source "sound/soc/renesas/Kconfig"
source "sound/soc/rockchip/Kconfig"
source "sound/soc/samsung/Kconfig"
source "sound/soc/sdca/Kconfig"
-source "sound/soc/sof/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sprd/Kconfig"
source "sound/soc/starfive/Kconfig"
@@ -131,13 +141,13 @@ source "sound/soc/ux500/Kconfig"
source "sound/soc/xilinx/Kconfig"
source "sound/soc/xtensa/Kconfig"
+# SOF
+source "sound/soc/sof/Kconfig"
+
# Supported codecs
source "sound/soc/codecs/Kconfig"
source "sound/soc/sdw_utils/Kconfig"
-# generic frame-work
-source "sound/soc/generic/Kconfig"
-
endif # SND_SOC
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 358e227c5ab6..462322c38aa4 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -39,6 +39,8 @@ endif
obj-$(CONFIG_SND_SOC_ACPI) += snd-soc-acpi.o
+obj-$(CONFIG_SND_SOC_USB) += soc-usb.o
+
obj-$(CONFIG_SND_SOC) += snd-soc-core.o
obj-$(CONFIG_SND_SOC) += codecs/
obj-$(CONFIG_SND_SOC) += generic/
diff --git a/sound/soc/adi/Kconfig b/sound/soc/adi/Kconfig
index 0236dc5b4e9f..d47dffbf40d0 100644
--- a/sound/soc/adi/Kconfig
+++ b/sound/soc/adi/Kconfig
@@ -1,12 +1,8 @@
# SPDX-License-Identifier: GPL-2.0-only
-config SND_SOC_ADI
- tristate "Audio support for Analog Devices reference designs"
- help
- Audio support for various reference designs by Analog Devices.
+menu "Analog Devices"
config SND_SOC_ADI_AXI_I2S
tristate "AXI-I2S support"
- depends on SND_SOC_ADI
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
@@ -14,8 +10,9 @@ config SND_SOC_ADI_AXI_I2S
config SND_SOC_ADI_AXI_SPDIF
tristate "AXI-SPDIF support"
- depends on SND_SOC_ADI
select SND_SOC_GENERIC_DMAENGINE_PCM
select REGMAP_MMIO
help
ASoC driver for the Analog Devices AXI-SPDIF softcore peripheral.
+
+endmenu
diff --git a/sound/soc/amd/Kconfig b/sound/soc/amd/Kconfig
index c7daae392d74..fd35a03aadcb 100644
--- a/sound/soc/amd/Kconfig
+++ b/sound/soc/amd/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "AMD"
+
config SND_SOC_AMD_ACP
tristate "AMD Audio Coprocessor support"
help
@@ -185,3 +187,4 @@ config SND_SOC_AMD_PS_MACH
If unsure select "N".
endif
+endmenu
diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c
index 02b04f355ca6..42aa009c4e13 100644
--- a/sound/soc/amd/acp-da7219-max98357a.c
+++ b/sound/soc/amd/acp-da7219-max98357a.c
@@ -517,11 +517,11 @@ static const struct snd_soc_ops cz_rt5682_dmic1_cap_ops = {
};
SND_SOC_DAILINK_DEF(designware1,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
SND_SOC_DAILINK_DEF(designware2,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
SND_SOC_DAILINK_DEF(designware3,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.3")));
SND_SOC_DAILINK_DEF(dlgs,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-DLGS7219:00", "da7219-hifi")));
@@ -533,7 +533,7 @@ SND_SOC_DAILINK_DEF(adau,
DAILINK_COMP_ARRAY(COMP_CODEC("ADAU7002:00", "adau7002-hifi")));
SND_SOC_DAILINK_DEF(platform,
- DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0.auto")));
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
static struct snd_soc_dai_link cz_dai_7219_98357[] = {
{
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
index 0193b3eae7a6..b16dde0e2987 100644
--- a/sound/soc/amd/acp-es8336.c
+++ b/sound/soc/amd/acp-es8336.c
@@ -137,11 +137,11 @@ static const struct snd_soc_ops st_es8336_ops = {
};
SND_SOC_DAILINK_DEF(designware1,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
SND_SOC_DAILINK_DEF(codec,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-ESSX8336:00", "ES8316 HiFi")));
SND_SOC_DAILINK_DEF(platform,
- DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.1.auto")));
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
static struct snd_soc_dai_link st_dai_es8336[] = {
{
diff --git a/sound/soc/amd/acp-rt5645.c b/sound/soc/amd/acp-rt5645.c
index 72ddad24dbda..11d373169380 100644
--- a/sound/soc/amd/acp-rt5645.c
+++ b/sound/soc/amd/acp-rt5645.c
@@ -108,15 +108,15 @@ static const struct snd_soc_ops cz_aif1_ops = {
};
SND_SOC_DAILINK_DEF(designware1,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.1")));
SND_SOC_DAILINK_DEF(designware2,
- DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2.auto")));
+ DAILINK_COMP_ARRAY(COMP_CPU("designware-i2s.2")));
SND_SOC_DAILINK_DEF(codec,
DAILINK_COMP_ARRAY(COMP_CODEC("i2c-10EC5650:00", "rt5645-aif1")));
SND_SOC_DAILINK_DEF(platform,
- DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0.auto")));
+ DAILINK_COMP_ARRAY(COMP_PLATFORM("acp_audio_dma.0")));
static struct snd_soc_dai_link cz_dai_rt5650[] = {
{
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index b9432052c638..c2a60bc80ee6 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -148,6 +148,7 @@ config SND_SOC_AMD_SOF_SDW_MACH
select SND_SOC_RT1316_SDW
select SND_SOC_RT715_SDW
select SND_SOC_RT715_SDCA_SDW
+ select SND_SOC_RT722_SDCA_SDW
help
This option enables SOF sound card support for SoundWire enabled
AMD platforms along with ACP PDM controller.
diff --git a/sound/soc/amd/acp/acp-i2s.c b/sound/soc/amd/acp/acp-i2s.c
index 70fa54d568ef..617690362ad7 100644
--- a/sound/soc/amd/acp/acp-i2s.c
+++ b/sound/soc/amd/acp/acp-i2s.c
@@ -58,6 +58,7 @@ static inline void acp_set_i2s_clk(struct acp_chip_info *chip, int dai_id)
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
val |= FIELD_PREP(ACP63_LRCLK_DIV_FIELD, chip->lrclk_div);
val |= FIELD_PREP(ACP63_BCLK_DIV_FIELD, chip->bclk_div);
break;
@@ -134,6 +135,7 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
switch (slots) {
case 1 ... 31:
no_of_slots = slots;
@@ -168,6 +170,7 @@ static int acp_i2s_set_tdm_slot(struct snd_soc_dai *dai, u32 tx_mask, u32 rx_mas
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
if (tx_mask && stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
chip->tdm_tx_fmt[stream->dai_id - 1] =
FRM_LEN | (slots << 13) | (slot_len << 18);
diff --git a/sound/soc/amd/acp/acp-legacy-common.c b/sound/soc/amd/acp/acp-legacy-common.c
index ba8db0851daa..3078f459e005 100644
--- a/sound/soc/amd/acp/acp-legacy-common.c
+++ b/sound/soc/amd/acp/acp-legacy-common.c
@@ -372,6 +372,7 @@ static int acp_power_on(struct acp_chip_info *chip)
break;
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
acp_pgfsm_stat_reg = ACP70_PGFSM_STATUS;
acp_pgfsm_ctrl_reg = ACP70_PGFSM_CONTROL;
break;
@@ -573,6 +574,7 @@ void check_acp_config(struct pci_dev *pci, struct acp_chip_info *chip)
break;
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
pdm_addr = ACP70_PDM_ADDR;
check_acp70_config(chip);
break;
diff --git a/sound/soc/amd/acp/acp-mach-common.c b/sound/soc/amd/acp/acp-mach-common.c
index a0dab85088ec..c4bc8e849284 100644
--- a/sound/soc/amd/acp/acp-mach-common.c
+++ b/sound/soc/amd/acp/acp-mach-common.c
@@ -1772,6 +1772,7 @@ int acp_legacy_dai_links_create(struct snd_soc_card *card)
break;
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
links[i].platforms = platform_acp70_component;
links[i].num_platforms = ARRAY_SIZE(platform_acp70_component);
break;
diff --git a/sound/soc/amd/acp/acp-pci.c b/sound/soc/amd/acp/acp-pci.c
index 0b2aa33cc426..f83708755ed1 100644
--- a/sound/soc/amd/acp/acp-pci.c
+++ b/sound/soc/amd/acp/acp-pci.c
@@ -137,26 +137,27 @@ static int acp_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id
chip->name = "acp_asoc_renoir";
chip->rsrc = &rn_rsrc;
chip->acp_hw_ops_init = acp31_hw_ops_init;
- chip->machines = &snd_soc_acpi_amd_acp_machines;
+ chip->machines = snd_soc_acpi_amd_acp_machines;
break;
case 0x6f:
chip->name = "acp_asoc_rembrandt";
chip->rsrc = &rmb_rsrc;
chip->acp_hw_ops_init = acp6x_hw_ops_init;
- chip->machines = &snd_soc_acpi_amd_rmb_acp_machines;
+ chip->machines = snd_soc_acpi_amd_rmb_acp_machines;
break;
case 0x63:
chip->name = "acp_asoc_acp63";
chip->rsrc = &acp63_rsrc;
chip->acp_hw_ops_init = acp63_hw_ops_init;
- chip->machines = &snd_soc_acpi_amd_acp63_acp_machines;
+ chip->machines = snd_soc_acpi_amd_acp63_acp_machines;
break;
case 0x70:
case 0x71:
+ case 0x72:
chip->name = "acp_asoc_acp70";
chip->rsrc = &acp70_rsrc;
chip->acp_hw_ops_init = acp70_hw_ops_init;
- chip->machines = &snd_soc_acpi_amd_acp70_acp_machines;
+ chip->machines = snd_soc_acpi_amd_acp70_acp_machines;
break;
default:
dev_err(dev, "Unsupported device revision:0x%x\n", pci->revision);
diff --git a/sound/soc/amd/acp/acp-platform.c b/sound/soc/amd/acp/acp-platform.c
index b3eddf76aaa4..b25ac5612808 100644
--- a/sound/soc/amd/acp/acp-platform.c
+++ b/sound/soc/amd/acp/acp-platform.c
@@ -140,6 +140,7 @@ void config_acp_dma(struct acp_chip_info *chip, struct acp_stream *stream, int s
switch (chip->acp_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
switch (stream->dai_id) {
case I2S_SP_INSTANCE:
if (stream->dir == SNDRV_PCM_STREAM_PLAYBACK)
@@ -205,6 +206,7 @@ static int acp_dma_open(struct snd_soc_component *component, struct snd_pcm_subs
case ACP63_PCI_ID:
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
runtime->hw = acp6x_pcm_hardware_playback;
else
diff --git a/sound/soc/amd/acp/acp-sdw-legacy-mach.c b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
index 2020c5cfb3d5..6c24f9d8694e 100644
--- a/sound/soc/amd/acp/acp-sdw-legacy-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-legacy-mach.c
@@ -272,7 +272,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card,
/* generate DAI links by each sdw link */
while (soc_dais->initialised) {
- int current_be_id;
+ int current_be_id = 0;
ret = create_sdw_dailink(card, soc_dais, dai_links,
&current_be_id, codec_conf, sdw_platform_component);
@@ -321,7 +321,7 @@ static int create_dmic_dailinks(struct snd_soc_card *card,
*be_id = ACP_DMIC_BE_ID;
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
0, 1, // DMIC only supports capture
- pdm_cpu->name, pdm_platform->name, 1,
+ pdm_cpu->name, pdm_platform->name,
"dmic-codec.0", "dmic-hifi", no_pcm,
asoc_sdw_dmic_init, NULL);
if (ret)
diff --git a/sound/soc/amd/acp/acp-sdw-sof-mach.c b/sound/soc/amd/acp/acp-sdw-sof-mach.c
index c09b1f118a6c..654fe78b2e2e 100644
--- a/sound/soc/amd/acp/acp-sdw-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sdw-sof-mach.c
@@ -128,6 +128,13 @@ static int create_sdw_dailink(struct snd_soc_card *card,
if (ret)
return ret;
break;
+ case ACP70_PCI_REV:
+ case ACP71_PCI_REV:
+ ret = get_acp70_cpu_pin_id(ffs(sof_end->link_mask - 1),
+ *be_id, &cpu_pin_id, dev);
+ if (ret)
+ return ret;
+ break;
default:
return -EINVAL;
}
@@ -219,7 +226,7 @@ static int create_sdw_dailinks(struct snd_soc_card *card,
/* generate DAI links by each sdw link */
while (sof_dais->initialised) {
- int current_be_id;
+ int current_be_id = 0;
ret = create_sdw_dailink(card, sof_dais, dai_links,
&current_be_id, codec_conf);
@@ -245,7 +252,6 @@ static int create_dmic_dailinks(struct snd_soc_card *card,
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "acp-dmic-codec",
0, 1, // DMIC only supports capture
"acp-sof-dmic", platform_component->name,
- ARRAY_SIZE(platform_component),
"dmic-codec", "dmic-hifi", no_pcm,
asoc_sdw_dmic_init, NULL);
if (ret)
diff --git a/sound/soc/amd/acp/acp-sof-mach.c b/sound/soc/amd/acp/acp-sof-mach.c
index d7b54f12f406..6215e31ecedd 100644
--- a/sound/soc/amd/acp/acp-sof-mach.c
+++ b/sound/soc/amd/acp/acp-sof-mach.c
@@ -88,7 +88,7 @@ static struct acp_card_drvdata sof_nau8821_max98388_data = {
static int acp_sof_probe(struct platform_device *pdev)
{
- struct snd_soc_card *card = NULL;
+ struct snd_soc_card *card;
struct device *dev = &pdev->dev;
struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
const struct dmi_system_id *dmi_id;
diff --git a/sound/soc/amd/acp/acp70.c b/sound/soc/amd/acp/acp70.c
index b95e3949e70b..bca311c88139 100644
--- a/sound/soc/amd/acp/acp70.c
+++ b/sound/soc/amd/acp/acp70.c
@@ -136,6 +136,7 @@ static int acp_acp70_audio_probe(struct platform_device *pdev)
switch (chip->acp_rev) {
case ACP70_PCI_ID:
case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
break;
default:
dev_err(&pdev->dev, "Un-supported ACP Revision %d\n", chip->acp_rev);
diff --git a/sound/soc/amd/acp/acp_common.h b/sound/soc/amd/acp/acp_common.h
index f1ae88013f62..984685602e3d 100644
--- a/sound/soc/amd/acp/acp_common.h
+++ b/sound/soc/amd/acp/acp_common.h
@@ -15,5 +15,6 @@
#define ACP63_PCI_ID 0x63
#define ACP70_PCI_ID 0x70
#define ACP71_PCI_ID 0x71
+#define ACP72_PCI_ID 0x72
#endif
diff --git a/sound/soc/amd/acp/amd-acp70-acpi-match.c b/sound/soc/amd/acp/amd-acp70-acpi-match.c
index e87ccfeee5bd..dcecac792e6d 100644
--- a/sound/soc/amd/acp/amd-acp70-acpi-match.c
+++ b/sound/soc/amd/acp/amd-acp70-acpi-match.c
@@ -155,6 +155,18 @@ struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[] = {
};
EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sdw_machines);
+struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[] = {
+ {
+ .link_mask = BIT(0),
+ .links = acp70_rt722_only,
+ .drv_name = "amd_sof_sdw",
+ .sof_tplg_filename = "sof-acp_7_0-rt722-l0.tplg",
+ .fw_filename = "sof-acp_7_0.ri",
+ },
+ {},
+};
+EXPORT_SYMBOL(snd_soc_acpi_amd_acp70_sof_sdw_machines);
+
MODULE_DESCRIPTION("AMD ACP7.0 & ACP7.1 tables and support for ACPI enumeration");
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vijendar.Mukunda@amd.com");
diff --git a/sound/soc/amd/acp/amd-acpi-mach.c b/sound/soc/amd/acp/amd-acpi-mach.c
index d95047d2ee94..27da2a862f1c 100644
--- a/sound/soc/amd/acp/amd-acpi-mach.c
+++ b/sound/soc/amd/acp/amd-acpi-mach.c
@@ -8,12 +8,12 @@
#include <sound/soc-acpi.h>
-struct snd_soc_acpi_codecs amp_rt1019 = {
+static struct snd_soc_acpi_codecs amp_rt1019 = {
.num_codecs = 1,
.codecs = {"10EC1019"}
};
-struct snd_soc_acpi_codecs amp_max = {
+static struct snd_soc_acpi_codecs amp_max = {
.num_codecs = 1,
.codecs = {"MX98360A"}
};
diff --git a/sound/soc/amd/acp/amd.h b/sound/soc/amd/acp/amd.h
index 863e74fcee43..cb8d97122f95 100644
--- a/sound/soc/amd/acp/amd.h
+++ b/sound/soc/amd/acp/amd.h
@@ -243,10 +243,10 @@ extern struct acp_resource rmb_rsrc;
extern struct acp_resource acp63_rsrc;
extern struct acp_resource acp70_rsrc;
-extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines;
-extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines;
-extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines;
-extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines;
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_rmb_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_acp_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_acp_machines[];
extern const struct snd_soc_dai_ops asoc_acp_cpu_dai_ops;
extern const struct snd_soc_dai_ops acp_dmic_dai_ops;
diff --git a/sound/soc/amd/mach-config.h b/sound/soc/amd/mach-config.h
index fdf016a64bbf..5b6362103ca0 100644
--- a/sound/soc/amd/mach-config.h
+++ b/sound/soc/amd/mach-config.h
@@ -27,6 +27,7 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp63_sof_sdw_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_machines[];
extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sdw_machines[];
+extern struct snd_soc_acpi_mach snd_soc_acpi_amd_acp70_sof_sdw_machines[];
struct config_entry {
u32 flags;
diff --git a/sound/soc/amd/ps/acp63.h b/sound/soc/amd/ps/acp63.h
index 85feae45c44c..d7c994e26e4d 100644
--- a/sound/soc/amd/ps/acp63.h
+++ b/sound/soc/amd/ps/acp63.h
@@ -334,6 +334,8 @@ struct acp_hw_ops {
* @addr: pci ioremap address
* @reg_range: ACP reigister range
* @acp_rev: ACP PCI revision id
+ * @acp_sw_pad_keeper_en: store acp SoundWire pad keeper enable register value
+ * @acp_pad_pulldown_ctrl: store acp pad pulldown control register value
* @acp63_sdw0-dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
* manager-SW0 instance
* @acp63_sdw_dma_intr_stat: DMA interrupt status array for ACP6.3 platform SoundWire
@@ -367,6 +369,8 @@ struct acp63_dev_data {
u32 addr;
u32 reg_range;
u32 acp_rev;
+ u32 acp_sw_pad_keeper_en;
+ u32 acp_pad_pulldown_ctrl;
u16 acp63_sdw0_dma_intr_stat[ACP63_SDW0_DMA_MAX_STREAMS];
u16 acp63_sdw1_dma_intr_stat[ACP63_SDW1_DMA_MAX_STREAMS];
u16 acp70_sdw0_dma_intr_stat[ACP70_SDW0_DMA_MAX_STREAMS];
diff --git a/sound/soc/amd/ps/ps-common.c b/sound/soc/amd/ps/ps-common.c
index 1c89fb5fe1da..7b4966b75dc6 100644
--- a/sound/soc/amd/ps/ps-common.c
+++ b/sound/soc/amd/ps/ps-common.c
@@ -160,6 +160,8 @@ static int __maybe_unused snd_acp63_suspend(struct device *dev)
adata = dev_get_drvdata(dev);
if (adata->is_sdw_dev) {
+ adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
if (adata->sdw_en_stat) {
writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
@@ -197,6 +199,7 @@ static int __maybe_unused snd_acp63_runtime_resume(struct device *dev)
static int __maybe_unused snd_acp63_resume(struct device *dev)
{
struct acp63_dev_data *adata;
+ u32 acp_sw_pad_keeper_en;
int ret;
adata = dev_get_drvdata(dev);
@@ -209,6 +212,12 @@ static int __maybe_unused snd_acp63_resume(struct device *dev)
if (ret)
dev_err(dev, "ACP init failed\n");
+ acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en);
+ if (!acp_sw_pad_keeper_en) {
+ writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ }
return ret;
}
@@ -408,6 +417,8 @@ static int __maybe_unused snd_acp70_suspend(struct device *dev)
adata = dev_get_drvdata(dev);
if (adata->is_sdw_dev) {
+ adata->acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ adata->acp_pad_pulldown_ctrl = readl(adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
adata->sdw_en_stat = check_acp_sdw_enable_status(adata);
if (adata->sdw_en_stat) {
writel(1, adata->acp63_base + ACP_ZSC_DSP_CTRL);
@@ -445,6 +456,7 @@ static int __maybe_unused snd_acp70_runtime_resume(struct device *dev)
static int __maybe_unused snd_acp70_resume(struct device *dev)
{
struct acp63_dev_data *adata;
+ u32 acp_sw_pad_keeper_en;
int ret;
adata = dev_get_drvdata(dev);
@@ -459,6 +471,12 @@ static int __maybe_unused snd_acp70_resume(struct device *dev)
if (ret)
dev_err(dev, "ACP init failed\n");
+ acp_sw_pad_keeper_en = readl(adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ dev_dbg(dev, "ACP_SW0_PAD_KEEPER_EN:0x%x\n", acp_sw_pad_keeper_en);
+ if (!acp_sw_pad_keeper_en) {
+ writel(adata->acp_sw_pad_keeper_en, adata->acp63_base + ACP_SW0_PAD_KEEPER_EN);
+ writel(adata->acp_pad_pulldown_ctrl, adata->acp63_base + ACP_PAD_PULLDOWN_CTRL);
+ }
return ret;
}
diff --git a/sound/soc/amd/yc/acp6x-mach.c b/sound/soc/amd/yc/acp6x-mach.c
index e632f16c9102..f210a253da9f 100644
--- a/sound/soc/amd/yc/acp6x-mach.c
+++ b/sound/soc/amd/yc/acp6x-mach.c
@@ -315,6 +315,20 @@ 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, "83BS"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "83HN"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "83L3"),
}
},
@@ -342,6 +356,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "RB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"),
DMI_MATCH(DMI_PRODUCT_NAME, "83J2"),
}
@@ -349,6 +370,13 @@ 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, "83J3"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "UM5302TA"),
}
@@ -360,7 +388,7 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "M5402RA"),
}
},
- {
+ {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
@@ -392,6 +420,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M6501RM"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "E1404FA"),
}
},
@@ -440,6 +475,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
{
.driver_data = &acp6x_card,
.matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "Micro-Star International Co., Ltd."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Bravo 17 D7VF"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "Alienware"),
DMI_MATCH(DMI_PRODUCT_NAME, "Alienware m17 R5 AMD"),
}
@@ -504,6 +546,20 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb1xxx"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Victus by HP Gaming Laptop 15-fb2xxx"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
DMI_MATCH(DMI_BOARD_NAME, "8A42"),
}
},
@@ -546,6 +602,13 @@ static const struct dmi_system_id yc_acp_quirk_table[] = {
.driver_data = &acp6x_card,
.matches = {
DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
+ DMI_MATCH(DMI_BOARD_NAME, "8A81"),
+ }
+ },
+ {
+ .driver_data = &acp6x_card,
+ .matches = {
+ DMI_MATCH(DMI_BOARD_VENDOR, "HP"),
DMI_MATCH(DMI_BOARD_NAME, "8B27"),
}
},
diff --git a/sound/soc/apple/Kconfig b/sound/soc/apple/Kconfig
index 793f7782e0d7..d8dc2f1ccc83 100644
--- a/sound/soc/apple/Kconfig
+++ b/sound/soc/apple/Kconfig
@@ -1,8 +1,11 @@
+menu "Apple"
+
config SND_SOC_APPLE_MCA
tristate "Apple Silicon MCA driver"
depends on ARCH_APPLE || COMPILE_TEST
select SND_DMAENGINE_PCM
- default ARCH_APPLE
help
This option enables an ASoC platform driver for MCA peripherals found
on Apple Silicon SoCs.
+
+endmenu
diff --git a/sound/soc/apple/mca.c b/sound/soc/apple/mca.c
index b4f4696809dd..5dd24ab90d0f 100644
--- a/sound/soc/apple/mca.c
+++ b/sound/soc/apple/mca.c
@@ -464,6 +464,28 @@ err:
return -EINVAL;
}
+static int mca_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mca_cluster *cl = mca_dai_to_cluster(dai);
+ unsigned int mask, nchannels;
+
+ if (cl->tdm_slots) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ mask = cl->tdm_tx_mask;
+ else
+ mask = cl->tdm_rx_mask;
+
+ nchannels = hweight32(mask);
+ } else {
+ nchannels = 2;
+ }
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ 1, nchannels);
+}
+
static int mca_fe_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots, int slot_width)
{
@@ -680,6 +702,7 @@ static int mca_fe_hw_params(struct snd_pcm_substream *substream,
}
static const struct snd_soc_dai_ops mca_fe_ops = {
+ .startup = mca_fe_startup,
.set_fmt = mca_fe_set_fmt,
.set_bclk_ratio = mca_set_bclk_ratio,
.set_tdm_slot = mca_fe_set_tdm_slot,
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 5d59e00be823..4f51612f3dd2 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -1,13 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
-config SND_ATMEL_SOC
- tristate "SoC Audio for the Atmel System-on-Chip"
+menu "Atmel"
depends on HAS_IOMEM
- help
- Say Y or M if you want to add support for codecs attached to
- the ATMEL SSC interface. You will also need
- to select the audio interfaces to support below.
-
-if SND_ATMEL_SOC
config SND_ATMEL_SOC_PDC
bool
@@ -176,4 +169,4 @@ config SND_MCHP_SOC_PDMC
2 data lines. The signal path includes an audio grade programmable
decimation filter and outputs 24-bit audio words.
-endif
+endmenu
diff --git a/sound/soc/atmel/mchp-spdifrx.c b/sound/soc/atmel/mchp-spdifrx.c
index fb820609c043..521bee4998f8 100644
--- a/sound/soc/atmel/mchp-spdifrx.c
+++ b/sound/soc/atmel/mchp-spdifrx.c
@@ -577,7 +577,6 @@ static int mchp_spdifrx_cs_get(struct mchp_spdifrx_dev *dev,
sizeof(ch_stat->data));
pm_runtime_put:
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
unlock:
mutex_unlock(&dev->mlock);
@@ -660,7 +659,6 @@ static int mchp_spdifrx_subcode_ch_get(struct mchp_spdifrx_dev *dev,
sizeof(user_data->data));
pm_runtime_put:
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
unlock:
mutex_unlock(&dev->mlock);
@@ -726,7 +724,6 @@ static int mchp_spdifrx_ulock_get(struct snd_kcontrol *kcontrol,
uvalue->value.integer.value[0] = ctrl->ulock;
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
unlock:
mutex_unlock(&dev->mlock);
@@ -762,7 +759,6 @@ static int mchp_spdifrx_badf_get(struct snd_kcontrol *kcontrol,
ctrl->badf = 0;
}
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
unlock:
mutex_unlock(&dev->mlock);
@@ -811,7 +807,6 @@ static int mchp_spdifrx_signal_get(struct snd_kcontrol *kcontrol,
regmap_read(dev->regmap, SPDIFRX_RSR, &val);
}
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
unlock:
@@ -875,7 +870,6 @@ static int mchp_spdifrx_rate_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] = rate / (32 * SPDIFRX_RSR_IFS(val));
pm_runtime_put:
- pm_runtime_mark_last_busy(dev->dev);
pm_runtime_put_autosuspend(dev->dev);
unlock:
mutex_unlock(&dev->mlock);
diff --git a/sound/soc/au1x/Kconfig b/sound/soc/au1x/Kconfig
index 8a78809e8754..a7630897bc0b 100644
--- a/sound/soc/au1x/Kconfig
+++ b/sound/soc/au1x/Kconfig
@@ -2,6 +2,8 @@
##
## Au1200/Au1550/Au1300 PSC + DBDMA
##
+menu "Au1x"
+
config SND_SOC_AU1XPSC
tristate "SoC Audio for Au12xx/Au13xx/Au1550"
depends on MIPS_ALCHEMY
@@ -63,3 +65,5 @@ config SND_SOC_DB1200
Select this option to enable audio (AC97 and I2S) on the
Alchemy/AMD/RMI/NetLogic Db1200, Db1550 and Db1300 evaluation boards.
If you need Db1300 touchscreen support, you definitely want to say Y.
+
+endmenu
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
index 4218057b0874..de4e8a0daf1c 100644
--- a/sound/soc/bcm/Kconfig
+++ b/sound/soc/bcm/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Broadcom"
+
config SND_BCM2835_SOC_I2S
tristate "SoC Audio support for the Broadcom BCM2835 I2S module"
depends on ARCH_BCM2835 || COMPILE_TEST
@@ -26,3 +28,5 @@ config SND_BCM63XX_I2S_WHISTLER
DSL/PON chips (bcm63158, bcm63178)
If you don't know what to do here, say N
+
+endmenu
diff --git a/sound/soc/cirrus/Kconfig b/sound/soc/cirrus/Kconfig
index 97def4e53fbc..31475e64e7dd 100644
--- a/sound/soc/cirrus/Kconfig
+++ b/sound/soc/cirrus/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Cirrus Logic"
+
config SND_EP93XX_SOC
tristate "SoC Audio support for the Cirrus Logic EP93xx series"
depends on ARCH_EP93XX || COMPILE_TEST
@@ -31,3 +33,4 @@ config SND_EP93XX_SOC_I2S_WATCHDOG
endif # if SND_EP93XX_SOC_I2S
+endmenu
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 20f99cbee29b..6d7e4725d89c 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -103,6 +103,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS47L85
imply SND_SOC_CS47L90
imply SND_SOC_CS47L92
+ imply SND_SOC_CS48L32
imply SND_SOC_CS53L30
imply SND_SOC_CS530X_I2C
imply SND_SOC_CX20442
@@ -119,6 +120,8 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_ES8326
imply SND_SOC_ES8328_SPI
imply SND_SOC_ES8328_I2C
+ imply SND_SOC_ES8375
+ imply SND_SOC_ES8389
imply SND_SOC_ES7134
imply SND_SOC_ES7241
imply SND_SOC_FRAMER
@@ -234,6 +237,8 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT1318_SDW
imply SND_SOC_RT1320_SDW
imply SND_SOC_RT9120
+ imply SND_SOC_RT9123
+ imply SND_SOC_RTQ9124
imply SND_SOC_RTQ9128
imply SND_SOC_SDW_MOCKUP
imply SND_SOC_SGTL5000
@@ -259,8 +264,6 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS2764
imply SND_SOC_TAS2770
imply SND_SOC_TAS2780
- imply SND_SOC_TAS2781_COMLIB
- imply SND_SOC_TAS2781_FMWLIB
imply SND_SOC_TAS2781_I2C
imply SND_SOC_TAS5086
imply SND_SOC_TAS571X
@@ -403,6 +406,7 @@ config SND_SOC_WM_ADSP
default y if SND_SOC_CS35L45_SPI=y
default y if SND_SOC_CS35L45_I2C=y
default y if SND_SOC_CS35L56=y
+ default y if SND_SOC_CS48L32=y
default m if SND_SOC_MADERA=m
default m if SND_SOC_CS47L24=m
default m if SND_SOC_WM5102=m
@@ -413,6 +417,7 @@ config SND_SOC_WM_ADSP
default m if SND_SOC_CS35L45_SPI=m
default m if SND_SOC_CS35L45_I2C=m
default m if SND_SOC_CS35L56=m
+ default m if SND_SOC_CS48L32=m
config SND_SOC_AB8500_CODEC
tristate
@@ -1048,6 +1053,13 @@ config SND_SOC_CS47L92
tristate
depends on MFD_CS47L92
+config SND_SOC_CS48L32
+ tristate "Cirrus Logic CS48L32 audio DSP"
+ depends on SPI_MASTER
+ select REGMAP_SPI
+ help
+ Build the codec driver for the Cirrus Logic CS48L32 audio DSP.
+
# Cirrus Logic Quad-Channel ADC
config SND_SOC_CS53L30
tristate "Cirrus Logic CS53L30 CODEC"
@@ -1199,6 +1211,14 @@ config SND_SOC_ES8328_SPI
depends on SPI_MASTER
select SND_SOC_ES8328
+config SND_SOC_ES8375
+ tristate "Everest Semi ES8375 CODEC"
+ depends on I2C
+
+config SND_SOC_ES8389
+ tristate "Everest Semi ES8389 CODEC"
+ depends on I2C
+
config SND_SOC_FRAMER
tristate "Framer codec"
depends on GENERIC_FRAMER
@@ -1822,6 +1842,28 @@ config SND_SOC_RT9120
Enable support for Richtek RT9120 20W, stereo, inductor-less,
high-efficiency Class-D audio amplifier.
+config SND_SOC_RT9123
+ tristate "Richtek RT9123 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the I2C control mode of Richtek RT9123 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RT9123P
+ tristate "Richtek RT9123P Mono Class-D Amplifier"
+ help
+ Enable support for the HW control mode of Richtek RT9123P 3.2W mono
+ Class-D audio amplifier.
+
+config SND_SOC_RTQ9124
+ tristate "Richtek RTQ9124 Mono Class-D Amplifier"
+ depends on I2C
+ select REGMAP
+ help
+ Enable support for Richtek RTQ9124 1x30W digital input automotive
+ audio amplifier with current sense and real-time load diagnostics.
+
config SND_SOC_RTQ9128
tristate "Richtek RTQ9128 45W Digital Input Amplifier"
depends on I2C
@@ -1990,20 +2032,24 @@ config SND_SOC_TAS2780
digital input mono Class-D audio power amplifiers.
config SND_SOC_TAS2781_COMLIB
+ tristate
+
+config SND_SOC_TAS2781_COMLIB_I2C
depends on I2C
select CRC8
select REGMAP_I2C
tristate
config SND_SOC_TAS2781_FMWLIB
- depends on SND_SOC_TAS2781_COMLIB
+ select SND_SOC_TAS2781_COMLIB
+ select CRC8
tristate
default n
config SND_SOC_TAS2781_I2C
tristate "Texas Instruments TAS2781 speaker amplifier based on I2C"
depends on I2C
- select SND_SOC_TAS2781_COMLIB
+ select SND_SOC_TAS2781_COMLIB_I2C
select SND_SOC_TAS2781_FMWLIB
help
Enable support for Texas Instruments TAS2781 Smart Amplifier
@@ -2238,6 +2284,7 @@ config SND_SOC_WCD938X
tristate
depends on SOUNDWIRE || !SOUNDWIRE
select SND_SOC_WCD_CLASSH
+ select MULTIPLEXER
config SND_SOC_WCD938X_SDW
tristate "WCD9380/WCD9385 Codec - SDW"
@@ -2478,7 +2525,7 @@ config SND_SOC_WM8997
depends on MFD_WM8997 && MFD_ARIZONA
config SND_SOC_WM8998
- tristate
+ tristate "Wolfson Microelectronics WM8998 codec driver"
depends on MFD_WM8998 && MFD_ARIZONA
config SND_SOC_WM9081
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 10f726066b6c..a68c3d192a1b 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -111,6 +111,7 @@ snd-soc-cs47l35-y := cs47l35.o
snd-soc-cs47l85-y := cs47l85.o
snd-soc-cs47l90-y := cs47l90.o
snd-soc-cs47l92-y := cs47l92.o
+snd-soc-cs48l32-y := cs48l32.o cs48l32-tables.o
snd-soc-cs53l30-y := cs53l30.o
snd-soc-cs530x-y := cs530x.o
snd-soc-cs530x-i2c-y := cs530x-i2c.o
@@ -133,6 +134,8 @@ snd-soc-es8326-y := es8326.o
snd-soc-es8328-y := es8328.o
snd-soc-es8328-i2c-y := es8328-i2c.o
snd-soc-es8328-spi-y := es8328-spi.o
+snd-soc-es8375-y := es8375.o
+snd-soc-es8389-y := es8389.o
snd-soc-framer-y := framer-codec.o
snd-soc-gtm601-y := gtm601.o
snd-soc-hdac-hdmi-y := hdac_hdmi.o
@@ -270,6 +273,9 @@ snd-soc-rt715-sdca-y := rt715-sdca.o rt715-sdca-sdw.o
snd-soc-rt721-sdca-y := rt721-sdca.o rt721-sdca-sdw.o
snd-soc-rt722-sdca-y := rt722-sdca.o rt722-sdca-sdw.o
snd-soc-rt9120-y := rt9120.o
+snd-soc-rt9123-y := rt9123.o
+snd-soc-rt9123p-y := rt9123p.o
+snd-soc-rtq9124-y := rtq9124.o
snd-soc-rtq9128-y := rtq9128.o
snd-soc-sdw-mockup-y := sdw-mockup.o
snd-soc-sgtl5000-y := sgtl5000.o
@@ -305,6 +311,7 @@ snd-soc-tas6424-y := tas6424.o
snd-soc-tda7419-y := tda7419.o
snd-soc-tas2770-y := tas2770.o
snd-soc-tas2781-comlib-y := tas2781-comlib.o
+snd-soc-tas2781-comlib-i2c-y := tas2781-comlib-i2c.o
snd-soc-tas2781-fmwlib-y := tas2781-fmwlib.o
snd-soc-tas2781-i2c-y := tas2781-i2c.o
snd-soc-tfa9879-y := tfa9879.o
@@ -529,6 +536,7 @@ obj-$(CONFIG_SND_SOC_CS47L35) += snd-soc-cs47l35.o
obj-$(CONFIG_SND_SOC_CS47L85) += snd-soc-cs47l85.o
obj-$(CONFIG_SND_SOC_CS47L90) += snd-soc-cs47l90.o
obj-$(CONFIG_SND_SOC_CS47L92) += snd-soc-cs47l92.o
+obj-$(CONFIG_SND_SOC_CS48L32) += snd-soc-cs48l32.o
obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
obj-$(CONFIG_SND_SOC_CS530X) += snd-soc-cs530x.o
obj-$(CONFIG_SND_SOC_CS530X_I2C) += snd-soc-cs530x-i2c.o
@@ -551,6 +559,8 @@ obj-$(CONFIG_SND_SOC_ES8326) += snd-soc-es8326.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
+obj-$(CONFIG_SND_SOC_ES8375) += snd-soc-es8375.o
+obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o
obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o
obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o
obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o
@@ -684,6 +694,9 @@ obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o
obj-$(CONFIG_SND_SOC_RT721_SDCA_SDW) += snd-soc-rt721-sdca.o
obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o
obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o
+obj-$(CONFIG_SND_SOC_RT9123) += snd-soc-rt9123.o
+obj-$(CONFIG_SND_SOC_RT9123P) += snd-soc-rt9123p.o
+obj-$(CONFIG_SND_SOC_RTQ9124) += snd-soc-rtq9124.o
obj-$(CONFIG_SND_SOC_RTQ9128) += snd-soc-rtq9128.o
obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o
obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o
@@ -713,6 +726,7 @@ obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o
obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o
+obj-$(CONFIG_SND_SOC_TAS2781_COMLIB_I2C) += snd-soc-tas2781-comlib-i2c.o
obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o
obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o
obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 0e013edfe63d..d8444a083af2 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -13,6 +13,7 @@
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/ac97_codec.h>
@@ -127,9 +128,18 @@ static int ac97_probe(struct platform_device *pdev)
&soc_component_dev_ac97, &ac97_dai, 1);
}
+#ifdef CONFIG_OF
+static const struct of_device_id ac97_codec_of_match[] = {
+ { .compatible = "realtek,alc203", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ac97_codec_of_match);
+#endif
+
static struct platform_driver ac97_codec_driver = {
.driver = {
.name = "ac97-codec",
+ .of_match_table = of_match_ptr(ac97_codec_of_match),
},
.probe = ac97_probe,
diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c
index abc4764697a5..14259807c872 100644
--- a/sound/soc/codecs/adau7118.c
+++ b/sound/soc/codecs/adau7118.c
@@ -169,6 +169,12 @@ static int adau7118_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_RIGHT_J:
st->right_j = true;
break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ret = snd_soc_component_update_bits(dai->component,
+ ADAU7118_REG_SPT_CTRL1,
+ ADAU7118_DATA_FMT_MASK,
+ ADAU7118_DATA_FMT(1));
+ break;
default:
dev_err(st->dev, "Invalid format %d",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
index 5f3a68dfe7bd..57cf601d3df3 100644
--- a/sound/soc/codecs/ak4458.c
+++ b/sound/soc/codecs/ak4458.c
@@ -586,13 +586,9 @@ static const struct snd_pcm_hw_constraint_list ak4458_rate_constraints = {
static int ak4458_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- int ret;
-
- ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &ak4458_rate_constraints);
-
- return ret;
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &ak4458_rate_constraints);
}
static const struct snd_soc_dai_ops ak4458_dai_ops = {
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index 21a44476f48d..6525d50b7ab2 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -6,11 +6,13 @@
* (c) 2013 Daniel Mack <zonque@gmail.com>
*/
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/soc.h>
#include <sound/pcm.h>
#include <sound/initval.h>
@@ -20,7 +22,7 @@ static const char * const supply_names[] = {
};
struct ak5386_priv {
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
};
@@ -110,8 +112,7 @@ static int ak5386_hw_params(struct snd_pcm_substream *substream,
* the AK5386 in power-down mode (PDN pin = “L”).
*/
- if (gpio_is_valid(priv->reset_gpio))
- gpio_set_value(priv->reset_gpio, 1);
+ gpiod_set_value(priv->reset_gpio, 1);
return 0;
}
@@ -122,8 +123,7 @@ static int ak5386_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct ak5386_priv *priv = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(priv->reset_gpio))
- gpio_set_value(priv->reset_gpio, 0);
+ gpiod_set_value(priv->reset_gpio, 0);
return 0;
}
@@ -177,14 +177,12 @@ static int ak5386_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
- priv->reset_gpio = of_get_named_gpio(dev->of_node,
- "reset-gpio", 0);
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio),
+ "Failed to get AK5386 reset GPIO\n");
- if (gpio_is_valid(priv->reset_gpio))
- if (devm_gpio_request_one(dev, priv->reset_gpio,
- GPIOF_OUT_INIT_LOW,
- "AK5386 Reset"))
- priv->reset_gpio = -EINVAL;
+ gpiod_set_consumer_name(priv->reset_gpio, "AK5386 Reset");
return devm_snd_soc_register_component(dev, &soc_component_ak5386,
&ak5386_dai, 1);
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
index 9c15ddba6008..22f9c431a0e5 100644
--- a/sound/soc/codecs/arizona-jack.c
+++ b/sound/soc/codecs/arizona-jack.c
@@ -319,7 +319,6 @@ static void arizona_stop_mic(struct arizona_priv *info)
if (change) {
regulator_disable(info->micvdd);
- pm_runtime_mark_last_busy(arizona->dev);
pm_runtime_put_autosuspend(arizona->dev);
}
}
@@ -1127,7 +1126,6 @@ out:
mutex_unlock(&info->lock);
- pm_runtime_mark_last_busy(arizona->dev);
pm_runtime_put_autosuspend(arizona->dev);
return IRQ_HANDLED;
diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c
index 6c50c4a18b6a..4f76ebe11cc7 100644
--- a/sound/soc/codecs/aw88166.c
+++ b/sound/soc/codecs/aw88166.c
@@ -11,6 +11,7 @@
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/minmax.h>
#include <linux/regmap.h>
#include <sound/soc.h>
#include "aw88166.h"
@@ -909,11 +910,7 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
goto error_operation;
for (i = 0; i < len; i += AW88166_MAX_RAM_WRITE_BYTE_SIZE) {
- if ((len - i) < AW88166_MAX_RAM_WRITE_BYTE_SIZE)
- tmp_len = len - i;
- else
- tmp_len = AW88166_MAX_RAM_WRITE_BYTE_SIZE;
-
+ tmp_len = min(len - i, AW88166_MAX_RAM_WRITE_BYTE_SIZE);
ret = regmap_raw_write(aw_dev->regmap, AW88166_DSPMDAT_REG,
&data[i], tmp_len);
if (ret)
diff --git a/sound/soc/codecs/aw88395/aw88395_device.c b/sound/soc/codecs/aw88395/aw88395_device.c
index b7ea8be0d0cb..e1430940015d 100644
--- a/sound/soc/codecs/aw88395/aw88395_device.c
+++ b/sound/soc/codecs/aw88395/aw88395_device.c
@@ -10,6 +10,7 @@
#include <linux/crc32.h>
#include <linux/i2c.h>
+#include <linux/minmax.h>
#include <linux/regmap.h>
#include "aw88395_device.h"
#include "aw88395_reg.h"
@@ -1114,11 +1115,7 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
goto error_operation;
for (i = 0; i < len; i += AW88395_MAX_RAM_WRITE_BYTE_SIZE) {
- if ((len - i) < AW88395_MAX_RAM_WRITE_BYTE_SIZE)
- tmp_len = len - i;
- else
- tmp_len = AW88395_MAX_RAM_WRITE_BYTE_SIZE;
-
+ tmp_len = min(len - i, AW88395_MAX_RAM_WRITE_BYTE_SIZE);
ret = regmap_raw_write(aw_dev->regmap, AW88395_DSPMDAT_REG,
&data[i], tmp_len);
if (ret)
diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h
index 0f750f654f3e..6f8b30b475da 100644
--- a/sound/soc/codecs/aw88395/aw88395_device.h
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -102,6 +102,11 @@ struct aw_profctrl_desc {
unsigned int cur_mode;
};
+enum {
+ CALI_RESULT_NORMAL,
+ CALI_RESULT_ERROR,
+};
+
struct aw_volume_desc {
unsigned int init_volume;
unsigned int mute_volume;
@@ -124,9 +129,25 @@ struct aw_cali_delay_desc {
unsigned int delay;
};
+#define AW_CALI_CFG_NUM (4)
+struct cali_cfg {
+ uint32_t data[AW_CALI_CFG_NUM];
+};
+
+struct aw_cali_backup_desc {
+ unsigned int dsp_ng_cfg;
+ unsigned int dsp_lp_cfg;
+};
+
struct aw_cali_desc {
u32 cali_re;
u32 ra;
+ bool cali_switch;
+ bool cali_running;
+ uint16_t cali_result;
+ uint16_t store_vol;
+ struct cali_cfg cali_cfg;
+ struct aw_cali_backup_desc backup_info;
};
struct aw_container {
diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c
index ee3cc2a95f85..bad3ad6b8c0e 100644
--- a/sound/soc/codecs/aw88399.c
+++ b/sound/soc/codecs/aw88399.c
@@ -11,7 +11,9 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/firmware.h>
+#include <linux/minmax.h>
#include <linux/regmap.h>
+#include <linux/sort.h>
#include <sound/soc.h>
#include "aw88399.h"
#include "aw88395/aw88395_device.h"
@@ -44,6 +46,67 @@ static int aw_dev_dsp_write_16bit(struct aw_device *aw_dev,
return 0;
}
+static int aw_dev_dsp_write_32bit(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data)
+{
+ unsigned int temp_data;
+ int ret;
+
+ ret = regmap_write(aw_dev->regmap, AW88399_DSPMADD_REG, dsp_addr);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write addr error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data & AW88395_DSP_16_DATA_MASK;
+ ret = regmap_write(aw_dev->regmap, AW88399_DSPMDAT_REG, temp_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "%s write datal error, ret=%d", __func__, ret);
+ return ret;
+ }
+
+ temp_data = dsp_data >> 16;
+ ret = regmap_write(aw_dev->regmap, AW88399_DSPMDAT_REG, temp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "%s write datah error, ret=%d", __func__, ret);
+
+ return ret;
+}
+
+static int aw_dev_dsp_write(struct aw_device *aw_dev,
+ unsigned short dsp_addr, unsigned int dsp_data, unsigned char data_type)
+{
+ unsigned int reg_value;
+ int ret;
+
+ mutex_lock(&aw_dev->dsp_lock);
+ switch (data_type) {
+ case AW88395_DSP_16_DATA:
+ ret = aw_dev_dsp_write_16bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 16-bit dsp_data[0x%x] failed",
+ dsp_addr, dsp_data);
+ break;
+ case AW88395_DSP_32_DATA:
+ ret = aw_dev_dsp_write_32bit(aw_dev, dsp_addr, dsp_data);
+ if (ret)
+ dev_err(aw_dev->dev, "write dsp_addr[0x%x] 32-bit dsp_data[0x%x] failed",
+ dsp_addr, dsp_data);
+ break;
+ default:
+ dev_err(aw_dev->dev, "data type[%d] unsupported", data_type);
+ ret = -EINVAL;
+ break;
+ }
+
+ /* clear dsp chip select state */
+ if (regmap_read(aw_dev->regmap, 0x00, &reg_value))
+ dev_err(aw_dev->dev, "%s fail to clear chip state. Err=%d\n", __func__, ret);
+ mutex_unlock(&aw_dev->dsp_lock);
+
+ return ret;
+}
+
static int aw_dev_dsp_read_16bit(struct aw_device *aw_dev,
unsigned short dsp_addr, unsigned int *dsp_data)
{
@@ -451,14 +514,14 @@ static int aw_dev_set_vcalb(struct aw88399 *aw88399)
case AW88399_DEV_VDSEL_VSENSE:
ret = aw88399_dev_get_vcalk(aw88399, &vcalk);
vcal_k = vcalk * AW88399_VCABLK_FACTOR + AW88399_CABL_BASE_VALUE;
- vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR /
+ vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_FACTOR / AW88399_ISCAL_FACTOR *
ical_k / vcal_k * aw88399->vcalb_init_val;
break;
case AW88399_DEV_VDSEL_DAC:
ret = aw88399_dev_get_internal_vcalk(aw88399, &vcalk);
vcal_k = vcalk * AW88399_VCABLK_DAC_FACTOR + AW88399_CABL_BASE_VALUE;
vcalb = AW88399_VCALB_ACCURACY * AW88399_VSCAL_DAC_FACTOR /
- AW88399_ISCAL_DAC_FACTOR / ical_k /
+ AW88399_ISCAL_DAC_FACTOR * ical_k /
vcal_k * aw88399->vcalb_init_val;
break;
default:
@@ -872,11 +935,7 @@ static int aw_dev_dsp_update_container(struct aw_device *aw_dev,
goto error_operation;
for (i = 0; i < len; i += AW88399_MAX_RAM_WRITE_BYTE_SIZE) {
- if ((len - i) < AW88399_MAX_RAM_WRITE_BYTE_SIZE)
- tmp_len = len - i;
- else
- tmp_len = AW88399_MAX_RAM_WRITE_BYTE_SIZE;
-
+ tmp_len = min(len - i, AW88399_MAX_RAM_WRITE_BYTE_SIZE);
ret = regmap_raw_write(aw_dev->regmap, AW88399_DSPMDAT_REG,
&data[i], tmp_len);
if (ret)
@@ -1359,6 +1418,329 @@ static struct snd_soc_dai_driver aw88399_dai[] = {
},
};
+static void aw_cali_svc_run_mute(struct aw_device *aw_dev, uint16_t cali_result)
+{
+ if (cali_result == CALI_RESULT_ERROR)
+ aw88399_dev_mute(aw_dev, true);
+ else if (cali_result == CALI_RESULT_NORMAL)
+ aw88399_dev_mute(aw_dev, false);
+}
+
+static int aw_cali_svc_get_cali_cfg(struct aw_device *aw_dev)
+{
+ struct cali_cfg *cali_cfg = &aw_dev->cali_desc.cali_cfg;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ &cali_cfg->data[0], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ &cali_cfg->data[1], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ &cali_cfg->data[2], AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ &cali_cfg->data[3], AW88399_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_set_cali_cfg(struct aw_device *aw_dev,
+ struct cali_cfg cali_cfg)
+{
+ int ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH,
+ cali_cfg.data[0], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH,
+ cali_cfg.data[1], AW88399_DSP_32_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_ADPZ_USTEPN,
+ cali_cfg.data[2], AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ ret = aw_dev_dsp_write(aw_dev, AW88399_DSP_REG_CFG_RE_ALPHA,
+ cali_cfg.data[3], AW88399_DSP_16_DATA);
+
+ return ret;
+}
+
+static int aw_cali_svc_cali_en(struct aw_device *aw_dev, bool cali_en)
+{
+ struct cali_cfg set_cfg;
+ int ret;
+
+ aw_dev_dsp_enable(aw_dev, false);
+ if (cali_en) {
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, AW883XX_DSP_NG_EN_DISABLE_VALUE);
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ AW88399_DSP_LOW_POWER_SWITCH_DISABLE, AW88399_DSP_16_DATA);
+
+ ret = aw_cali_svc_get_cali_cfg(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get cali cfg failed\n");
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ set_cfg.data[0] = 0;
+ set_cfg.data[1] = 0;
+ set_cfg.data[2] = -1;
+ set_cfg.data[3] = 1;
+
+ ret = aw_cali_svc_set_cali_cfg(aw_dev, set_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "set cali cfg failed\n");
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ aw_dev_dsp_enable(aw_dev, true);
+ return ret;
+ }
+ } else {
+ aw_cali_svc_set_cali_cfg(aw_dev, aw_dev->cali_desc.cali_cfg);
+ }
+
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+}
+
+static int aw_cali_svc_cali_run_dsp_vol(struct aw_device *aw_dev, bool enable)
+{
+ unsigned int reg_val;
+ int ret;
+
+ if (enable) {
+ ret = regmap_read(aw_dev->regmap, AW88399_DSPCFG_REG, &reg_val);
+ if (ret) {
+ dev_err(aw_dev->dev, "read reg 0x%x failed\n", AW88399_DSPCFG_REG);
+ return ret;
+ }
+
+ aw_dev->cali_desc.store_vol = reg_val & (~AW88399_DSP_VOL_MASK);
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, AW88399_DSP_VOL_MUTE);
+ } else {
+ ret = regmap_update_bits(aw_dev->regmap, AW88399_DSPCFG_REG,
+ ~AW88399_DSP_VOL_MASK, aw_dev->cali_desc.store_vol);
+ }
+
+ return ret;
+}
+
+static void aw_cali_svc_backup_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+ unsigned int reg_val, dsp_val;
+
+ regmap_read(aw_dev->regmap, AW88399_DBGCTRL_REG, &reg_val);
+ backup_desc->dsp_ng_cfg = reg_val & (~AW883XX_DSP_NG_EN_MASK);
+
+ aw_dev_dsp_read(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ &dsp_val, AW88399_DSP_16_DATA);
+
+ backup_desc->dsp_lp_cfg = dsp_val;
+}
+
+static void aw_cali_svc_recover_info(struct aw_device *aw_dev)
+{
+ struct aw_cali_backup_desc *backup_desc = &aw_dev->cali_desc.backup_info;
+
+ regmap_update_bits(aw_dev->regmap, AW88399_DBGCTRL_REG,
+ ~AW883XX_DSP_NG_EN_MASK, backup_desc->dsp_ng_cfg);
+
+ aw_dev_dsp_write(aw_dev, AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR,
+ backup_desc->dsp_lp_cfg, AW88399_DSP_16_DATA);
+}
+
+static int aw_cali_svc_cali_re_mode_enable(struct aw_device *aw_dev, bool is_enable)
+{
+ int ret;
+
+ if (is_enable) {
+ ret = aw_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start\n");
+ return ret;
+ }
+
+ ret = aw_dev_get_dsp_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "dsp status error\n");
+ return ret;
+ }
+
+ aw_cali_svc_backup_info(aw_dev);
+ ret = aw_cali_svc_cali_en(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_cali_svc_cali_en failed\n");
+ return ret;
+ }
+
+ ret = aw_cali_svc_cali_run_dsp_vol(aw_dev, true);
+ if (ret) {
+ aw_cali_svc_cali_en(aw_dev, false);
+ return ret;
+ }
+
+ } else {
+ aw_cali_svc_cali_run_dsp_vol(aw_dev, false);
+ aw_cali_svc_recover_info(aw_dev);
+ aw_cali_svc_cali_en(aw_dev, false);
+ }
+
+ return 0;
+}
+
+static int aw_cali_svc_get_dev_re(struct aw_device *aw_dev, uint32_t *re)
+{
+ uint32_t dsp_re, show_re;
+ int ret;
+
+ ret = aw_dev_dsp_read(aw_dev, AW88399_DSP_REG_CALRE, &dsp_re, AW88399_DSP_16_DATA);
+ if (ret)
+ return ret;
+
+ show_re = AW88399_DSP_RE_TO_SHOW_RE(dsp_re, AW88399_DSP_REG_CALRE_SHIFT);
+
+ *re = (uint32_t)(show_re - aw_dev->cali_desc.ra);
+
+ return 0;
+}
+
+static void aw_cali_svc_del_max_min_ave_algo(uint32_t *data, int data_size, uint32_t *dsp_re)
+{
+ int sum = 0, i;
+
+ for (i = 1; i < data_size - 1; i++)
+ sum += data[i];
+
+ *dsp_re = sum / (data_size - AW_CALI_DATA_SUM_RM);
+}
+
+static int aw_cali_svc_get_iv_st(struct aw_device *aw_dev)
+{
+ unsigned int reg_data;
+ int ret, i;
+
+ for (i = 0; i < AW_GET_IV_CNT_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88399_ASR1_REG, &reg_data);
+ if (ret) {
+ dev_err(aw_dev->dev, "read 0x%x failed\n", AW88399_ASR1_REG);
+ return ret;
+ }
+
+ reg_data &= (~AW88399_REABS_MASK);
+ if (!reg_data)
+ return 0;
+ msleep(30);
+ }
+
+ dev_err(aw_dev->dev, "IV data abnormal, please check\n");
+
+ return -EINVAL;
+}
+
+static int compare_ints(const void *a, const void *b)
+{
+ return *(int *)a - *(int *)b;
+}
+
+static int aw_cali_svc_get_smooth_cali_re(struct aw_device *aw_dev)
+{
+ uint32_t re_temp[AW_CALI_READ_CNT_MAX];
+ uint32_t dsp_re;
+ int ret, i;
+
+ for (i = 0; i < AW_CALI_READ_CNT_MAX; i++) {
+ ret = aw_cali_svc_get_dev_re(aw_dev, &re_temp[i]);
+ if (ret)
+ goto cali_re_fail;
+ msleep(30);
+ }
+
+ sort(re_temp, AW_CALI_READ_CNT_MAX, sizeof(uint32_t), compare_ints, NULL);
+
+ aw_cali_svc_del_max_min_ave_algo(re_temp, AW_CALI_READ_CNT_MAX, &dsp_re);
+
+ ret = aw_cali_svc_get_iv_st(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "get iv data failed");
+ goto cali_re_fail;
+ }
+
+ if (dsp_re < AW88399_CALI_RE_MIN || dsp_re > AW88399_CALI_RE_MAX) {
+ dev_err(aw_dev->dev, "out range re value: [%d]mohm\n", dsp_re);
+ aw_dev->cali_desc.cali_re = dsp_re;
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+
+ return 0;
+ }
+
+ aw_dev->cali_desc.cali_result = CALI_RESULT_NORMAL;
+
+ aw_dev->cali_desc.cali_re = dsp_re;
+ dev_dbg(aw_dev->dev, "re[%d]mohm\n", aw_dev->cali_desc.cali_re);
+
+ aw_dev_dsp_enable(aw_dev, false);
+ aw_dev_update_cali_re(&aw_dev->cali_desc);
+ aw_dev_dsp_enable(aw_dev, true);
+
+ return 0;
+
+cali_re_fail:
+ aw_dev->cali_desc.cali_result = CALI_RESULT_ERROR;
+ aw_cali_svc_run_mute(aw_dev, aw_dev->cali_desc.cali_result);
+ return -EINVAL;
+}
+
+static int aw_cali_svc_dev_cali_re(struct aw88399 *aw88399)
+{
+ struct aw_device *aw_dev = aw88399->aw_pa;
+ struct aw_cali_desc *cali_desc = &aw_dev->cali_desc;
+ int ret;
+
+ if (cali_desc->cali_running) {
+ dev_err(aw_dev->dev, "calibration in progress\n");
+ return -EINVAL;
+ }
+
+ cali_desc->cali_running = true;
+ aw_cali_svc_run_mute(aw_dev, CALI_RESULT_NORMAL);
+
+ ret = aw_cali_svc_cali_re_mode_enable(aw_dev, true);
+ if (ret) {
+ dev_err(aw_dev->dev, "start cali re failed\n");
+ goto re_mode_err;
+ }
+
+ msleep(1000);
+
+ ret = aw_cali_svc_get_smooth_cali_re(aw_dev);
+ if (ret)
+ dev_err(aw_dev->dev, "get cali re failed\n");
+
+ aw_cali_svc_cali_re_mode_enable(aw_dev, false);
+
+re_mode_err:
+ cali_desc->cali_running = false;
+
+ return ret;
+}
+
static int aw88399_get_fade_in_time(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1619,6 +2001,53 @@ static int aw88399_re_set(struct snd_kcontrol *kcontrol,
return 0;
}
+static int aw88399_calib_switch_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->cali_desc.cali_switch;
+
+ return 0;
+}
+
+static int aw88399_calib_switch_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->cali_desc.cali_switch == ucontrol->value.integer.value[0])
+ return 0;
+
+ aw_dev->cali_desc.cali_switch = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int aw88399_calib_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /* do nothing */
+ return 0;
+}
+
+static int aw88399_calib_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88399 *aw88399 = snd_soc_component_get_drvdata(codec);
+ struct aw_device *aw_dev = aw88399->aw_pa;
+
+ if (aw_dev->status && aw_dev->cali_desc.cali_switch)
+ aw_cali_svc_dev_cali_re(aw88399);
+
+ return 0;
+}
+
static int aw88399_dev_init(struct aw88399 *aw88399, struct aw_container *aw_cfg)
{
struct aw_device *aw_dev = aw88399->aw_pa;
@@ -1711,6 +2140,10 @@ static const struct snd_kcontrol_new aw88399_controls[] = {
aw88399_get_fade_out_time, aw88399_set_fade_out_time),
SOC_SINGLE_EXT("Calib", 0, 0, AW88399_CALI_RE_MAX, 0,
aw88399_re_get, aw88399_re_set),
+ SOC_SINGLE_BOOL_EXT("Calib Switch", 0,
+ aw88399_calib_switch_get, aw88399_calib_switch_set),
+ SOC_SINGLE_EXT("Trigger Calib", SND_SOC_NOPM, 0, 1, 0,
+ aw88399_calib_get, aw88399_calib_set),
AW88399_PROFILE_EXT("AW88399 Profile Set", aw88399_profile_info,
aw88399_profile_get, aw88399_profile_set),
};
diff --git a/sound/soc/codecs/aw88399.h b/sound/soc/codecs/aw88399.h
index 5e9cdf725d3d..cacc03b1eefa 100644
--- a/sound/soc/codecs/aw88399.h
+++ b/sound/soc/codecs/aw88399.h
@@ -451,6 +451,24 @@
#define AW88399_WDT_CNT_MASK \
(~(((1<<AW88399_WDT_CNT_BITS_LEN)-1) << AW88399_WDT_CNT_START_BIT))
+#define AW88399_REABS_START_BIT (3)
+#define AW88399_REABS_BITS_LEN (1)
+#define AW88399_REABS_MASK \
+ (~(((1<<AW88399_REABS_BITS_LEN)-1) << AW88399_REABS_START_BIT))
+
+#define AW88399_DSP_VOL_START_BIT (8)
+#define AW88399_DSP_VOL_BITS_LEN (8)
+#define AW88399_DSP_VOL_MASK \
+ (~(((1<<AW88399_DSP_VOL_BITS_LEN)-1) << AW88399_DSP_VOL_START_BIT))
+
+#define AW883XX_DSP_NG_EN_START (13)
+#define AW883XX_DSP_NG_EN_LEN (1)
+#define AW883XX_DSP_NG_EN_MASK \
+ (~(((1 << AW883XX_DSP_NG_EN_LEN) - 1) << AW883XX_DSP_NG_EN_START))
+#define AW883XX_DSP_NG_EN_DISABLE (0)
+#define AW883XX_DSP_NG_EN_DISABLE_VALUE \
+ (AW883XX_DSP_NG_EN_DISABLE << AW883XX_DSP_NG_EN_START)
+
#define AW88399_VOLUME_STEP_DB (64)
#define AW88399_VOL_DEFAULT_VALUE (0)
#define AW88399_DSP_ODD_NUM_BIT_TEST (0x5555)
@@ -507,6 +525,22 @@
#define FADE_TIME_MAX 100000
#define FADE_TIME_MIN 0
+#define AW_CALI_READ_CNT_MAX (8)
+#define AW88399_DSP_REG_CALRE (0x8141)
+#define AW88399_DSP_REG_CALRE_SHIFT (10)
+#define AW_CALI_DATA_SUM_RM (2)
+
+#define AW88399_DSP_REG_CFG_MBMEC_ACTAMPTH (0x9B4C)
+#define AW88399_DSP_REG_CFG_MBMEC_NOISEAMPTH (0x9B4E)
+#define AW88399_DSP_REG_CFG_ADPZ_USTEPN (0x9B6E)
+#define AW88399_DSP_REG_CFG_RE_ALPHA (0x9BD4)
+#define AW_GET_IV_CNT_MAX (6)
+
+#define AW88399_DSP_VOL_MUTE (0XFF00)
+
+#define AW88399_DSP_LOW_POWER_SWITCH_CFG_ADDR (0x9BEC)
+#define AW88399_DSP_LOW_POWER_SWITCH_DISABLE (0x110b)
+
#define AW88399_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
{ \
.iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c
index 571222ec520c..937c8cec682a 100644
--- a/sound/soc/codecs/cros_ec_codec.c
+++ b/sound/soc/codecs/cros_ec_codec.c
@@ -18,6 +18,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/platform_data/cros_ec_commands.h>
#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
@@ -961,7 +962,6 @@ static int cros_ec_codec_platform_probe(struct platform_device *pdev)
struct ec_response_ec_codec_get_capabilities r;
int ret;
#ifdef CONFIG_OF
- struct device_node *node;
struct resource res;
u64 ec_shm_size;
const __be32 *regaddr_p;
@@ -981,22 +981,18 @@ static int cros_ec_codec_platform_probe(struct platform_device *pdev)
priv->ec_shm_addr, priv->ec_shm_len);
}
- node = of_parse_phandle(dev->of_node, "memory-region", 0);
- if (node) {
- ret = of_address_to_resource(node, 0, &res);
- if (!ret) {
- priv->ap_shm_phys_addr = res.start;
- priv->ap_shm_len = resource_size(&res);
- priv->ap_shm_addr =
- (uint64_t)(uintptr_t)devm_ioremap_wc(
- dev, priv->ap_shm_phys_addr,
- priv->ap_shm_len);
- priv->ap_shm_last_alloc = priv->ap_shm_phys_addr;
-
- dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n",
- priv->ap_shm_phys_addr, priv->ap_shm_len);
- }
- of_node_put(node);
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 0, &res);
+ if (!ret) {
+ priv->ap_shm_phys_addr = res.start;
+ priv->ap_shm_len = resource_size(&res);
+ priv->ap_shm_addr =
+ (uint64_t)(uintptr_t)devm_ioremap_wc(
+ dev, priv->ap_shm_phys_addr,
+ priv->ap_shm_len);
+ priv->ap_shm_last_alloc = priv->ap_shm_phys_addr;
+
+ dev_dbg(dev, "ap_shm_phys_addr=%#llx len=%#x\n",
+ priv->ap_shm_phys_addr, priv->ap_shm_len);
}
#endif
diff --git a/sound/soc/codecs/cs-amp-lib-test.c b/sound/soc/codecs/cs-amp-lib-test.c
index 45626f99a417..f53650128fc3 100644
--- a/sound/soc/codecs/cs-amp-lib-test.c
+++ b/sound/soc/codecs/cs-amp-lib-test.c
@@ -5,19 +5,25 @@
// Copyright (C) 2024 Cirrus Logic, Inc. and
// Cirrus Logic International Semiconductor Ltd.
+#include <kunit/resource.h>
#include <kunit/test.h>
#include <kunit/static_stub.h>
+#include <linux/device/faux.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/firmware/cirrus/wmfw.h>
#include <linux/gpio/driver.h>
#include <linux/list.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/platform_device.h>
#include <linux/random.h>
#include <sound/cs-amp-lib.h>
+KUNIT_DEFINE_ACTION_WRAPPER(faux_device_destroy_wrapper, faux_device_destroy,
+ struct faux_device *)
+
struct cs_amp_lib_test_priv {
- struct platform_device amp_pdev;
+ struct faux_device *amp_dev;
struct cirrus_amp_efi_data *cal_blob;
struct list_head ctl_write_list;
@@ -40,8 +46,7 @@ static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps
unsigned int blob_size;
int i;
- blob_size = offsetof(struct cirrus_amp_efi_data, data) +
- sizeof(struct cirrus_amp_cal_data) * num_amps;
+ blob_size = struct_size(priv->cal_blob, data, num_amps);
priv->cal_blob = kunit_kzalloc(test, blob_size, GFP_KERNEL);
KUNIT_ASSERT_NOT_NULL(test, priv->cal_blob);
@@ -49,7 +54,7 @@ static void cs_amp_lib_test_init_dummy_cal_blob(struct kunit *test, int num_amps
priv->cal_blob->size = blob_size;
priv->cal_blob->count = num_amps;
- get_random_bytes(priv->cal_blob->data, sizeof(struct cirrus_amp_cal_data) * num_amps);
+ get_random_bytes(priv->cal_blob->data, flex_array_size(priv->cal_blob, data, num_amps));
/* Ensure all timestamps are non-zero to mark the entry valid. */
for (i = 0; i < num_amps; i++)
@@ -99,7 +104,7 @@ static void cs_amp_lib_test_cal_data_too_short_test(struct kunit *test)
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable_nohead);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
@@ -142,7 +147,7 @@ static void cs_amp_lib_test_cal_count_too_big_test(struct kunit *test)
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable_bad_count);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
KUNIT_EXPECT_EQ(test, ret, -EOVERFLOW);
kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
@@ -169,7 +174,7 @@ static void cs_amp_lib_test_no_cal_data_test(struct kunit *test)
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable_none);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 0, &result_data);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 0, &result_data);
KUNIT_EXPECT_EQ(test, ret, -ENOENT);
kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
@@ -223,7 +228,7 @@ static void cs_amp_lib_test_get_efi_cal_by_uid_test(struct kunit *test)
cs_amp_lib_test_get_efi_variable);
target_uid = cs_amp_lib_test_get_target_uid(test);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid, -1, &result_data);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid, -1, &result_data);
KUNIT_EXPECT_EQ(test, ret, 0);
kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
@@ -257,7 +262,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_unchecked_test(struct kunit *te
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0,
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0,
param->amp_index, &result_data);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -292,7 +297,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_checked_test(struct kunit *test
cs_amp_lib_test_get_efi_variable);
target_uid = cs_amp_lib_test_get_target_uid(test);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid,
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
param->amp_index, &result_data);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -331,7 +336,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_uid_mismatch_test(struct kunit
/* Get a target UID that won't match the entry */
target_uid = ~cs_amp_lib_test_get_target_uid(test);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, target_uid,
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, target_uid,
param->amp_index, &result_data);
KUNIT_EXPECT_EQ(test, ret, -ENOENT);
@@ -363,7 +368,7 @@ static void cs_amp_lib_test_get_efi_cal_by_index_fallback_test(struct kunit *tes
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid,
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid,
param->amp_index, &result_data);
KUNIT_EXPECT_EQ(test, ret, 0);
@@ -405,7 +410,7 @@ static void cs_amp_lib_test_get_efi_cal_uid_not_found_noindex_test(struct kunit
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, -1,
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, -1,
&result_data);
KUNIT_EXPECT_EQ(test, ret, -ENOENT);
@@ -436,7 +441,7 @@ static void cs_amp_lib_test_get_efi_cal_uid_not_found_index_not_found_test(struc
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, bad_target_uid, 99,
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, bad_target_uid, 99,
&result_data);
KUNIT_EXPECT_EQ(test, ret, -ENOENT);
@@ -460,7 +465,7 @@ static void cs_amp_lib_test_get_efi_cal_no_uid_index_not_found_test(struct kunit
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, 99, &result_data);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, 99, &result_data);
KUNIT_EXPECT_EQ(test, ret, -ENOENT);
kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
@@ -480,7 +485,7 @@ static void cs_amp_lib_test_get_efi_cal_no_uid_no_index_test(struct kunit *test)
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
KUNIT_EXPECT_EQ(test, ret, -ENOENT);
kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
@@ -509,7 +514,7 @@ static void cs_amp_lib_test_get_efi_cal_zero_not_matched_test(struct kunit *test
cs_amp_test_hooks->get_efi_variable,
cs_amp_lib_test_get_efi_variable);
- ret = cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev, 0, -1, &result_data);
+ ret = cs_amp_get_efi_calibration_data(&priv->amp_dev->dev, 0, -1, &result_data);
KUNIT_EXPECT_EQ(test, ret, -ENOENT);
kunit_deactivate_static_stub(test, cs_amp_test_hooks->get_efi_variable);
@@ -543,14 +548,14 @@ static void cs_amp_lib_test_get_efi_cal_empty_entry_test(struct kunit *test)
/* Lookup by UID should not find it */
KUNIT_EXPECT_EQ(test,
- cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
uid, -1,
&result_data),
-ENOENT);
/* Get by index should ignore it */
KUNIT_EXPECT_EQ(test,
- cs_amp_get_efi_calibration_data(&priv->amp_pdev.dev,
+ cs_amp_get_efi_calibration_data(&priv->amp_dev->dev,
0, 2,
&result_data),
-ENOENT);
@@ -600,7 +605,7 @@ static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
dsp = kunit_kzalloc(test, sizeof(*dsp), GFP_KERNEL);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, dsp);
- dsp->dev = &priv->amp_pdev.dev;
+ dsp->dev = &priv->amp_dev->dev;
get_random_bytes(&data, sizeof(data));
@@ -637,14 +642,9 @@ static void cs_amp_lib_test_write_cal_data_test(struct kunit *test)
KUNIT_EXPECT_EQ(test, entry->value, data.calStatus);
}
-static void cs_amp_lib_test_dev_release(struct device *dev)
-{
-}
-
static int cs_amp_lib_test_case_init(struct kunit *test)
{
struct cs_amp_lib_test_priv *priv;
- int ret;
KUNIT_ASSERT_NOT_NULL(test, cs_amp_test_hooks);
@@ -656,23 +656,16 @@ static int cs_amp_lib_test_case_init(struct kunit *test)
INIT_LIST_HEAD(&priv->ctl_write_list);
/* Create dummy amp driver dev */
- priv->amp_pdev.name = "cs_amp_lib_test_drv";
- priv->amp_pdev.id = -1;
- priv->amp_pdev.dev.release = cs_amp_lib_test_dev_release;
- ret = platform_device_register(&priv->amp_pdev);
- KUNIT_ASSERT_GE_MSG(test, ret, 0, "Failed to register amp platform device\n");
+ priv->amp_dev = faux_device_create("cs_amp_lib_test_drv", NULL, NULL);
+ KUNIT_ASSERT_NOT_NULL(test, priv->amp_dev);
+ KUNIT_ASSERT_EQ(test, 0,
+ kunit_add_action_or_reset(test,
+ faux_device_destroy_wrapper,
+ priv->amp_dev));
return 0;
}
-static void cs_amp_lib_test_case_exit(struct kunit *test)
-{
- struct cs_amp_lib_test_priv *priv = test->priv;
-
- if (priv->amp_pdev.name)
- platform_device_unregister(&priv->amp_pdev);
-}
-
static const struct cs_amp_lib_test_param cs_amp_lib_test_get_cal_param_cases[] = {
{ .num_amps = 2, .amp_index = 0 },
{ .num_amps = 2, .amp_index = 1 },
@@ -750,7 +743,6 @@ static struct kunit_case cs_amp_lib_test_cases[] = {
static struct kunit_suite cs_amp_lib_test_suite = {
.name = "snd-soc-cs-amp-lib-test",
.init = cs_amp_lib_test_case_init,
- .exit = cs_amp_lib_test_case_exit,
.test_cases = cs_amp_lib_test_cases,
};
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index c677868c5d5f..808e67c90f7c 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -11,6 +11,7 @@
#include <linux/efi.h>
#include <linux/firmware/cirrus/cs_dsp.h>
#include <linux/module.h>
+#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <sound/cs-amp-lib.h>
@@ -147,7 +148,7 @@ static struct cirrus_amp_efi_data *cs_amp_get_cal_efi_buffer(struct device *dev)
dev_dbg(dev, "Calibration: Size=%d, Amp Count=%d\n", efi_data->size, efi_data->count);
if ((efi_data->count > 128) ||
- offsetof(struct cirrus_amp_efi_data, data[efi_data->count]) > data_size) {
+ struct_size(efi_data, data, efi_data->count) > data_size) {
dev_err(dev, "EFI cal variable truncated\n");
ret = -EOVERFLOW;
goto err;
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index b49c6905e872..b60697ff7a50 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -129,7 +129,7 @@ static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = {
{27000000, 0x3F, 0x0A},
};
-static struct reg_default cs35l36_reg[] = {
+static const struct reg_default cs35l36_reg[] = {
{CS35L36_TESTKEY_CTRL, 0x00000000},
{CS35L36_USERKEY_CTL, 0x00000000},
{CS35L36_OTP_CTRL1, 0x00002460},
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index ff4134bee858..224d65987a8d 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -483,7 +483,6 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
}
done:
- pm_runtime_mark_last_busy(cs35l41->dev);
pm_runtime_put_autosuspend(cs35l41->dev);
return ret;
@@ -1328,7 +1327,6 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
pm_runtime_use_autosuspend(cs35l41->dev);
- pm_runtime_mark_last_busy(cs35l41->dev);
pm_runtime_set_active(cs35l41->dev);
pm_runtime_get_noresume(cs35l41->dev);
pm_runtime_enable(cs35l41->dev);
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index 432a19f4de2b..d4dcdf37bb70 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -1427,7 +1427,6 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
pm_runtime_use_autosuspend(cs35l45->dev);
- pm_runtime_mark_last_busy(cs35l45->dev);
pm_runtime_set_active(cs35l45->dev);
pm_runtime_get_noresume(cs35l45->dev);
pm_runtime_enable(cs35l45->dev);
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
index 8a518df1e16e..073f1796ae29 100644
--- a/sound/soc/codecs/cs35l56-i2c.c
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -17,9 +17,10 @@
static int cs35l56_i2c_probe(struct i2c_client *client)
{
+ unsigned int id = (u32)(uintptr_t)i2c_get_match_data(client);
struct cs35l56_private *cs35l56;
struct device *dev = &client->dev;
- const struct regmap_config *regmap_config = &cs35l56_regmap_i2c;
+ const struct regmap_config *regmap_config;
int ret;
cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
@@ -30,6 +31,20 @@ static int cs35l56_i2c_probe(struct i2c_client *client)
cs35l56->base.can_hibernate = true;
i2c_set_clientdata(client, cs35l56);
+
+ switch (id) {
+ case 0x3556:
+ regmap_config = &cs35l56_regmap_i2c;
+ cs35l56->base.fw_reg = &cs35l56_fw_reg;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_i2c;
+ cs35l56->base.fw_reg = &cs35l63_fw_reg;
+ break;
+ default:
+ return -ENODEV;
+ }
+
cs35l56->base.regmap = devm_regmap_init_i2c(client, regmap_config);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);
@@ -57,14 +72,16 @@ static void cs35l56_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id cs35l56_id_i2c[] = {
- { "cs35l56" },
+ { "cs35l56", 0x3556 },
+ { "cs35l63", 0x3563 },
{}
};
MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
#ifdef CONFIG_ACPI
static const struct acpi_device_id cs35l56_asoc_acpi_match[] = {
- { "CSC355C", 0 },
+ { "CSC355C", 0x3556 },
+ { "CSC356C", 0x3563 },
{},
};
MODULE_DEVICE_TABLE(acpi, cs35l56_asoc_acpi_match);
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
index 3f91cb3f9ae7..ee14031695a1 100644
--- a/sound/soc/codecs/cs35l56-sdw.c
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -238,16 +238,15 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = {
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
-static int cs35l56_sdw_set_cal_index(struct cs35l56_private *cs35l56)
+static int cs35l56_sdw_get_unique_id(struct cs35l56_private *cs35l56)
{
int ret;
- /* SoundWire UniqueId is used to index the calibration array */
ret = sdw_read_no_pm(cs35l56->sdw_peripheral, SDW_SCP_DEVID_0);
if (ret < 0)
return ret;
- cs35l56->base.cal_index = ret & 0xf;
+ cs35l56->sdw_unique_id = ret & 0xf;
return 0;
}
@@ -259,11 +258,13 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral)
pm_runtime_get_noresume(cs35l56->base.dev);
- if (cs35l56->base.cal_index < 0) {
- ret = cs35l56_sdw_set_cal_index(cs35l56);
- if (ret < 0)
- goto out;
- }
+ ret = cs35l56_sdw_get_unique_id(cs35l56);
+ if (ret)
+ goto out;
+
+ /* SoundWire UniqueId is used to index the calibration array */
+ if (cs35l56->base.cal_index < 0)
+ cs35l56->base.cal_index = cs35l56->sdw_unique_id;
ret = cs35l56_init(cs35l56);
if (ret < 0) {
@@ -282,7 +283,6 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral)
}
out:
- pm_runtime_mark_last_busy(cs35l56->base.dev);
pm_runtime_put_autosuspend(cs35l56->base.dev);
}
@@ -393,6 +393,74 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
return 0;
}
+static int cs35l63_sdw_kick_divider(struct cs35l56_private *cs35l56,
+ struct sdw_slave *peripheral)
+{
+ unsigned int curr_scale_reg, next_scale_reg;
+ int curr_scale, next_scale, ret;
+
+ if (!cs35l56->base.init_done)
+ return 0;
+
+ if (peripheral->bus->params.curr_bank) {
+ curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+ next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+ } else {
+ curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+ next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+ }
+
+ /*
+ * Current clock scale value must be different to new value.
+ * Modify current to guarantee this. If next still has the dummy
+ * value we wrote when it was current, the core code has not set
+ * a new scale so restore its original good value
+ */
+ curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg);
+ if (curr_scale < 0) {
+ dev_err(cs35l56->base.dev, "Failed to read current clock scale: %d\n", curr_scale);
+ return curr_scale;
+ }
+
+ next_scale = sdw_read_no_pm(peripheral, next_scale_reg);
+ if (next_scale < 0) {
+ dev_err(cs35l56->base.dev, "Failed to read next clock scale: %d\n", next_scale);
+ return next_scale;
+ }
+
+ if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) {
+ next_scale = cs35l56->old_sdw_clock_scale;
+ ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale);
+ if (ret < 0) {
+ dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ cs35l56->old_sdw_clock_scale = curr_scale;
+ ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE);
+ if (ret < 0) {
+ dev_err(cs35l56->base.dev, "Failed to modify current clock scale: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l56->base.dev, "Next bus scale: %#x\n", next_scale);
+
+ return 0;
+}
+
+static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
+ struct sdw_bus_params *params)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ if ((cs35l56->base.type == 0x63) && (cs35l56->base.rev < 0xa1))
+ return cs35l63_sdw_kick_divider(cs35l56, peripheral);
+
+ return 0;
+}
+
static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
enum sdw_clk_stop_mode mode,
enum sdw_clk_stop_type type)
@@ -408,6 +476,7 @@ static const struct sdw_slave_ops cs35l56_sdw_ops = {
.read_prop = cs35l56_sdw_read_prop,
.interrupt_callback = cs35l56_sdw_interrupt,
.update_status = cs35l56_sdw_update_status,
+ .bus_config = cs35l56_sdw_bus_config,
#ifdef DEBUG
.clk_stop = cs35l56_sdw_clk_stop,
#endif
@@ -509,6 +578,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi
{
struct device *dev = &peripheral->dev;
struct cs35l56_private *cs35l56;
+ const struct regmap_config *regmap_config;
int ret;
cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
@@ -517,12 +587,27 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi
cs35l56->base.dev = dev;
cs35l56->sdw_peripheral = peripheral;
+ cs35l56->sdw_link_num = peripheral->bus->link_id;
INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
dev_set_drvdata(dev, cs35l56);
+ switch ((unsigned int)id->driver_data) {
+ case 0x3556:
+ case 0x3557:
+ regmap_config = &cs35l56_regmap_sdw;
+ cs35l56->base.fw_reg = &cs35l56_fw_reg;
+ break;
+ case 0x3563:
+ regmap_config = &cs35l63_regmap_sdw;
+ cs35l56->base.fw_reg = &cs35l63_fw_reg;
+ break;
+ default:
+ return -ENODEV;
+ }
+
cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
- peripheral, &cs35l56_regmap_sdw);
+ peripheral, regmap_config);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);
return dev_err_probe(dev, ret, "Failed to allocate register map\n");
@@ -562,8 +647,9 @@ static const struct dev_pm_ops cs35l56_sdw_pm = {
};
static const struct sdw_device_id cs35l56_sdw_id[] = {
- SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0),
- SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0x3556),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3557, 0x3557),
+ SDW_SLAVE_ENTRY(0x01FA, 0x3563, 0x3563),
{},
};
MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index e28bfefa72f3..ba653f6ccfae 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -38,17 +38,48 @@ static const struct reg_sequence cs35l56_patch[] = {
{ CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
{ CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
{ CS35L56_IRQ1_MASK_18, 0x1f7df0ff },
+};
+static const struct reg_sequence cs35l56_patch_fw[] = {
/* These are not reset by a soft-reset, so patch to defaults. */
{ CS35L56_MAIN_RENDER_USER_MUTE, 0x00000000 },
{ CS35L56_MAIN_RENDER_USER_VOLUME, 0x00000000 },
{ CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
};
+static const struct reg_sequence cs35l63_patch_fw[] = {
+ /* These are not reset by a soft-reset, so patch to defaults. */
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
int cs35l56_set_patch(struct cs35l56_base *cs35l56_base)
{
- return regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
+ int ret;
+
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch,
ARRAY_SIZE(cs35l56_patch));
+ if (ret)
+ return ret;
+
+
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l56_patch_fw,
+ ARRAY_SIZE(cs35l56_patch_fw));
+ break;
+ case 0x63:
+ ret = regmap_register_patch(cs35l56_base->regmap, cs35l63_patch_fw,
+ ARRAY_SIZE(cs35l63_patch_fw));
+ break;
+ default:
+ break;
+ }
+
+ return ret;
}
EXPORT_SYMBOL_NS_GPL(cs35l56_set_patch, "SND_SOC_CS35L56_SHARED");
@@ -82,6 +113,36 @@ static const struct reg_default cs35l56_reg_defaults[] = {
{ CS35L56_MAIN_POSTURE_NUMBER, 0x00000000 },
};
+static const struct reg_default cs35l63_reg_defaults[] = {
+ /* no defaults for OTP_MEM - first read populates cache */
+
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1_CONTROL1, 0x00000028 },
+ { CS35L56_ASP1_CONTROL2, 0x18180200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000002 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000000 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000000 },
+ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
+ { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
+ { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
+ { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
+ { CS35L56_IRQ1_MASK_1, 0x8003ffff },
+ { CS35L56_IRQ1_MASK_2, 0xffff7fff },
+ { CS35L56_IRQ1_MASK_4, 0xe0ffffff },
+ { CS35L56_IRQ1_MASK_8, 0x8c000fff },
+ { CS35L56_IRQ1_MASK_18, 0x0760f000 },
+ { CS35L56_IRQ1_MASK_20, 0x15c00000 },
+ { CS35L63_MAIN_RENDER_USER_MUTE, 0x00000000 },
+ { CS35L63_MAIN_RENDER_USER_VOLUME, 0x00000000 },
+ { CS35L63_MAIN_POSTURE_NUMBER, 0x00000000 },
+};
+
static bool cs35l56_is_dsp_memory(unsigned int reg)
{
switch (reg) {
@@ -153,6 +214,8 @@ static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
case CS35L56_DSP_VIRTUAL1_MBOX_6:
case CS35L56_DSP_VIRTUAL1_MBOX_7:
case CS35L56_DSP_VIRTUAL1_MBOX_8:
+ case CS35L56_DIE_STS1:
+ case CS35L56_DIE_STS2:
case CS35L56_DSP_RESTRICT_STS1:
case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
@@ -179,7 +242,7 @@ static bool cs35l56_precious_reg(struct device *dev, unsigned int reg)
}
}
-static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+static bool cs35l56_common_volatile_reg(unsigned int reg)
{
switch (reg) {
case CS35L56_DEVID:
@@ -217,12 +280,32 @@ static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
case CS35L56_DSP1_SCRATCH3:
case CS35L56_DSP1_SCRATCH4:
return true;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
case CS35L56_MAIN_RENDER_USER_MUTE:
case CS35L56_MAIN_RENDER_USER_VOLUME:
case CS35L56_MAIN_POSTURE_NUMBER:
return false;
default:
- return cs35l56_is_dsp_memory(reg);
+ return cs35l56_common_volatile_reg(reg);
+ }
+}
+
+static bool cs35l63_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L63_MAIN_RENDER_USER_MUTE:
+ case CS35L63_MAIN_RENDER_USER_VOLUME:
+ case CS35L63_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_common_volatile_reg(reg);
}
}
@@ -253,7 +336,8 @@ int cs35l56_firmware_shutdown(struct cs35l56_base *cs35l56_base)
if (ret)
return ret;
- ret = regmap_read_poll_timeout(cs35l56_base->regmap, CS35L56_DSP1_PM_CUR_STATE,
+ ret = regmap_read_poll_timeout(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->pm_cur_stat,
val, (val == CS35L56_HALO_STATE_SHUTDOWN),
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US);
@@ -278,7 +362,9 @@ int cs35l56_wait_for_firmware_boot(struct cs35l56_base *cs35l56_base)
CS35L56_HALO_STATE_POLL_US,
CS35L56_HALO_STATE_TIMEOUT_US,
false,
- cs35l56_base->regmap, CS35L56_DSP1_HALO_STATE, &val);
+ cs35l56_base->regmap,
+ cs35l56_base->fw_reg->halo_state,
+ &val);
if (poll_ret) {
dev_err(cs35l56_base->dev, "Firmware boot timed out(%d): HALO_STATE=%#x\n",
@@ -382,6 +468,11 @@ static const struct reg_sequence cs35l56_system_reset_seq[] = {
REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
};
+static const struct reg_sequence cs35l63_system_reset_seq[] = {
+ REG_SEQ0(CS35L63_DSP1_HALO_STATE, 0),
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
{
/*
@@ -395,9 +486,22 @@ void cs35l56_system_reset(struct cs35l56_base *cs35l56_base, bool is_soundwire)
return;
}
- regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
- cs35l56_system_reset_seq,
- ARRAY_SIZE(cs35l56_system_reset_seq));
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l56_system_reset_seq,
+ ARRAY_SIZE(cs35l56_system_reset_seq));
+ break;
+ case 0x63:
+ regmap_multi_reg_write_bypassed(cs35l56_base->regmap,
+ cs35l63_system_reset_seq,
+ ARRAY_SIZE(cs35l63_system_reset_seq));
+ break;
+ default:
+ break;
+ }
/* On SoundWire the registers won't be accessible until it re-enumerates. */
if (is_soundwire)
@@ -514,7 +618,9 @@ int cs35l56_is_fw_reload_needed(struct cs35l56_base *cs35l56_base)
return ret;
}
- ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &val);
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->prot_sts,
+ &val);
if (ret)
dev_err(cs35l56_base->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
else
@@ -562,7 +668,7 @@ int cs35l56_runtime_suspend_common(struct cs35l56_base *cs35l56_base)
/* Firmware must have entered a power-save state */
ret = regmap_read_poll_timeout(cs35l56_base->regmap,
- CS35L56_TRANSDUCER_ACTUAL_PS,
+ cs35l56_base->fw_reg->transducer_actual_ps,
val, (val >= CS35L56_PS3),
CS35L56_PS3_POLL_US,
CS35L56_PS3_TIMEOUT_US);
@@ -698,13 +804,29 @@ static int cs35l56_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
unique_id |= (u32)pte.x | ((u32)pte.y << 8) | ((u32)pte.wafer_id << 16) |
((u32)pte.dvs << 24);
- dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", unique_id);
-
*uid = unique_id;
return 0;
}
+static int cs35l63_read_silicon_uid(struct cs35l56_base *cs35l56_base, u64 *uid)
+{
+ u32 tmp[2];
+ int ret;
+
+ ret = regmap_bulk_read(cs35l56_base->regmap, CS35L56_DIE_STS1, tmp, ARRAY_SIZE(tmp));
+ if (ret) {
+ dev_err(cs35l56_base->dev, "Cannot obtain CS35L56_DIE_STS: %d\n", ret);
+ return ret;
+ }
+
+ *uid = tmp[1];
+ *uid <<= 32;
+ *uid |= tmp[0];
+
+ return 0;
+}
+
/* Firmware calibration controls */
const struct cirrus_amp_cal_controls cs35l56_calibration_controls = {
.alg_id = 0x9f210,
@@ -725,10 +847,25 @@ int cs35l56_get_calibration(struct cs35l56_base *cs35l56_base)
if (cs35l56_base->secured)
return 0;
- ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid);
+ switch (cs35l56_base->type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = cs35l56_read_silicon_uid(cs35l56_base, &silicon_uid);
+ break;
+ case 0x63:
+ ret = cs35l63_read_silicon_uid(cs35l56_base, &silicon_uid);
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
if (ret < 0)
return ret;
+ dev_dbg(cs35l56_base->dev, "UniqueID = %#llx\n", silicon_uid);
+
ret = cs_amp_get_efi_calibration_data(cs35l56_base->dev, silicon_uid,
cs35l56_base->cal_index,
&cs35l56_base->cal_data);
@@ -752,7 +889,8 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
unsigned int prot_status;
int ret;
- ret = regmap_read(cs35l56_base->regmap, CS35L56_PROTECTION_STATUS, &prot_status);
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->prot_sts, &prot_status);
if (ret) {
dev_err(cs35l56_base->dev, "Get PROTECTION_STATUS failed: %d\n", ret);
return ret;
@@ -760,7 +898,8 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
*fw_missing = !!(prot_status & CS35L56_FIRMWARE_MISSING);
- ret = regmap_read(cs35l56_base->regmap, CS35L56_DSP1_FW_VER, fw_version);
+ ret = regmap_read(cs35l56_base->regmap,
+ cs35l56_base->fw_reg->fw_ver, fw_version);
if (ret) {
dev_err(cs35l56_base->dev, "Get FW VER failed: %d\n", ret);
return ret;
@@ -770,6 +909,33 @@ int cs35l56_read_prot_status(struct cs35l56_base *cs35l56_base,
}
EXPORT_SYMBOL_NS_GPL(cs35l56_read_prot_status, "SND_SOC_CS35L56_SHARED");
+void cs35l56_log_tuning(struct cs35l56_base *cs35l56_base, struct cs_dsp *cs_dsp)
+{
+ __be32 pid, sid, tid;
+ int ret;
+
+ scoped_guard(mutex, &cs_dsp->pwr_lock) {
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_PRJCT_ID",
+ WMFW_ADSP2_XM, 0x9f212),
+ 0, &pid, sizeof(pid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_CHNNL_ID",
+ WMFW_ADSP2_XM, 0x9f212),
+ 0, &sid, sizeof(sid));
+ if (!ret)
+ ret = cs_dsp_coeff_read_ctrl(cs_dsp_get_ctl(cs_dsp, "AS_SNPSHT_ID",
+ WMFW_ADSP2_XM, 0x9f212),
+ 0, &tid, sizeof(tid));
+ }
+
+ if (ret)
+ dev_warn(cs35l56_base->dev, "Can't read tuning IDs");
+ else
+ dev_info(cs35l56_base->dev, "Tuning PID: %#x, SID: %#x, TID: %#x\n",
+ be32_to_cpu(pid), be32_to_cpu(sid), be32_to_cpu(tid));
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_log_tuning, "SND_SOC_CS35L56_SHARED");
+
int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
{
int ret;
@@ -809,9 +975,12 @@ int cs35l56_hw_init(struct cs35l56_base *cs35l56_base)
case 0x35A56:
case 0x35A57:
break;
+ case 0x35A630:
+ devid = devid >> 4;
+ break;
default:
dev_err(cs35l56_base->dev, "Unknown device %x\n", devid);
- return ret;
+ return -ENODEV;
}
cs35l56_base->type = devid & 0xFF;
@@ -1045,8 +1214,66 @@ const struct regmap_config cs35l56_regmap_sdw = {
};
EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, "SND_SOC_CS35L56_SHARED");
+const struct regmap_config cs35l63_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_base = 0x8000,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_i2c, "SND_SOC_CS35L56_SHARED");
+
+const struct regmap_config cs35l63_regmap_sdw = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l63_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l63_reg_defaults),
+ .volatile_reg = cs35l63_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_regmap_sdw, "SND_SOC_CS35L56_SHARED");
+
+const struct cs35l56_fw_reg cs35l56_fw_reg = {
+ .fw_ver = CS35L56_DSP1_FW_VER,
+ .halo_state = CS35L56_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L56_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L56_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L56_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L56_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L56_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L56_MAIN_POSTURE_NUMBER,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_fw_reg, "SND_SOC_CS35L56_SHARED");
+
+const struct cs35l56_fw_reg cs35l63_fw_reg = {
+ .fw_ver = CS35L63_DSP1_FW_VER,
+ .halo_state = CS35L63_DSP1_HALO_STATE,
+ .pm_cur_stat = CS35L63_DSP1_PM_CUR_STATE,
+ .prot_sts = CS35L63_PROTECTION_STATUS,
+ .transducer_actual_ps = CS35L63_TRANSDUCER_ACTUAL_PS,
+ .user_mute = CS35L63_MAIN_RENDER_USER_MUTE,
+ .user_volume = CS35L63_MAIN_RENDER_USER_VOLUME,
+ .posture_number = CS35L63_MAIN_POSTURE_NUMBER,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l63_fw_reg, "SND_SOC_CS35L56_SHARED");
+
MODULE_DESCRIPTION("ASoC CS35L56 Shared");
MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
MODULE_LICENSE("GPL");
MODULE_IMPORT_NS("SND_SOC_CS_AMP_LIB");
+MODULE_IMPORT_NS("FW_CS_DSP");
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
index ca6c03a8766d..c2ddee22cd23 100644
--- a/sound/soc/codecs/cs35l56-spi.c
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -25,6 +25,9 @@ static int cs35l56_spi_probe(struct spi_device *spi)
return -ENOMEM;
spi_set_drvdata(spi, cs35l56);
+
+ cs35l56->base.fw_reg = &cs35l56_fw_reg;
+
cs35l56->base.regmap = devm_regmap_init_spi(spi, regmap_config);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index b3158a84b87a..b1c65d8331e7 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -84,6 +84,25 @@ static const struct snd_kcontrol_new cs35l56_controls[] = {
cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
};
+static const struct snd_kcontrol_new cs35l63_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch",
+ CS35L63_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+ CS35L63_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SHIFT,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MIN,
+ CS35L56_MAIN_RENDER_USER_VOLUME_MAX,
+ CS35L56_MAIN_RENDER_USER_VOLUME_SIGNBIT,
+ 0,
+ cs35l56_dspwait_get_volsw,
+ cs35l56_dspwait_put_volsw,
+ vol_tlv),
+ SOC_SINGLE_EXT("Posture Number", CS35L63_MAIN_POSTURE_NUMBER,
+ 0, 255, 0,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+};
+
static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
CS35L56_ASP1TX1_INPUT,
0, CS35L56_ASP_TXn_SRC_MASK,
@@ -174,7 +193,7 @@ static int cs35l56_play_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
/* Wait for firmware to enter PS0 power state */
ret = regmap_read_poll_timeout(cs35l56->base.regmap,
- CS35L56_TRANSDUCER_ACTUAL_PS,
+ cs35l56->base.fw_reg->transducer_actual_ps,
val, (val == CS35L56_PS0),
CS35L56_PS0_POLL_US,
CS35L56_PS0_TIMEOUT_US);
@@ -687,17 +706,41 @@ static int cs35l56_write_cal(struct cs35l56_private *cs35l56)
return ret;
}
-static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
+static int cs35l56_dsp_download_and_power_up(struct cs35l56_private *cs35l56,
+ bool load_firmware)
{
int ret;
- /* Use wm_adsp to load and apply the firmware patch and coefficient files */
- ret = wm_adsp_power_up(&cs35l56->dsp, true);
+ /*
+ * Abort the first load if it didn't find the suffixed bins and
+ * we have an alternate fallback suffix.
+ */
+ cs35l56->dsp.bin_mandatory = (load_firmware && cs35l56->fallback_fw_suffix);
+
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ if ((ret == -ENOENT) && cs35l56->dsp.bin_mandatory) {
+ cs35l56->dsp.fwf_suffix = cs35l56->fallback_fw_suffix;
+ cs35l56->fallback_fw_suffix = NULL;
+ cs35l56->dsp.bin_mandatory = false;
+ ret = wm_adsp_power_up(&cs35l56->dsp, load_firmware);
+ }
+
if (ret) {
- dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
- return;
+ dev_dbg(cs35l56->base.dev, "wm_adsp_power_up ret %d\n", ret);
+ return ret;
}
+ return 0;
+}
+
+static void cs35l56_reinit_patch(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ ret = cs35l56_dsp_download_and_power_up(cs35l56, true);
+ if (ret)
+ return;
+
cs35l56_write_cal(cs35l56);
/* Always REINIT after applying patch or coefficients */
@@ -731,11 +774,9 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing
* but only if firmware is missing. If firmware is already patched just
* power-up wm_adsp without downloading firmware.
*/
- ret = wm_adsp_power_up(&cs35l56->dsp, !!firmware_missing);
- if (ret) {
- dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
+ ret = cs35l56_dsp_download_and_power_up(cs35l56, firmware_missing);
+ if (ret)
goto err;
- }
mutex_lock(&cs35l56->base.irq_lock);
@@ -760,7 +801,8 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing
goto err_unlock;
}
- regmap_clear_bits(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS,
+ regmap_clear_bits(cs35l56->base.regmap,
+ cs35l56->base.fw_reg->prot_sts,
CS35L56_FIRMWARE_MISSING);
cs35l56->base.fw_patched = true;
@@ -827,16 +869,45 @@ static void cs35l56_dsp_work(struct work_struct *work)
else
cs35l56_patch(cs35l56, firmware_missing);
+ cs35l56_log_tuning(&cs35l56->base, &cs35l56->dsp.cs_dsp);
err:
- pm_runtime_mark_last_busy(cs35l56->base.dev);
pm_runtime_put_autosuspend(cs35l56->base.dev);
}
+static int cs35l56_set_fw_suffix(struct cs35l56_private *cs35l56)
+{
+ if (cs35l56->dsp.fwf_suffix)
+ return 0;
+
+ if (!cs35l56->sdw_peripheral)
+ return 0;
+
+ cs35l56->dsp.fwf_suffix = devm_kasprintf(cs35l56->base.dev, GFP_KERNEL,
+ "l%uu%u",
+ cs35l56->sdw_link_num,
+ cs35l56->sdw_unique_id);
+ if (!cs35l56->dsp.fwf_suffix)
+ return -ENOMEM;
+
+ /*
+ * There are published firmware files for L56 B0 silicon using
+ * the ALSA prefix as the filename suffix. Default to trying these
+ * first, with the new name as an alternate.
+ */
+ if ((cs35l56->base.type == 0x56) && (cs35l56->base.rev == 0xb0)) {
+ cs35l56->fallback_fw_suffix = cs35l56->dsp.fwf_suffix;
+ cs35l56->dsp.fwf_suffix = cs35l56->component->name_prefix;
+ }
+
+ return 0;
+}
+
static int cs35l56_component_probe(struct snd_soc_component *component)
{
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
struct dentry *debugfs_root = component->debugfs_root;
unsigned short vendor, device;
+ int ret;
BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
@@ -870,12 +941,36 @@ static int cs35l56_component_probe(struct snd_soc_component *component)
return -ENOMEM;
cs35l56->component = component;
+ ret = cs35l56_set_fw_suffix(cs35l56);
+ if (ret)
+ return ret;
+
wm_adsp2_component_probe(&cs35l56->dsp, component);
debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->base.init_done);
debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->base.can_hibernate);
debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->base.fw_patched);
+
+ switch (cs35l56->base.type) {
+ case 0x54:
+ case 0x56:
+ case 0x57:
+ ret = snd_soc_add_component_controls(component, cs35l56_controls,
+ ARRAY_SIZE(cs35l56_controls));
+ break;
+ case 0x63:
+ ret = snd_soc_add_component_controls(component, cs35l63_controls,
+ ARRAY_SIZE(cs35l63_controls));
+ break;
+ default:
+ ret = -ENODEV;
+ break;
+ }
+
+ if (ret)
+ return dev_err_probe(cs35l56->base.dev, ret, "unable to add controls\n");
+
queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
return 0;
@@ -931,8 +1026,6 @@ static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
.num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets),
.dapm_routes = cs35l56_audio_map,
.num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map),
- .controls = cs35l56_controls,
- .num_controls = ARRAY_SIZE(cs35l56_controls),
.set_bias_level = cs35l56_set_bias_level,
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index 8a987ec01507..bd77a57249d7 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -38,6 +38,7 @@ struct cs35l56_private {
struct snd_soc_component *component;
struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
struct sdw_slave *sdw_peripheral;
+ const char *fallback_fw_suffix;
struct work_struct sdw_irq_work;
bool sdw_irq_no_unmask;
bool soft_resetting;
@@ -51,6 +52,9 @@ struct cs35l56_private {
u8 asp_slot_count;
bool tdm_mode;
bool sysclk_set;
+ u8 old_sdw_clock_scale;
+ u8 sdw_link_num;
+ u8 sdw_unique_id;
};
extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index 56668c392063..78bb093fa0cc 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -1775,7 +1775,6 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
}
mutex_unlock(&cs42l42->irq_lock);
- pm_runtime_mark_last_busy(cs42l42->dev);
pm_runtime_put_autosuspend(cs42l42->dev);
return IRQ_HANDLED;
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
index 6165ac16c3a9..f5c5150c25e5 100644
--- a/sound/soc/codecs/cs42l43-jack.c
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -242,7 +242,6 @@ done:
error:
mutex_unlock(&priv->jack_lock);
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return ret;
@@ -423,7 +422,6 @@ void cs42l43_button_press_work(struct work_struct *work)
error:
mutex_unlock(&priv->jack_lock);
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
}
@@ -462,7 +460,6 @@ void cs42l43_button_release_work(struct work_struct *work)
mutex_unlock(&priv->jack_lock);
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
}
@@ -504,7 +501,6 @@ void cs42l43_bias_sense_timeout(struct work_struct *work)
mutex_unlock(&priv->jack_lock);
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
}
@@ -776,7 +772,6 @@ error:
priv->suspend_jack_debounce = false;
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
}
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
index ea84ac64c775..d84ad8d43438 100644
--- a/sound/soc/codecs/cs42l43.c
+++ b/sound/soc/codecs/cs42l43.c
@@ -1088,7 +1088,6 @@ static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift)
ret ? "open" : "closed");
error:
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return ret;
@@ -2370,7 +2369,6 @@ static int cs42l43_codec_probe(struct platform_device *pdev)
goto err_clk;
}
- pm_runtime_mark_last_busy(priv->dev);
pm_runtime_put_autosuspend(priv->dev);
return 0;
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index cd3f92c19045..a9ffba62aaf8 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -8,27 +8,26 @@
* Author: Brian Austin <brian.austin@cirrus.com>
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/of_gpio.h>
-#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/pm.h>
+#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/platform_device.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l52.h>
#include "cs42l52.h"
struct sp_config {
@@ -36,6 +35,24 @@ struct sp_config {
u32 srate;
};
+struct cs42l52_platform_data {
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* MICA mode selection Differential or Single-ended */
+ bool mica_diff_cfg;
+
+ /* MICB mode selection Differential or Single-ended */
+ bool micb_diff_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg73 */
+ unsigned int chgfreq;
+
+ /* Reset GPIO */
+ struct gpio_desc *reset_gpio;
+};
+
struct cs42l52_private {
struct regmap *regmap;
struct snd_soc_component *component;
@@ -1090,7 +1107,7 @@ static const struct regmap_config cs42l52_regmap = {
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);
+ struct cs42l52_platform_data *pdata;
int ret;
unsigned int devid;
unsigned int reg;
@@ -1107,50 +1124,43 @@ static int cs42l52_i2c_probe(struct i2c_client *i2c_client)
dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
return ret;
}
- if (pdata) {
- cs42l52->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- if (i2c_client->dev.of_node) {
- if (of_property_read_bool(i2c_client->dev.of_node,
- "cirrus,mica-differential-cfg"))
- pdata->mica_diff_cfg = true;
-
- if (of_property_read_bool(i2c_client->dev.of_node,
- "cirrus,micb-differential-cfg"))
- pdata->micb_diff_cfg = true;
-
- if (of_property_read_u32(i2c_client->dev.of_node,
- "cirrus,micbias-lvl", &val32) >= 0)
- pdata->micbias_lvl = val32;
-
- if (of_property_read_u32(i2c_client->dev.of_node,
- "cirrus,chgfreq-divisor", &val32) >= 0)
- pdata->chgfreq = val32;
-
- pdata->reset_gpio =
- of_get_named_gpio(i2c_client->dev.of_node,
- "cirrus,reset-gpio", 0);
- }
- cs42l52->pdata = *pdata;
+
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,mica-differential-cfg"))
+ pdata->mica_diff_cfg = true;
+
+ if (of_property_read_bool(i2c_client->dev.of_node,
+ "cirrus,micb-differential-cfg"))
+ pdata->micb_diff_cfg = true;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,micbias-lvl", &val32) >= 0)
+ pdata->micbias_lvl = val32;
+
+ if (of_property_read_u32(i2c_client->dev.of_node,
+ "cirrus,chgfreq-divisor", &val32) >= 0)
+ pdata->chgfreq = val32;
+
+ pdata->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "cirrus,reset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L52 /RST");
}
+ cs42l52->pdata = *pdata;
+
if (cs42l52->pdata.reset_gpio) {
- ret = devm_gpio_request_one(&i2c_client->dev,
- cs42l52->pdata.reset_gpio,
- GPIOF_OUT_INIT_HIGH,
- "CS42L52 /RST");
- if (ret < 0) {
- dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n",
- cs42l52->pdata.reset_gpio, ret);
- return ret;
- }
- gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
- gpio_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l52->pdata.reset_gpio, 0);
}
i2c_set_clientdata(i2c_client, cs42l52);
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index aaf90c8b7339..98fa812bc07b 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -7,32 +7,64 @@
* Author: Brian Austin <brian.austin@cirrus.com>
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
#include <linux/delay.h>
-#include <linux/pm.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
+#include <linux/init.h>
#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
-#include <linux/platform_device.h>
-#include <linux/regulator/consumer.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l56.h>
#include "cs42l56.h"
#define CS42L56_NUM_SUPPLIES 3
+
+struct cs42l56_platform_data {
+ /* GPIO for Reset */
+ struct gpio_desc *gpio_nreset;
+
+ /* MICBIAS Level. Check datasheet Pg48 */
+ unsigned int micbias_lvl;
+
+ /* Analog Input 1A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1a_ref_cfg;
+
+ /* Analog Input 2A Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2a_ref_cfg;
+
+ /* Analog Input 1B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain1b_ref_cfg;
+
+ /* Analog Input 2B Reference 0=Single 1=Pseudo-Differential */
+ unsigned int ain2b_ref_cfg;
+
+ /* Charge Pump Freq. Check datasheet Pg62 */
+ unsigned int chgfreq;
+
+ /* HighPass Filter Right Channel Corner Frequency */
+ unsigned int hpfb_freq;
+
+ /* HighPass Filter Left Channel Corner Frequency */
+ unsigned int hpfa_freq;
+
+ /* Adaptive Power Control for LO/HP */
+ unsigned int adaptive_pwr;
+};
+
static const char *const cs42l56_supply_names[CS42L56_NUM_SUPPLIES] = {
"VA",
"VCP",
@@ -1161,7 +1193,13 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
if (of_property_read_u32(np, "cirrus,hpf-left-freq", &val32) >= 0)
pdata->hpfb_freq = val32;
- pdata->gpio_nreset = of_get_named_gpio(np, "cirrus,gpio-nreset", 0);
+ pdata->gpio_nreset = devm_gpiod_get_optional(&i2c_client->dev, "cirrus,gpio-nreset",
+ GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->gpio_nreset))
+ return PTR_ERR(pdata->gpio_nreset);
+
+ gpiod_set_consumer_name(pdata->gpio_nreset, "CS42L56 /RST");
return 0;
}
@@ -1169,8 +1207,6 @@ static int cs42l56_handle_of_data(struct i2c_client *i2c_client,
static int cs42l56_i2c_probe(struct i2c_client *i2c_client)
{
struct cs42l56_private *cs42l56;
- struct cs42l56_platform_data *pdata =
- dev_get_platdata(&i2c_client->dev);
int ret, i;
unsigned int devid;
unsigned int alpha_rev, metal_rev;
@@ -1188,31 +1224,17 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client)
return ret;
}
- if (pdata) {
- cs42l56->pdata = *pdata;
- } else {
- if (i2c_client->dev.of_node) {
- ret = cs42l56_handle_of_data(i2c_client,
- &cs42l56->pdata);
- if (ret != 0)
- return ret;
- }
+ if (i2c_client->dev.of_node) {
+ ret = cs42l56_handle_of_data(i2c_client, &cs42l56->pdata);
+ if (ret != 0)
+ return ret;
}
if (cs42l56->pdata.gpio_nreset) {
- ret = gpio_request_one(cs42l56->pdata.gpio_nreset,
- GPIOF_OUT_INIT_HIGH, "CS42L56 /RST");
- if (ret < 0) {
- dev_err(&i2c_client->dev,
- "Failed to request /RST %d: %d\n",
- cs42l56->pdata.gpio_nreset, ret);
- return ret;
- }
- gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
- gpio_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 1);
+ gpiod_set_value_cansleep(cs42l56->pdata.gpio_nreset, 0);
}
-
i2c_set_clientdata(i2c_client, cs42l56);
for (i = 0; i < ARRAY_SIZE(cs42l56->supplies); i++)
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index ddf36001100e..535a867f9f2a 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -8,26 +8,33 @@
* Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>
*/
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/of_gpio.h>
#include <linux/pm.h>
-#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-#include <sound/cs42l73.h>
-#include "cs42l73.h"
#include "cirrus_legacy.h"
+#include "cs42l73.h"
+
+struct cs42l73_platform_data {
+ /* RST GPIO */
+ struct gpio_desc *reset_gpio;
+ unsigned int chgfreq;
+ int jack_detection;
+ unsigned int mclk_freq;
+};
struct sp_config {
u8 spc, mmcc, spfs;
@@ -1276,7 +1283,7 @@ static const struct regmap_config cs42l73_regmap = {
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);
+ struct cs42l73_platform_data *pdata;
int ret, devid;
unsigned int reg;
u32 val32;
@@ -1292,38 +1299,27 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client)
return ret;
}
- if (pdata) {
- cs42l73->pdata = *pdata;
- } else {
- pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
- GFP_KERNEL);
- if (!pdata)
- return -ENOMEM;
-
- if (i2c_client->dev.of_node) {
- if (of_property_read_u32(i2c_client->dev.of_node,
- "chgfreq", &val32) >= 0)
- pdata->chgfreq = val32;
- }
- pdata->reset_gpio = of_get_named_gpio(i2c_client->dev.of_node,
- "reset-gpio", 0);
- cs42l73->pdata = *pdata;
+ pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+
+ if (i2c_client->dev.of_node) {
+ if (of_property_read_u32(i2c_client->dev.of_node, "chgfreq", &val32) >= 0)
+ pdata->chgfreq = val32;
}
+ pdata->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev, "reset", GPIOD_OUT_LOW);
+
+ if (IS_ERR(pdata->reset_gpio))
+ return PTR_ERR(pdata->reset_gpio);
+
+ gpiod_set_consumer_name(pdata->reset_gpio, "CS42L73 /RST");
+ cs42l73->pdata = *pdata;
i2c_set_clientdata(i2c_client, cs42l73);
if (cs42l73->pdata.reset_gpio) {
- ret = devm_gpio_request_one(&i2c_client->dev,
- cs42l73->pdata.reset_gpio,
- GPIOF_OUT_INIT_HIGH,
- "CS42L73 /RST");
- if (ret < 0) {
- dev_err(&i2c_client->dev, "Failed to request /RST %d: %d\n",
- cs42l73->pdata.reset_gpio, ret);
- return ret;
- }
- gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
- gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
}
/* initialize codec */
@@ -1360,7 +1356,7 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client)
return 0;
err_reset:
- gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 0);
+ gpiod_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
return ret;
}
diff --git a/sound/soc/codecs/cs48l32-tables.c b/sound/soc/codecs/cs48l32-tables.c
new file mode 100644
index 000000000000..59eaa9a5029f
--- /dev/null
+++ b/sound/soc/codecs/cs48l32-tables.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Regmap tables and other data for Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/device.h>
+#include <linux/linear_range.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+
+#include "cs48l32.h"
+
+static const struct reg_sequence cs48l32_reva_patch[] = {
+ { 0x00001044, 0x0005000f },
+ { 0x00001c34, 0x000037e8 },
+ { 0x000046d8, 0x00000fe0 },
+};
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_register_patch(cs48l32->regmap, cs48l32_reva_patch,
+ ARRAY_SIZE(cs48l32_reva_patch));
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to apply patch\n");
+
+ return 0;
+}
+
+static const struct reg_default cs48l32_reg_default[] = {
+ { 0x00000c08, 0xe1000001 }, /* GPIO1_CTRL1 */
+ { 0x00000c0c, 0xe1000001 }, /* GPIO2_CTRL1 */
+ { 0x00000c10, 0xe1000001 }, /* GPIO3_CTRL1 */
+ { 0x00000c14, 0xe1000001 }, /* GPIO4_CTRL1 */
+ { 0x00000c18, 0xe1000001 }, /* GPIO5_CTRL1 */
+ { 0x00000c1c, 0xe1000001 }, /* GPIO6_CTRL1 */
+ { 0x00000c20, 0xe1000001 }, /* GPIO7_CTRL1 */
+ { 0x00000c24, 0xe1000001 }, /* GPIO8_CTRL1 */
+ { 0x00000c28, 0xe1000001 }, /* GPIO9_CTRL1 */
+ { 0x00000c2c, 0xe1000001 }, /* GPIO10_CTRL1 */
+ { 0x00000c30, 0xe1000001 }, /* GPIO11_CTRL1 */
+ { 0x00000c34, 0xe1000001 }, /* GPIO12_CTRL1 */
+ { 0x00000c38, 0xe1000001 }, /* GPIO13_CTRL1 */
+ { 0x00000c3c, 0xe1000001 }, /* GPIO14_CTRL1 */
+ { 0x00000c40, 0xe1000001 }, /* GPIO15_CTRL1 */
+ { 0x00000c44, 0xe1000001 }, /* GPIO16_CTRL1 */
+ { 0x00001020, 0x00000000 }, /* OUTPUT_SYS_CLK */
+ { 0x00001044, 0x0005000f }, /* AUXPDM_CTRL */
+ { 0x0000105c, 0x00000000 }, /* AUXPDM_CTRL2 */
+ { 0x00001400, 0x00000002 }, /* CLOCK32K */
+ { 0x00001404, 0x00000404 }, /* SYSTEM_CLOCK1 */
+ { 0x00001420, 0x00000003 }, /* SAMPLE_RATE1 */
+ { 0x00001424, 0x00000003 }, /* SAMPLE_RATE2 */
+ { 0x00001428, 0x00000003 }, /* SAMPLE_RATE3 */
+ { 0x0000142c, 0x00000003 }, /* SAMPLE_RATE4 */
+ { 0x00001c00, 0x00000002 }, /* FLL1_CONTROL1 */
+ { 0x00001c04, 0x88203004 }, /* FLL1_CONTROL2 */
+ { 0x00001c08, 0x00000000 }, /* FLL1_CONTROL3 */
+ { 0x00001c0c, 0x21f05001 }, /* FLL1_CONTROL4 */
+ { 0x00001ca0, 0x00000c04 }, /* FLL1_GPIO_CLOCK */
+ { 0x00002000, 0x00000006 }, /* CHARGE_PUMP1 */
+ { 0x00002408, 0x000003e4 }, /* LDO2_CTRL1 */
+ { 0x00002410, 0x000000e6 }, /* MICBIAS_CTRL1 */
+ { 0x00002418, 0x00000222 }, /* MICBIAS_CTRL5 */
+ { 0x00002710, 0x00004600 }, /* IRQ1_CTRL_AOD */
+ { 0x00004000, 0x00000000 }, /* INPUT_CONTROL */
+ { 0x00004008, 0x00000400 }, /* INPUT_RATE_CONTROL */
+ { 0x0000400c, 0x00000000 }, /* INPUT_CONTROL2 */
+ { 0x00004020, 0x00050020 }, /* INPUT1_CONTROL1 */
+ { 0x00004024, 0x00000000 }, /* IN1L_CONTROL1 */
+ { 0x00004028, 0x10800080 }, /* IN1L_CONTROL2 */
+ { 0x00004044, 0x00000000 }, /* IN1R_CONTROL1 */
+ { 0x00004048, 0x10800080 }, /* IN1R_CONTROL2 */
+ { 0x00004060, 0x00050020 }, /* INPUT2_CONTROL1 */
+ { 0x00004064, 0x00000000 }, /* IN2L_CONTROL1 */
+ { 0x00004068, 0x10800000 }, /* IN2L_CONTROL2 */
+ { 0x00004084, 0x00000000 }, /* IN2R_CONTROL1 */
+ { 0x00004088, 0x10800000 }, /* IN2R_CONTROL2 */
+ { 0x00004244, 0x00000002 }, /* INPUT_HPF_CONTROL */
+ { 0x00004248, 0x00000022 }, /* INPUT_VOL_CONTROL */
+ { 0x00004300, 0x00000000 }, /* AUXPDM_CONTROL1 */
+ { 0x00004304, 0x00000000 }, /* AUXPDM_CONTROL2 */
+ { 0x00004308, 0x00010008 }, /* AUXPDM1_CONTROL1 */
+ { 0x00004310, 0x00010008 }, /* AUXPDM2_CONTROL1 */
+ { 0x00004688, 0x00000000 }, /* ADC1L_ANA_CONTROL1 */
+ { 0x0000468c, 0x00000000 }, /* ADC1R_ANA_CONTROL1 */
+ { 0x00006000, 0x00000000 }, /* ASP1_ENABLES1 */
+ { 0x00006004, 0x00000028 }, /* ASP1_CONTROL1 */
+ { 0x00006008, 0x18180200 }, /* ASP1_CONTROL2 */
+ { 0x0000600c, 0x00000002 }, /* ASP1_CONTROL3 */
+ { 0x00006010, 0x03020100 }, /* ASP1_FRAME_CONTROL1 */
+ { 0x00006014, 0x07060504 }, /* ASP1_FRAME_CONTROL2 */
+ { 0x00006020, 0x03020100 }, /* ASP1_FRAME_CONTROL5 */
+ { 0x00006024, 0x07060504 }, /* ASP1_FRAME_CONTROL6 */
+ { 0x00006030, 0x00000020 }, /* ASP1_DATA_CONTROL1 */
+ { 0x00006040, 0x00000020 }, /* ASP1_DATA_CONTROL5 */
+ { 0x00006080, 0x00000000 }, /* ASP2_ENABLES1 */
+ { 0x00006084, 0x00000028 }, /* ASP2_CONTROL1 */
+ { 0x00006088, 0x18180200 }, /* ASP2_CONTROL2 */
+ { 0x0000608c, 0x00000002 }, /* ASP2_CONTROL3 */
+ { 0x00006090, 0x03020100 }, /* ASP2_FRAME_CONTROL1 */
+ { 0x000060a0, 0x03020100 }, /* ASP2_FRAME_CONTROL5 */
+ { 0x000060b0, 0x00000020 }, /* ASP2_DATA_CONTROL1 */
+ { 0x000060c0, 0x00000020 }, /* ASP2_DATA_CONTROL5 */
+ { 0x00008200, 0x00800000 }, /* ASP1TX1_INPUT1 */
+ { 0x00008204, 0x00800000 }, /* ASP1TX1_INPUT2 */
+ { 0x00008208, 0x00800000 }, /* ASP1TX1_INPUT3 */
+ { 0x0000820c, 0x00800000 }, /* ASP1TX1_INPUT4 */
+ { 0x00008210, 0x00800000 }, /* ASP1TX2_INPUT1 */
+ { 0x00008214, 0x00800000 }, /* ASP1TX2_INPUT2 */
+ { 0x00008218, 0x00800000 }, /* ASP1TX2_INPUT3 */
+ { 0x0000821c, 0x00800000 }, /* ASP1TX2_INPUT4 */
+ { 0x00008220, 0x00800000 }, /* ASP1TX3_INPUT1 */
+ { 0x00008224, 0x00800000 }, /* ASP1TX3_INPUT2 */
+ { 0x00008228, 0x00800000 }, /* ASP1TX3_INPUT3 */
+ { 0x0000822c, 0x00800000 }, /* ASP1TX3_INPUT4 */
+ { 0x00008230, 0x00800000 }, /* ASP1TX4_INPUT1 */
+ { 0x00008234, 0x00800000 }, /* ASP1TX4_INPUT2 */
+ { 0x00008238, 0x00800000 }, /* ASP1TX4_INPUT3 */
+ { 0x0000823c, 0x00800000 }, /* ASP1TX4_INPUT4 */
+ { 0x00008240, 0x00800000 }, /* ASP1TX5_INPUT1 */
+ { 0x00008244, 0x00800000 }, /* ASP1TX5_INPUT2 */
+ { 0x00008248, 0x00800000 }, /* ASP1TX5_INPUT3 */
+ { 0x0000824c, 0x00800000 }, /* ASP1TX5_INPUT4 */
+ { 0x00008250, 0x00800000 }, /* ASP1TX6_INPUT1 */
+ { 0x00008254, 0x00800000 }, /* ASP1TX6_INPUT2 */
+ { 0x00008258, 0x00800000 }, /* ASP1TX6_INPUT3 */
+ { 0x0000825c, 0x00800000 }, /* ASP1TX6_INPUT4 */
+ { 0x00008260, 0x00800000 }, /* ASP1TX7_INPUT1 */
+ { 0x00008264, 0x00800000 }, /* ASP1TX7_INPUT2 */
+ { 0x00008268, 0x00800000 }, /* ASP1TX7_INPUT3 */
+ { 0x0000826c, 0x00800000 }, /* ASP1TX7_INPUT4 */
+ { 0x00008270, 0x00800000 }, /* ASP1TX8_INPUT1 */
+ { 0x00008274, 0x00800000 }, /* ASP1TX8_INPUT2 */
+ { 0x00008278, 0x00800000 }, /* ASP1TX8_INPUT3 */
+ { 0x0000827c, 0x00800000 }, /* ASP1TX8_INPUT4 */
+ { 0x00008300, 0x00800000 }, /* ASP2TX1_INPUT1 */
+ { 0x00008304, 0x00800000 }, /* ASP2TX1_INPUT2 */
+ { 0x00008308, 0x00800000 }, /* ASP2TX1_INPUT3 */
+ { 0x0000830c, 0x00800000 }, /* ASP2TX1_INPUT4 */
+ { 0x00008310, 0x00800000 }, /* ASP2TX2_INPUT1 */
+ { 0x00008314, 0x00800000 }, /* ASP2TX2_INPUT2 */
+ { 0x00008318, 0x00800000 }, /* ASP2TX2_INPUT3 */
+ { 0x0000831c, 0x00800000 }, /* ASP2TX2_INPUT4 */
+ { 0x00008320, 0x00800000 }, /* ASP2TX3_INPUT1 */
+ { 0x00008324, 0x00800000 }, /* ASP2TX3_INPUT2 */
+ { 0x00008328, 0x00800000 }, /* ASP2TX3_INPUT3 */
+ { 0x0000832c, 0x00800000 }, /* ASP2TX3_INPUT4 */
+ { 0x00008330, 0x00800000 }, /* ASP2TX4_INPUT1 */
+ { 0x00008334, 0x00800000 }, /* ASP2TX4_INPUT2 */
+ { 0x00008338, 0x00800000 }, /* ASP2TX4_INPUT3 */
+ { 0x0000833c, 0x00800000 }, /* ASP2TX4_INPUT4 */
+ { 0x00008980, 0x00000000 }, /* ISRC1INT1_INPUT1 */
+ { 0x00008990, 0x00000000 }, /* ISRC1INT2_INPUT1 */
+ { 0x000089a0, 0x00000000 }, /* ISRC1INT3_INPUT1 */
+ { 0x000089b0, 0x00000000 }, /* ISRC1INT4_INPUT1 */
+ { 0x000089c0, 0x00000000 }, /* ISRC1DEC1_INPUT1 */
+ { 0x000089d0, 0x00000000 }, /* ISRC1DEC2_INPUT1 */
+ { 0x000089e0, 0x00000000 }, /* ISRC1DEC3_INPUT1 */
+ { 0x000089f0, 0x00000000 }, /* ISRC1DEC4_INPUT1 */
+ { 0x00008a00, 0x00000000 }, /* ISRC2INT1_INPUT1 */
+ { 0x00008a10, 0x00000000 }, /* ISRC2INT2_INPUT1 */
+ { 0x00008a40, 0x00000000 }, /* ISRC2DEC1_INPUT1 */
+ { 0x00008a50, 0x00000000 }, /* ISRC2DEC2_INPUT1 */
+ { 0x00008a80, 0x00000000 }, /* ISRC3INT1_INPUT1 */
+ { 0x00008a90, 0x00000000 }, /* ISRC3INT2_INPUT1 */
+ { 0x00008ac0, 0x00000000 }, /* ISRC3DEC1_INPUT1 */
+ { 0x00008ad0, 0x00000000 }, /* ISRC3DEC2_INPUT1 */
+ { 0x00008b80, 0x00800000 }, /* EQ1_INPUT1 */
+ { 0x00008b84, 0x00800000 }, /* EQ1_INPUT2 */
+ { 0x00008b88, 0x00800000 }, /* EQ1_INPUT3 */
+ { 0x00008b8c, 0x00800000 }, /* EQ1_INPUT4 */
+ { 0x00008b90, 0x00800000 }, /* EQ2_INPUT1 */
+ { 0x00008b94, 0x00800000 }, /* EQ2_INPUT2 */
+ { 0x00008b98, 0x00800000 }, /* EQ2_INPUT3 */
+ { 0x00008b9c, 0x00800000 }, /* EQ2_INPUT4 */
+ { 0x00008ba0, 0x00800000 }, /* EQ3_INPUT1 */
+ { 0x00008ba4, 0x00800000 }, /* EQ3_INPUT2 */
+ { 0x00008ba8, 0x00800000 }, /* EQ3_INPUT3 */
+ { 0x00008bac, 0x00800000 }, /* EQ3_INPUT4 */
+ { 0x00008bb0, 0x00800000 }, /* EQ4_INPUT1 */
+ { 0x00008bb4, 0x00800000 }, /* EQ4_INPUT2 */
+ { 0x00008bb8, 0x00800000 }, /* EQ4_INPUT3 */
+ { 0x00008bbc, 0x00800000 }, /* EQ4_INPUT4 */
+ { 0x00008c00, 0x00800000 }, /* DRC1L_INPUT1 */
+ { 0x00008c04, 0x00800000 }, /* DRC1L_INPUT2 */
+ { 0x00008c08, 0x00800000 }, /* DRC1L_INPUT3 */
+ { 0x00008c0c, 0x00800000 }, /* DRC1L_INPUT4 */
+ { 0x00008c10, 0x00800000 }, /* DRC1R_INPUT1 */
+ { 0x00008c14, 0x00800000 }, /* DRC1R_INPUT2 */
+ { 0x00008c18, 0x00800000 }, /* DRC1R_INPUT3 */
+ { 0x00008c1c, 0x00800000 }, /* DRC1R_INPUT4 */
+ { 0x00008c20, 0x00800000 }, /* DRC2L_INPUT1 */
+ { 0x00008c24, 0x00800000 }, /* DRC2L_INPUT2 */
+ { 0x00008c28, 0x00800000 }, /* DRC2L_INPUT3 */
+ { 0x00008c2c, 0x00800000 }, /* DRC2L_INPUT4 */
+ { 0x00008c30, 0x00800000 }, /* DRC2R_INPUT1 */
+ { 0x00008c34, 0x00800000 }, /* DRC2R_INPUT2 */
+ { 0x00008c38, 0x00800000 }, /* DRC2R_INPUT3 */
+ { 0x00008c3c, 0x00800000 }, /* DRC2R_INPUT4 */
+ { 0x00008c80, 0x00800000 }, /* LHPF1_INPUT1 */
+ { 0x00008c84, 0x00800000 }, /* LHPF1_INPUT2 */
+ { 0x00008c88, 0x00800000 }, /* LHPF1_INPUT3 */
+ { 0x00008c8c, 0x00800000 }, /* LHPF1_INPUT4 */
+ { 0x00008c90, 0x00800000 }, /* LHPF2_INPUT1 */
+ { 0x00008c94, 0x00800000 }, /* LHPF2_INPUT2 */
+ { 0x00008c98, 0x00800000 }, /* LHPF2_INPUT3 */
+ { 0x00008c9c, 0x00800000 }, /* LHPF2_INPUT4 */
+ { 0x00008ca0, 0x00800000 }, /* LHPF3_INPUT1 */
+ { 0x00008ca4, 0x00800000 }, /* LHPF3_INPUT2 */
+ { 0x00008ca8, 0x00800000 }, /* LHPF3_INPUT3 */
+ { 0x00008cac, 0x00800000 }, /* LHPF3_INPUT4 */
+ { 0x00008cb0, 0x00800000 }, /* LHPF4_INPUT1 */
+ { 0x00008cb4, 0x00800000 }, /* LHPF4_INPUT2 */
+ { 0x00008cb8, 0x00800000 }, /* LHPF4_INPUT3 */
+ { 0x00008cbc, 0x00800000 }, /* LHPF4_INPUT4 */
+ { 0x00009000, 0x00800000 }, /* DSP1RX1_INPUT1 */
+ { 0x00009004, 0x00800000 }, /* DSP1RX1_INPUT2 */
+ { 0x00009008, 0x00800000 }, /* DSP1RX1_INPUT3 */
+ { 0x0000900c, 0x00800000 }, /* DSP1RX1_INPUT4 */
+ { 0x00009010, 0x00800000 }, /* DSP1RX2_INPUT1 */
+ { 0x00009014, 0x00800000 }, /* DSP1RX2_INPUT2 */
+ { 0x00009018, 0x00800000 }, /* DSP1RX2_INPUT3 */
+ { 0x0000901c, 0x00800000 }, /* DSP1RX2_INPUT4 */
+ { 0x00009020, 0x00800000 }, /* DSP1RX3_INPUT1 */
+ { 0x00009024, 0x00800000 }, /* DSP1RX3_INPUT2 */
+ { 0x00009028, 0x00800000 }, /* DSP1RX3_INPUT3 */
+ { 0x0000902c, 0x00800000 }, /* DSP1RX3_INPUT4 */
+ { 0x00009030, 0x00800000 }, /* DSP1RX4_INPUT1 */
+ { 0x00009034, 0x00800000 }, /* DSP1RX4_INPUT2 */
+ { 0x00009038, 0x00800000 }, /* DSP1RX4_INPUT3 */
+ { 0x0000903c, 0x00800000 }, /* DSP1RX4_INPUT4 */
+ { 0x00009040, 0x00800000 }, /* DSP1RX5_INPUT1 */
+ { 0x00009044, 0x00800000 }, /* DSP1RX5_INPUT2 */
+ { 0x00009048, 0x00800000 }, /* DSP1RX5_INPUT3 */
+ { 0x0000904c, 0x00800000 }, /* DSP1RX5_INPUT4 */
+ { 0x00009050, 0x00800000 }, /* DSP1RX6_INPUT1 */
+ { 0x00009054, 0x00800000 }, /* DSP1RX6_INPUT2 */
+ { 0x00009058, 0x00800000 }, /* DSP1RX6_INPUT3 */
+ { 0x0000905c, 0x00800000 }, /* DSP1RX6_INPUT4 */
+ { 0x00009060, 0x00800000 }, /* DSP1RX7_INPUT1 */
+ { 0x00009064, 0x00800000 }, /* DSP1RX7_INPUT2 */
+ { 0x00009068, 0x00800000 }, /* DSP1RX7_INPUT3 */
+ { 0x0000906c, 0x00800000 }, /* DSP1RX7_INPUT4 */
+ { 0x00009070, 0x00800000 }, /* DSP1RX8_INPUT1 */
+ { 0x00009074, 0x00800000 }, /* DSP1RX8_INPUT2 */
+ { 0x00009078, 0x00800000 }, /* DSP1RX8_INPUT3 */
+ { 0x0000907c, 0x00800000 }, /* DSP1RX8_INPUT4 */
+ { 0x0000a400, 0x00000000 }, /* ISRC1_CONTROL1 */
+ { 0x0000a404, 0x00000000 }, /* ISRC1_CONTROL2 */
+ { 0x0000a510, 0x00000000 }, /* ISRC2_CONTROL1 */
+ { 0x0000a514, 0x00000000 }, /* ISRC2_CONTROL2 */
+ { 0x0000a620, 0x00000000 }, /* ISRC3_CONTROL1 */
+ { 0x0000a624, 0x00000000 }, /* ISRC3_CONTROL2 */
+ { 0x0000a800, 0x00000000 }, /* FX_SAMPLE_RATE */
+ { 0x0000a808, 0x00000000 }, /* EQ_CONTROL1 */
+ { 0x0000a80c, 0x00000000 }, /* EQ_CONTROL2 */
+ { 0x0000a810, 0x0c0c0c0c }, /* EQ1_GAIN1 */
+ { 0x0000a814, 0x0000000c }, /* EQ1_GAIN2 */
+ { 0x0000a818, 0x03fe0fc8 }, /* EQ1_BAND1_COEFF1 */
+ { 0x0000a81c, 0x00000b75 }, /* EQ1_BAND1_COEFF2 */
+ { 0x0000a820, 0x000000e0 }, /* EQ1_BAND1_PG */
+ { 0x0000a824, 0xf1361ec4 }, /* EQ1_BAND2_COEFF1 */
+ { 0x0000a828, 0x00000409 }, /* EQ1_BAND2_COEFF2 */
+ { 0x0000a82c, 0x000004cc }, /* EQ1_BAND2_PG */
+ { 0x0000a830, 0xf3371c9b }, /* EQ1_BAND3_COEFF1 */
+ { 0x0000a834, 0x0000040b }, /* EQ1_BAND3_COEFF2 */
+ { 0x0000a838, 0x00000cbb }, /* EQ1_BAND3_PG */
+ { 0x0000a83c, 0xf7d916f8 }, /* EQ1_BAND4_COEFF1 */
+ { 0x0000a840, 0x0000040a }, /* EQ1_BAND4_COEFF2 */
+ { 0x0000a844, 0x00001f14 }, /* EQ1_BAND4_PG */
+ { 0x0000a848, 0x0563058c }, /* EQ1_BAND5_COEFF1 */
+ { 0x0000a84c, 0x00000000 }, /* EQ1_BAND5_COEFF1 + 4 */
+ { 0x0000a850, 0x00004000 }, /* EQ1_BAND5_PG */
+ { 0x0000a854, 0x0c0c0c0c }, /* EQ2_GAIN1 */
+ { 0x0000a858, 0x0000000c }, /* EQ2_GAIN2 */
+ { 0x0000a85c, 0x03fe0fc8 }, /* EQ2_BAND1_COEFF1 */
+ { 0x0000a860, 0x00000b75 }, /* EQ2_BAND1_COEFF2 */
+ { 0x0000a864, 0x000000e0 }, /* EQ2_BAND1_PG */
+ { 0x0000a868, 0xf1361ec4 }, /* EQ2_BAND2_COEFF1 */
+ { 0x0000a86c, 0x00000409 }, /* EQ2_BAND2_COEFF2 */
+ { 0x0000a870, 0x000004cc }, /* EQ2_BAND2_PG */
+ { 0x0000a874, 0xf3371c9b }, /* EQ2_BAND3_COEFF1 */
+ { 0x0000a878, 0x0000040b }, /* EQ2_BAND3_COEFF2 */
+ { 0x0000a87c, 0x00000cbb }, /* EQ2_BAND3_PG */
+ { 0x0000a880, 0xf7d916f8 }, /* EQ2_BAND4_COEFF1 */
+ { 0x0000a884, 0x0000040a }, /* EQ2_BAND4_COEFF2 */
+ { 0x0000a888, 0x00001f14 }, /* EQ2_BAND4_PG */
+ { 0x0000a88c, 0x0563058c }, /* EQ2_BAND5_COEFF1 */
+ { 0x0000a890, 0x00000000 }, /* EQ2_BAND5_COEFF1 + 4 */
+ { 0x0000a894, 0x00004000 }, /* EQ2_BAND5_PG */
+ { 0x0000a898, 0x0c0c0c0c }, /* EQ3_GAIN1 */
+ { 0x0000a89c, 0x0000000c }, /* EQ3_GAIN2 */
+ { 0x0000a8a0, 0x03fe0fc8 }, /* EQ3_BAND1_COEFF1 */
+ { 0x0000a8a4, 0x00000b75 }, /* EQ3_BAND1_COEFF2 */
+ { 0x0000a8a8, 0x000000e0 }, /* EQ3_BAND1_PG */
+ { 0x0000a8ac, 0xf1361ec4 }, /* EQ3_BAND2_COEFF1 */
+ { 0x0000a8b0, 0x00000409 }, /* EQ3_BAND2_COEFF2 */
+ { 0x0000a8b4, 0x000004cc }, /* EQ3_BAND2_PG */
+ { 0x0000a8b8, 0xf3371c9b }, /* EQ3_BAND3_COEFF1 */
+ { 0x0000a8bc, 0x0000040b }, /* EQ3_BAND3_COEFF2 */
+ { 0x0000a8c0, 0x00000cbb }, /* EQ3_BAND3_PG */
+ { 0x0000a8c4, 0xf7d916f8 }, /* EQ3_BAND4_COEFF1 */
+ { 0x0000a8c8, 0x0000040a }, /* EQ3_BAND4_COEFF2 */
+ { 0x0000a8cc, 0x00001f14 }, /* EQ3_BAND4_PG */
+ { 0x0000a8d0, 0x0563058c }, /* EQ3_BAND5_COEFF1 */
+ { 0x0000a8d4, 0x00000000 }, /* EQ3_BAND5_COEFF1 + 4 */
+ { 0x0000a8d8, 0x00004000 }, /* EQ3_BAND5_PG */
+ { 0x0000a8dc, 0x0c0c0c0c }, /* EQ4_GAIN1 */
+ { 0x0000a8e0, 0x0000000c }, /* EQ4_GAIN2 */
+ { 0x0000a8e4, 0x03fe0fc8 }, /* EQ4_BAND1_COEFF1 */
+ { 0x0000a8e8, 0x00000b75 }, /* EQ4_BAND1_COEFF2 */
+ { 0x0000a8ec, 0x000000e0 }, /* EQ4_BAND1_PG */
+ { 0x0000a8f0, 0xf1361ec4 }, /* EQ4_BAND2_COEFF1 */
+ { 0x0000a8f4, 0x00000409 }, /* EQ4_BAND2_COEFF2 */
+ { 0x0000a8f8, 0x000004cc }, /* EQ4_BAND2_PG */
+ { 0x0000a8fc, 0xf3371c9b }, /* EQ4_BAND3_COEFF1 */
+ { 0x0000a900, 0x0000040b }, /* EQ4_BAND3_COEFF2 */
+ { 0x0000a904, 0x00000cbb }, /* EQ4_BAND3_PG */
+ { 0x0000a908, 0xf7d916f8 }, /* EQ4_BAND4_COEFF1 */
+ { 0x0000a90c, 0x0000040a }, /* EQ4_BAND4_COEFF2 */
+ { 0x0000a910, 0x00001f14 }, /* EQ4_BAND4_PG */
+ { 0x0000a914, 0x0563058c }, /* EQ4_BAND5_COEFF1 */
+ { 0x0000a918, 0x00000000 }, /* EQ4_BAND5_COEFF1 + 4 */
+ { 0x0000a91c, 0x00004000 }, /* EQ4_BAND5_PG */
+ { 0x0000aa30, 0x00000000 }, /* LHPF_CONTROL1 */
+ { 0x0000aa34, 0x00000000 }, /* LHPF_CONTROL2 */
+ { 0x0000aa38, 0x00000000 }, /* LHPF1_COEFF */
+ { 0x0000aa3c, 0x00000000 }, /* LHPF2_COEFF */
+ { 0x0000aa40, 0x00000000 }, /* LHPF3_COEFF */
+ { 0x0000aa44, 0x00000000 }, /* LHPF4_COEFF */
+ { 0x0000ab00, 0x00000000 }, /* DRC1_CONTROL1 */
+ { 0x0000ab04, 0x49130018 }, /* DRC1_CONTROL2 */
+ { 0x0000ab08, 0x00000018 }, /* DRC1_CONTROL3 */
+ { 0x0000ab0c, 0x00000000 }, /* DRC1_CONTROL4 */
+ { 0x0000ab14, 0x00000000 }, /* DRC2_CONTROL1 */
+ { 0x0000ab18, 0x49130018 }, /* DRC2_CONTROL2 */
+ { 0x0000ab1c, 0x00000018 }, /* DRC2_CONTROL3 */
+ { 0x0000ab20, 0x00000000 }, /* DRC2_CONTROL4 */
+ { 0x0000b000, 0x00000000 }, /* TONE_GENERATOR1 */
+ { 0x0000b004, 0x00100000 }, /* TONE_GENERATOR2 */
+ { 0x0000b400, 0x00000000 }, /* COMFORT_NOISE_GENERATOR */
+ { 0x0000b800, 0x00000000 }, /* US_CONTROL */
+ { 0x0000b804, 0x00002020 }, /* US1_CONTROL */
+ { 0x0000b808, 0x00000000 }, /* US1_DET_CONTROL */
+ { 0x0000b814, 0x00002020 }, /* US2_CONTROL */
+ { 0x0000b818, 0x00000000 }, /* US2_DET_CONTROL */
+ { 0x00018110, 0x00000700 }, /* IRQ1_MASK_1 */
+ { 0x00018114, 0x00000004 }, /* IRQ1_MASK_2 */
+ { 0x00018120, 0x03ff0000 }, /* IRQ1_MASK_5 */
+ { 0x00018124, 0x00000103 }, /* IRQ1_MASK_6 */
+ { 0x00018128, 0x003f0000 }, /* IRQ1_MASK_7 */
+ { 0x00018130, 0xff00000f }, /* IRQ1_MASK_9 */
+ { 0x00018138, 0xffff0000 }, /* IRQ1_MASK_11 */
+};
+
+static bool cs48l32_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_GPIO1_CTRL1 ... CS48L32_GPIO16_CTRL1:
+ case CS48L32_OUTPUT_SYS_CLK:
+ case CS48L32_AUXPDM_CTRL:
+ case CS48L32_AUXPDM_CTRL2:
+ case CS48L32_CLOCK32K:
+ case CS48L32_SYSTEM_CLOCK1 ... CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_SAMPLE_RATE1 ... CS48L32_SAMPLE_RATE4:
+ case CS48L32_FLL1_CONTROL1 ... CS48L32_FLL1_GPIO_CLOCK:
+ case CS48L32_CHARGE_PUMP1:
+ case CS48L32_LDO2_CTRL1:
+ case CS48L32_MICBIAS_CTRL1:
+ case CS48L32_MICBIAS_CTRL5:
+ case CS48L32_IRQ1_CTRL_AOD:
+ case CS48L32_INPUT_CONTROL:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_RATE_CONTROL:
+ case CS48L32_INPUT_CONTROL2:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_INPUT1_CONTROL1:
+ case CS48L32_IN1L_CONTROL1 ... CS48L32_IN1L_CONTROL2:
+ case CS48L32_IN1R_CONTROL1 ... CS48L32_IN1R_CONTROL2:
+ case CS48L32_INPUT2_CONTROL1:
+ case CS48L32_IN2L_CONTROL1 ... CS48L32_IN2L_CONTROL2:
+ case CS48L32_IN2R_CONTROL1 ... CS48L32_IN2R_CONTROL2:
+ case CS48L32_INPUT_HPF_CONTROL:
+ case CS48L32_INPUT_VOL_CONTROL:
+ case CS48L32_AUXPDM_CONTROL1:
+ case CS48L32_AUXPDM_CONTROL2:
+ case CS48L32_AUXPDM1_CONTROL1:
+ case CS48L32_AUXPDM2_CONTROL1:
+ case CS48L32_ADC1L_ANA_CONTROL1:
+ case CS48L32_ADC1R_ANA_CONTROL1:
+ case CS48L32_ASP1_ENABLES1 ... CS48L32_ASP1_DATA_CONTROL5:
+ case CS48L32_ASP2_ENABLES1 ... CS48L32_ASP2_DATA_CONTROL5:
+ case CS48L32_ASP1TX1_INPUT1 ... CS48L32_ASP1TX8_INPUT4:
+ case CS48L32_ASP2TX1_INPUT1 ... CS48L32_ASP2TX4_INPUT4:
+ case CS48L32_ISRC1INT1_INPUT1 ... CS48L32_ISRC1DEC4_INPUT1:
+ case CS48L32_ISRC2INT1_INPUT1 ... CS48L32_ISRC2DEC2_INPUT1:
+ case CS48L32_ISRC3INT1_INPUT1 ... CS48L32_ISRC3DEC2_INPUT1:
+ case CS48L32_EQ1_INPUT1 ... CS48L32_EQ4_INPUT4:
+ case CS48L32_DRC1L_INPUT1 ... CS48L32_DRC1R_INPUT4:
+ case CS48L32_DRC2L_INPUT1 ... CS48L32_DRC2R_INPUT4:
+ case CS48L32_LHPF1_INPUT1 ... CS48L32_LHPF1_INPUT4:
+ case CS48L32_LHPF2_INPUT1 ... CS48L32_LHPF2_INPUT4:
+ case CS48L32_LHPF3_INPUT1 ... CS48L32_LHPF3_INPUT4:
+ case CS48L32_LHPF4_INPUT1 ... CS48L32_LHPF4_INPUT4:
+ case CS48L32_DSP1RX1_INPUT1 ... CS48L32_DSP1RX8_INPUT4:
+ case CS48L32_ISRC1_CONTROL1 ... CS48L32_ISRC1_CONTROL2:
+ case CS48L32_ISRC2_CONTROL1 ... CS48L32_ISRC2_CONTROL2:
+ case CS48L32_ISRC3_CONTROL1 ... CS48L32_ISRC3_CONTROL2:
+ case CS48L32_FX_SAMPLE_RATE:
+ case CS48L32_EQ_CONTROL1 ... CS48L32_EQ_CONTROL2:
+ case CS48L32_EQ1_GAIN1 ... CS48L32_EQ1_BAND5_PG:
+ case CS48L32_EQ2_GAIN1 ... CS48L32_EQ2_BAND5_PG:
+ case CS48L32_EQ3_GAIN1 ... CS48L32_EQ3_BAND5_PG:
+ case CS48L32_EQ4_GAIN1 ... CS48L32_EQ4_BAND5_PG:
+ case CS48L32_LHPF_CONTROL1 ... CS48L32_LHPF_CONTROL2:
+ case CS48L32_LHPF1_COEFF ... CS48L32_LHPF4_COEFF:
+ case CS48L32_DRC1_CONTROL1 ... CS48L32_DRC1_CONTROL4:
+ case CS48L32_DRC2_CONTROL1 ... CS48L32_DRC2_CONTROL4:
+ case CS48L32_TONE_GENERATOR1 ... CS48L32_TONE_GENERATOR2:
+ case CS48L32_COMFORT_NOISE_GENERATOR:
+ case CS48L32_US_CONTROL:
+ case CS48L32_US1_CONTROL:
+ case CS48L32_US1_DET_CONTROL:
+ case CS48L32_US2_CONTROL:
+ case CS48L32_US2_DET_CONTROL:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_IRQ1_MASK_1 ... CS48L32_IRQ1_MASK_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs48l32_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DEVID:
+ case CS48L32_REVID:
+ case CS48L32_OTPID:
+ case CS48L32_SFT_RESET:
+ case CS48L32_CTRL_IF_DEBUG3:
+ case CS48L32_MCU_CTRL1:
+ case CS48L32_SYSTEM_CLOCK2:
+ case CS48L32_FLL1_CONTROL5:
+ case CS48L32_FLL1_CONTROL6:
+ case CS48L32_INPUT_STATUS:
+ case CS48L32_INPUT_CONTROL3:
+ case CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24:
+ case CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8:
+ case CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0 ... CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7:
+ case CS48L32_IRQ1_STATUS:
+ case CS48L32_IRQ1_EINT_1 ... CS48L32_IRQ1_EINT_11:
+ case CS48L32_IRQ1_STS_1 ... CS48L32_IRQ1_STS_11:
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_SYS_INFO_ID ... CS48L32_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS48L32_DSP1_XMEM_UNPACKED24_0 ... CS48L32_DSP1_XMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_CLOCK_FREQ ... CS48L32_DSP1_SAMPLE_RATE_TX8:
+ case CS48L32_DSP1_SCRATCH1 ... CS48L32_DSP1_SCRATCH4:
+ case CS48L32_DSP1_CCM_CORE_CONTROL ... CS48L32_DSP1_STREAM_ARB_RESYNC_MSK1:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_UNPACKED24_0 ... CS48L32_DSP1_YMEM_UNPACKED24_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/*
+ * The bus bridge requires DSP packed memory registers to be accessed in
+ * aligned block multiples.
+ * Mark precious to prevent regmap debugfs causing an illegal bus transaction.
+ */
+static bool cs48l32_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS48L32_DSP1_XMEM_PACKED_0 ... CS48L32_DSP1_XMEM_PACKED_LAST:
+ case CS48L32_DSP1_YMEM_PACKED_0 ... CS48L32_DSP1_YMEM_PACKED_LAST:
+ case CS48L32_DSP1_PMEM_0 ... CS48L32_DSP1_PMEM_LAST:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config cs48l32_regmap = {
+ .name = "cs48l32",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .pad_bits = 32,
+ .val_bits = 32,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+
+ .max_register = CS48L32_DSP1_PMEM_LAST,
+ .readable_reg = &cs48l32_readable_register,
+ .volatile_reg = &cs48l32_volatile_register,
+ .precious_reg = &cs48l32_precious_register,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = cs48l32_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(cs48l32_reg_default),
+};
+
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32)
+{
+ cs48l32->regmap = devm_regmap_init_spi(spi, &cs48l32_regmap);
+ if (IS_ERR(cs48l32->regmap))
+ return PTR_ERR(cs48l32->regmap);
+
+ return 0;
+}
diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c
new file mode 100644
index 000000000000..a306af4289ad
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.c
@@ -0,0 +1,4076 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Cirrus Logic CS48L32 audio DSP.
+//
+// Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <dt-bindings/sound/cs48l32.h>
+#include <linux/array_size.h>
+#include <linux/build_bug.h>
+#include <linux/clk.h>
+#include <linux/container_of.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gcd.h>
+#include <linux/gpio/consumer.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+#include <linux/string_choices.h>
+#include <sound/cs48l32.h>
+#include <sound/cs48l32_registers.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "cs48l32.h"
+
+static const char * const cs48l32_core_supplies[] = { "vdd-a", "vdd-io" };
+
+static const struct cs_dsp_region cs48l32_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = 0x3800000 },
+ { .type = WMFW_HALO_XM_PACKED, .base = 0x2000000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x2800000 },
+ { .type = WMFW_HALO_YM_PACKED, .base = 0x2C00000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x3400000 },
+};
+
+static const struct cs48l32_dsp_power_reg_block cs48l32_dsp1_sram_ext_regs[] = {
+ { CS48L32_DSP1_XM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_XM_SRAM_IBUS_SETUP_24 },
+ { CS48L32_DSP1_YM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_YM_SRAM_IBUS_SETUP_8 },
+ { CS48L32_DSP1_PM_SRAM_IBUS_SETUP_1, CS48L32_DSP1_PM_SRAM_IBUS_SETUP_7 },
+};
+
+static const unsigned int cs48l32_dsp1_sram_pwd_regs[] = {
+ CS48L32_DSP1_XM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_YM_SRAM_IBUS_SETUP_0,
+ CS48L32_DSP1_PM_SRAM_IBUS_SETUP_0,
+};
+
+static const struct cs48l32_dsp_power_regs cs48l32_dsp_sram_regs = {
+ .ext = cs48l32_dsp1_sram_ext_regs,
+ .n_ext = ARRAY_SIZE(cs48l32_dsp1_sram_ext_regs),
+ .pwd = cs48l32_dsp1_sram_pwd_regs,
+ .n_pwd = ARRAY_SIZE(cs48l32_dsp1_sram_pwd_regs),
+};
+
+static const char * const cs48l32_mixer_texts[] = {
+ "None",
+ "Tone Generator 1",
+ "Tone Generator 2",
+ "Noise Generator",
+ "IN1L",
+ "IN1R",
+ "IN2L",
+ "IN2R",
+ "ASP1RX1",
+ "ASP1RX2",
+ "ASP1RX3",
+ "ASP1RX4",
+ "ASP1RX5",
+ "ASP1RX6",
+ "ASP1RX7",
+ "ASP1RX8",
+ "ASP2RX1",
+ "ASP2RX2",
+ "ASP2RX3",
+ "ASP2RX4",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "EQ1",
+ "EQ2",
+ "EQ3",
+ "EQ4",
+ "DRC1L",
+ "DRC1R",
+ "DRC2L",
+ "DRC2R",
+ "LHPF1",
+ "LHPF2",
+ "LHPF3",
+ "LHPF4",
+ "Ultrasonic 1",
+ "Ultrasonic 2",
+ "DSP1.1",
+ "DSP1.2",
+ "DSP1.3",
+ "DSP1.4",
+ "DSP1.5",
+ "DSP1.6",
+ "DSP1.7",
+ "DSP1.8",
+};
+
+static unsigned int cs48l32_mixer_values[] = {
+ 0x000, /* Silence (mute) */
+ 0x004, /* Tone generator 1 */
+ 0x005, /* Tone generator 2 */
+ 0x00C, /* Noise Generator */
+ 0x010, /* IN1L signal path */
+ 0x011, /* IN1R signal path */
+ 0x012, /* IN2L signal path */
+ 0x013, /* IN2R signal path */
+ 0x020, /* ASP1 RX1 */
+ 0x021, /* ASP1 RX2 */
+ 0x022, /* ASP1 RX3 */
+ 0x023, /* ASP1 RX4 */
+ 0x024, /* ASP1 RX5 */
+ 0x025, /* ASP1 RX6 */
+ 0x026, /* ASP1 RX7 */
+ 0x027, /* ASP1 RX8 */
+ 0x030, /* ASP2 RX1 */
+ 0x031, /* ASP2 RX2 */
+ 0x032, /* ASP2 RX3 */
+ 0x033, /* ASP2 RX4 */
+ 0x098, /* ISRC1 INT1 */
+ 0x099, /* ISRC1 INT2 */
+ 0x09a, /* ISRC1 INT3 */
+ 0x09b, /* ISRC1 INT4 */
+ 0x09C, /* ISRC1 DEC1 */
+ 0x09D, /* ISRC1 DEC2 */
+ 0x09e, /* ISRC1 DEC3 */
+ 0x09f, /* ISRC1 DEC4 */
+ 0x0A0, /* ISRC2 INT1 */
+ 0x0A1, /* ISRC2 INT2 */
+ 0x0A4, /* ISRC2 DEC1 */
+ 0x0A5, /* ISRC2 DEC2 */
+ 0x0A8, /* ISRC3 INT1 */
+ 0x0A9, /* ISRC3 INT2 */
+ 0x0AC, /* ISRC3 DEC1 */
+ 0x0AD, /* ISRC3 DEC2 */
+ 0x0B8, /* EQ1 */
+ 0x0B9, /* EQ2 */
+ 0x0BA, /* EQ3 */
+ 0x0BB, /* EQ4 */
+ 0x0C0, /* DRC1 Left */
+ 0x0C1, /* DRC1 Right */
+ 0x0C2, /* DRC2 Left */
+ 0x0C3, /* DRC2 Right */
+ 0x0C8, /* LHPF1 */
+ 0x0C9, /* LHPF2 */
+ 0x0CA, /* LHPF3 */
+ 0x0CB, /* LHPF4 */
+ 0x0D8, /* Ultrasonic 1 */
+ 0x0D9, /* Ultrasonic 2 */
+ 0x100, /* DSP1 channel 1 */
+ 0x101, /* DSP1 channel 2 */
+ 0x102, /* DSP1 channel 3 */
+ 0x103, /* DSP1 channel 4 */
+ 0x104, /* DSP1 channel 5 */
+ 0x105, /* DSP1 channel 6 */
+ 0x106, /* DSP1 channel 7 */
+ 0x107, /* DSP1 channel 8 */
+};
+static_assert(ARRAY_SIZE(cs48l32_mixer_texts) == ARRAY_SIZE(cs48l32_mixer_values));
+#define CS48L32_NUM_MIXER_INPUTS ARRAY_SIZE(cs48l32_mixer_values)
+
+static const DECLARE_TLV_DB_SCALE(cs48l32_ana_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_eq_tlv, -1200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_digital_tlv, -6400, 50, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_noise_tlv, -10800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_mixer_tlv, -3200, 100, 0);
+static const DECLARE_TLV_DB_SCALE(cs48l32_us_tlv, 0, 600, 0);
+
+static void cs48l32_spin_sysclk(struct cs48l32_codec *cs48l32_codec)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret, i;
+
+ /* Skip this if the chip is down */
+ if (pm_runtime_suspended(cs48l32->dev))
+ return;
+
+ /*
+ * Just read a register a few times to ensure the internal
+ * oscillator sends out some clocks.
+ */
+ for (i = 0; i < 4; i++) {
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &val);
+ if (ret)
+ dev_err(cs48l32_codec->core.dev, "%s Failed to read register: %d (%d)\n",
+ __func__, ret, i);
+ }
+
+ udelay(300);
+}
+
+static const char * const cs48l32_rate_text[] = {
+ "Sample Rate 1", "Sample Rate 2", "Sample Rate 3", "Sample Rate 4",
+};
+
+static const unsigned int cs48l32_rate_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+static_assert(ARRAY_SIZE(cs48l32_rate_val) == ARRAY_SIZE(cs48l32_rate_text));
+
+static int cs48l32_rate_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Prevent any mixer mux changes while we do this */
+ mutex_lock(&cs48l32_codec->rate_lock);
+
+ /* The write must be guarded by a number of SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ mutex_unlock(&cs48l32_codec->rate_lock);
+
+ return ret;
+}
+
+static const char * const cs48l32_sample_rate_text[] = {
+ "12kHz",
+ "24kHz",
+ "48kHz",
+ "96kHz",
+ "192kHz",
+ "384kHz",
+ "768kHz",
+ "11.025kHz",
+ "22.05kHz",
+ "44.1kHz",
+ "88.2kHz",
+ "176.4kHz",
+ "352.8kHz",
+ "705.6kHz",
+ "8kHz",
+ "16kHz",
+ "32kHz",
+};
+
+static const unsigned int cs48l32_sample_rate_val[] = {
+ 0x01, /* 12kHz */
+ 0x02, /* 24kHz */
+ 0x03, /* 48kHz */
+ 0x04, /* 96kHz */
+ 0x05, /* 192kHz */
+ 0x06, /* 384kHz */
+ 0x07, /* 768kHz */
+ 0x09, /* 11.025kHz */
+ 0x0a, /* 22.05kHz */
+ 0x0b, /* 44.1kHz */
+ 0x0c, /* 88.2kHz */
+ 0x0d, /* 176.4kHz */
+ 0x0e, /* 352.8kHz */
+ 0x0f, /* 705.6kHz */
+ 0x11, /* 8kHz */
+ 0x12, /* 16kHz */
+ 0x13, /* 32kHz */
+};
+static_assert(ARRAY_SIZE(cs48l32_sample_rate_val) == ARRAY_SIZE(cs48l32_sample_rate_text));
+#define CS48L32_SAMPLE_RATE_ENUM_SIZE ARRAY_SIZE(cs48l32_sample_rate_val)
+
+static const struct soc_enum cs48l32_sample_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE1,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE2,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE3,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_SAMPLE_RATE4,
+ CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_1_MASK >> CS48L32_SAMPLE_RATE_1_SHIFT,
+ CS48L32_SAMPLE_RATE_ENUM_SIZE,
+ cs48l32_sample_rate_text,
+ cs48l32_sample_rate_val),
+};
+
+static int cs48l32_inmux_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mux, src_val, in_type;
+ int ret;
+
+ mux = ucontrol->value.enumerated.item[0];
+ if (mux > 1)
+ return -EINVAL;
+
+ switch (e->reg) {
+ case CS48L32_IN1L_CONTROL1:
+ in_type = cs48l32_codec->in_type[0][mux];
+ break;
+ case CS48L32_IN1R_CONTROL1:
+ in_type = cs48l32_codec->in_type[1][mux];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ src_val = mux << e->shift_l;
+
+ if (in_type == CS48L32_IN_TYPE_SE)
+ src_val |= 1 << CS48L32_INx_SRC_SHIFT;
+
+ ret = snd_soc_component_update_bits(dapm->component,
+ e->reg,
+ CS48L32_INx_SRC_MASK,
+ src_val);
+ if (ret > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
+
+ return ret;
+}
+
+static const char * const cs48l32_inmux_texts[] = {
+ "Analog 1", "Analog 2",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxl_enum,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1muxr_enum,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_SHIFT + 1,
+ cs48l32_inmux_texts);
+
+static const struct snd_kcontrol_new cs48l32_inmux[] = {
+ SOC_DAPM_ENUM_EXT("IN1L Mux", cs48l32_in1muxl_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+ SOC_DAPM_ENUM_EXT("IN1R Mux", cs48l32_in1muxr_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_inmux_put),
+};
+
+static const char * const cs48l32_dmode_texts[] = {
+ "Analog", "Digital",
+};
+
+static int cs48l32_dmode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int mode;
+ int ret, result;
+
+ mode = ucontrol->value.enumerated.item[0];
+ switch (mode) {
+ case 0:
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to set ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ result = snd_soc_component_update_bits(component,
+ e->reg,
+ BIT(CS48L32_IN1_MODE_SHIFT),
+ 0);
+ if (result < 0) {
+ dev_err(component->dev, "Failed to set input mode: %d\n", result);
+ return result;
+ }
+
+ usleep_range(200, 300);
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1L_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ if (ret < 0) {
+ dev_err(component->dev,
+ "Failed to clear ADC1R_INT_ENA_FRC: %d\n", ret);
+ return ret;
+ }
+
+ if (result > 0)
+ snd_soc_dapm_mux_update_power(dapm, kcontrol, mode, e, NULL);
+
+ return result;
+ case 1:
+ return snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ default:
+ return -EINVAL;
+ }
+}
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in1dmode_enum,
+ CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_MODE_SHIFT,
+ cs48l32_dmode_texts);
+
+static const struct snd_kcontrol_new cs48l32_dmode_mux[] = {
+ SOC_DAPM_ENUM_EXT("IN1 Mode", cs48l32_in1dmode_enum,
+ snd_soc_dapm_get_enum_double, cs48l32_dmode_put),
+};
+
+static const char * const cs48l32_in_texts[] = {
+ "IN1L", "IN1R", "IN2L", "IN2R",
+};
+static_assert(ARRAY_SIZE(cs48l32_in_texts) == CS48L32_MAX_INPUT);
+
+static const char * const cs48l32_us_freq_texts[] = {
+ "16-24kHz", "20-28kHz",
+};
+
+static const unsigned int cs48l32_us_freq_val[] = {
+ 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_freq[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_FREQ_SHIFT,
+ CS48L32_US1_FREQ_MASK >> CS48L32_US1_FREQ_SHIFT,
+ ARRAY_SIZE(cs48l32_us_freq_val),
+ cs48l32_us_freq_texts,
+ cs48l32_us_freq_val),
+};
+
+static const unsigned int cs48l32_us_in_val[] = {
+ 0x0, 0x1, 0x2, 0x3,
+};
+
+static const struct soc_enum cs48l32_us_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_SRC_SHIFT,
+ CS48L32_US1_SRC_MASK >> CS48L32_US1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_us_in_val),
+ cs48l32_in_texts,
+ cs48l32_us_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_inmux[] = {
+ SOC_DAPM_ENUM("Ultrasonic 1 Input", cs48l32_us_inmux_enum[0]),
+ SOC_DAPM_ENUM("Ultrasonic 2 Input", cs48l32_us_inmux_enum[1]),
+};
+
+static const char * const cs48l32_us_det_thr_texts[] = {
+ "-6dB", "-9dB", "-12dB", "-15dB", "-18dB", "-21dB", "-24dB", "-27dB",
+};
+
+static const struct soc_enum cs48l32_us_det_thr[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_THR_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_thr_texts),
+ cs48l32_us_det_thr_texts),
+};
+
+static const char * const cs48l32_us_det_num_texts[] = {
+ "1 Sample",
+ "2 Samples",
+ "4 Samples",
+ "8 Samples",
+ "16 Samples",
+ "32 Samples",
+ "64 Samples",
+ "128 Samples",
+ "256 Samples",
+ "512 Samples",
+ "1024 Samples",
+ "2048 Samples",
+ "4096 Samples",
+ "8192 Samples",
+ "16384 Samples",
+ "32768 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_num[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_NUM_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_num_texts),
+ cs48l32_us_det_num_texts),
+};
+
+static const char * const cs48l32_us_det_hold_texts[] = {
+ "0 Samples",
+ "31 Samples",
+ "63 Samples",
+ "127 Samples",
+ "255 Samples",
+ "511 Samples",
+ "1023 Samples",
+ "2047 Samples",
+ "4095 Samples",
+ "8191 Samples",
+ "16383 Samples",
+ "32767 Samples",
+ "65535 Samples",
+ "131071 Samples",
+ "262143 Samples",
+ "524287 Samples",
+};
+
+static const struct soc_enum cs48l32_us_det_hold[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_HOLD_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_hold_texts),
+ cs48l32_us_det_hold_texts),
+};
+
+static const struct soc_enum cs48l32_us_output_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US1_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_US2_CONTROL,
+ CS48L32_US1_RATE_SHIFT,
+ CS48L32_US1_RATE_MASK >> CS48L32_US1_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const char * const cs48l32_us_det_lpf_cut_texts[] = {
+ "1722Hz", "833Hz", "408Hz", "203Hz",
+};
+
+static const struct soc_enum cs48l32_us_det_lpf_cut[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_LPF_CUT_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_lpf_cut_texts),
+ cs48l32_us_det_lpf_cut_texts),
+};
+
+static const char * const cs48l32_us_det_dcy_texts[] = {
+ "0 ms", "0.79 ms", "1.58 ms", "3.16 ms", "6.33 ms", "12.67 ms", "25.34 ms", "50.69 ms",
+};
+
+static const struct soc_enum cs48l32_us_det_dcy[] = {
+ SOC_ENUM_SINGLE(CS48L32_US1_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+ SOC_ENUM_SINGLE(CS48L32_US2_DET_CONTROL,
+ CS48L32_US1_DET_DCY_SHIFT,
+ ARRAY_SIZE(cs48l32_us_det_dcy_texts),
+ cs48l32_us_det_dcy_texts),
+};
+
+static const struct snd_kcontrol_new cs48l32_us_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const char * const cs48l32_vol_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB", "16ms/6dB", "32ms/6dB",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vd_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VD_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_vi_ramp,
+ CS48L32_INPUT_VOL_CONTROL,
+ CS48L32_IN_VI_RAMP_SHIFT,
+ cs48l32_vol_ramp_text);
+
+static const char * const cs48l32_in_hpf_cut_text[] = {
+ "2.5Hz", "5Hz", "10Hz", "20Hz", "40Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_in_hpf_cut_enum,
+ CS48L32_INPUT_HPF_CONTROL,
+ CS48L32_IN_HPF_CUT_SHIFT,
+ cs48l32_in_hpf_cut_text);
+
+static const char * const cs48l32_in_dmic_osr_text[] = {
+ "384kHz", "768kHz", "1.536MHz", "2.048MHz", "2.4576MHz", "3.072MHz", "6.144MHz",
+};
+
+static const struct soc_enum cs48l32_in_dmic_osr[] = {
+ SOC_ENUM_SINGLE(CS48L32_INPUT1_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+ SOC_ENUM_SINGLE(CS48L32_INPUT2_CONTROL1,
+ CS48L32_IN1_OSR_SHIFT,
+ ARRAY_SIZE(cs48l32_in_dmic_osr_text),
+ cs48l32_in_dmic_osr_text),
+};
+
+static bool cs48l32_is_input_enabled(struct snd_soc_component *component,
+ unsigned int reg)
+{
+ unsigned int input_active;
+
+ input_active = snd_soc_component_read(component, CS48L32_INPUT_CONTROL);
+ switch (reg) {
+ case CS48L32_IN1L_CONTROL1:
+ return input_active & BIT(CS48L32_IN1L_EN_SHIFT);
+ case CS48L32_IN1R_CONTROL1:
+ return input_active & BIT(CS48L32_IN1R_EN_SHIFT);
+ case CS48L32_IN2L_CONTROL1:
+ return input_active & BIT(CS48L32_IN2L_EN_SHIFT);
+ case CS48L32_IN2R_CONTROL1:
+ return input_active & BIT(CS48L32_IN2R_EN_SHIFT);
+ default:
+ return false;
+ }
+}
+
+static int cs48l32_in_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, e->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static const struct soc_enum cs48l32_input_rate[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2L_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_IN2R_CONTROL1,
+ CS48L32_INx_RATE_SHIFT,
+ CS48L32_INx_RATE_MASK >> CS48L32_INx_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static int cs48l32_low_power_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value;
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ /* Cannot change rate on an active input */
+ if (cs48l32_is_input_enabled(component, mc->reg)) {
+ ret = -EBUSY;
+ goto exit;
+ }
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+exit:
+ snd_soc_dapm_mutex_unlock(dapm);
+ return ret;
+}
+
+static const struct soc_enum noise_gen_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_RATE_SHIFT,
+ CS48L32_NOISE_GEN_RATE_MASK >> CS48L32_NOISE_GEN_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_auxpdm_freq_texts[] = {
+ "3.072MHz", "2.048MHz", "1.536MHz", "768kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_freq,
+ CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_freq,
+ CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_FREQ_SHIFT,
+ cs48l32_auxpdm_freq_texts);
+
+static const char * const cs48l32_auxpdm_src_texts[] = {
+ "Analog", "IN1 Digital", "IN2 Digital",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm1_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT1_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static SOC_ENUM_SINGLE_DECL(cs48l32_auxpdm2_in,
+ CS48L32_AUXPDM_CTRL2,
+ CS48L32_AUXPDMDAT2_SRC_SHIFT,
+ cs48l32_auxpdm_src_texts);
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Input", cs48l32_auxpdm1_in),
+ SOC_DAPM_ENUM("AUXPDM2 Input", cs48l32_auxpdm2_in),
+};
+
+static const unsigned int cs48l32_auxpdm_analog_in_val[] = {
+ 0x0, 0x1,
+};
+
+static const struct soc_enum cs48l32_auxpdm_analog_inmux_enum[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM1_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_AUXPDM2_CONTROL1,
+ CS48L32_AUXPDM1_SRC_SHIFT,
+ CS48L32_AUXPDM1_SRC_MASK >> CS48L32_AUXPDM1_SRC_SHIFT,
+ ARRAY_SIZE(cs48l32_auxpdm_analog_in_val),
+ cs48l32_in_texts,
+ cs48l32_auxpdm_analog_in_val),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_analog_inmux[] = {
+ SOC_DAPM_ENUM("AUXPDM1 Analog Input", cs48l32_auxpdm_analog_inmux_enum[0]),
+ SOC_DAPM_ENUM("AUXPDM2 Analog Input", cs48l32_auxpdm_analog_inmux_enum[1]),
+};
+
+static const struct snd_kcontrol_new cs48l32_auxpdm_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct soc_enum cs48l32_isrc_fsh[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSH_SHIFT,
+ CS48L32_ISRC1_FSH_MASK >> CS48L32_ISRC1_FSH_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_isrc_fsl[] = {
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC1_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC2_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(CS48L32_ISRC3_CONTROL1,
+ CS48L32_ISRC1_FSL_SHIFT,
+ CS48L32_ISRC1_FSL_MASK >> CS48L32_ISRC1_FSL_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val),
+};
+
+static const struct soc_enum cs48l32_fx_rate =
+ SOC_VALUE_ENUM_SINGLE(CS48L32_FX_SAMPLE_RATE,
+ CS48L32_FX_RATE_SHIFT,
+ CS48L32_FX_RATE_MASK >> CS48L32_FX_RATE_SHIFT,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text,
+ cs48l32_rate_val);
+
+static const char * const cs48l32_lhpf_mode_text[] = {
+ "Low-pass", "High-pass"
+};
+
+static const struct soc_enum cs48l32_lhpf_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_LHPF_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_lhpf_mode_text), cs48l32_lhpf_mode_text),
+};
+
+static int cs48l32_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ __be32 *data = (__be32 *)ucontrol->value.bytes.data;
+ s16 val = (s16)be32_to_cpu(*data);
+
+ if (abs(val) > CS48L32_LHPF_MAX_COEFF) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable LHPF coefficients\n");
+ return -EINVAL;
+ }
+
+ return snd_soc_bytes_put(kcontrol, ucontrol);
+}
+
+static const char * const cs48l32_eq_mode_text[] = {
+ "Low-pass", "High-pass",
+};
+
+static const struct soc_enum cs48l32_eq_mode[] = {
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 0,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 1,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 2,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+ SOC_ENUM_SINGLE(CS48L32_EQ_CONTROL2, 3,
+ ARRAY_SIZE(cs48l32_eq_mode_text),
+ cs48l32_eq_mode_text),
+};
+
+static int cs48l32_eq_mode_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int item;
+
+ item = snd_soc_enum_val_to_item(e, cs48l32_codec->eq_mode[e->shift_l]);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_eq_mode_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ unsigned int val;
+ bool changed = false;
+
+ if (item[0] >= e->items)
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->eq_mode[e->shift_l] != val) {
+ cs48l32_codec->eq_mode[e->shift_l] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static int cs48l32_eq_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct cs48l32_eq_control *ctl = (void *) kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = ctl->max;
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* High __be16 is in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ ucontrol->value.integer.value[0] = be16_to_cpu(coeffs[coeff_idx]);
+
+ return 0;
+}
+
+static int cs48l32_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_eq_control *params = (void *)kcontrol->private_value;
+ __be16 *coeffs;
+ unsigned int coeff_idx;
+ int block_idx;
+
+ block_idx = ((int) params->block_base - (int) CS48L32_EQ1_BAND1_COEFF1);
+ block_idx /= (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ coeffs = &cs48l32_codec->eq_coefficients[block_idx][0];
+ coeff_idx = (params->reg - params->block_base) / 2;
+
+ /* Put high __be16 in [coeff_idx] and low __be16 in [coeff_idx + 1] */
+ if (params->shift == 0)
+ coeff_idx++;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ coeffs[coeff_idx] = cpu_to_be16(ucontrol->value.integer.value[0]);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new cs48l32_drc_activity_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new cs48l32_dsp_trigger_output_mux[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+};
+
+static int cs48l32_dsp_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ unsigned int cached_rate;
+ const unsigned int rate_num = e->mask;
+ int item;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ cached_rate = cs48l32_codec->dsp_dma_rates[rate_num];
+ item = snd_soc_enum_val_to_item(e, cached_rate);
+ ucontrol->value.enumerated.item[0] = item;
+
+ return 0;
+}
+
+static int cs48l32_dsp_rate_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *) kcontrol->private_value;
+ const unsigned int rate_num = e->mask;
+ const unsigned int item = ucontrol->value.enumerated.item[0];
+ unsigned int val;
+ bool changed = false;
+
+ if (item >= e->items)
+ return -EINVAL;
+
+ if (rate_num >= ARRAY_SIZE(cs48l32_codec->dsp_dma_rates))
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item);
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (cs48l32_codec->dsp_dma_rates[rate_num] != val) {
+ cs48l32_codec->dsp_dma_rates[rate_num] = val;
+ changed = true;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return changed;
+}
+
+static const struct soc_enum cs48l32_dsp_rate_enum[] = {
+ /* RX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 0,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 1,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 2,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 3,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 4,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 5,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 6,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 7,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ /* TX rates */
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 8,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 9,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 10,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 11,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 12,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 13,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 14,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+ SOC_VALUE_ENUM_SINGLE(SND_SOC_NOPM, 0,
+ 15,
+ ARRAY_SIZE(cs48l32_rate_text),
+ cs48l32_rate_text, cs48l32_rate_val),
+};
+
+static int cs48l32_dsp_pre_run(struct wm_adsp *dsp)
+{
+ struct cs48l32_codec *cs48l32_codec = container_of(dsp, struct cs48l32_codec, dsp);
+ unsigned int reg;
+ const u8 *rate = cs48l32_codec->dsp_dma_rates;
+ int i;
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_RX1;
+ for (i = 0; i < CS48L32_DSP_N_RX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ reg = dsp->cs_dsp.base + CS48L32_HALO_SAMPLE_RATE_TX1;
+ for (i = 0; i < CS48L32_DSP_N_TX_CHANNELS; ++i) {
+ regmap_update_bits(dsp->cs_dsp.regmap, reg, CS48L32_HALO_DSP_RATE_MASK, *rate);
+ reg += 8;
+ rate++;
+ }
+
+ usleep_range(300, 600);
+
+ return 0;
+}
+
+static void cs48l32_dsp_memory_disable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0);
+ if (ret)
+ goto err;
+ }
+
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0);
+ if (ret)
+ goto err;
+ }
+ }
+
+ return;
+
+err:
+ dev_warn(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+}
+
+static int cs48l32_dsp_memory_enable(struct cs48l32_codec *cs48l32_codec,
+ const struct cs48l32_dsp_power_regs *regs)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int i, j, ret;
+
+ /* disable power-off */
+ for (i = 0; i < regs->n_ext; ++i) {
+ for (j = regs->ext[i].start; j <= regs->ext[i].end; j += 4) {
+ ret = regmap_write(regmap, j, 0x3);
+ if (ret)
+ goto err;
+ }
+ }
+
+ /* power-up the banks in sequence */
+ for (i = 0; i < regs->n_pwd; ++i) {
+ ret = regmap_write(regmap, regs->pwd[i], 0x1);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+
+ ret = regmap_write(regmap, regs->pwd[i], 0x3);
+ if (ret)
+ goto err;
+
+ udelay(1); /* allow bank to power-up */
+ }
+
+ return 0;
+
+err:
+ dev_err(cs48l32_codec->core.dev, "Failed to write SRAM enables (%d)\n", ret);
+ cs48l32_dsp_memory_disable(cs48l32_codec, regs);
+
+ return ret;
+}
+
+static int cs48l32_dsp_freq_update(struct snd_soc_dapm_widget *w, unsigned int freq_reg,
+ unsigned int freqsel_reg)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ struct wm_adsp *dsp = &cs48l32_codec->dsp;
+ int ret;
+ unsigned int freq, freq_sel, freq_sts;
+
+ if (!freq_reg)
+ return -EINVAL;
+
+ ret = regmap_read(regmap, freq_reg, &freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freq_reg, ret);
+ return ret;
+ }
+
+ if (freqsel_reg) {
+ freq_sts = (freq & CS48L32_SYSCLK_FREQ_STS_MASK) >> CS48L32_SYSCLK_FREQ_STS_SHIFT;
+
+ ret = regmap_read(regmap, freqsel_reg, &freq_sel);
+ if (ret) {
+ dev_err(component->dev, "Failed to read #%x: %d\n", freqsel_reg, ret);
+ return ret;
+ }
+ freq_sel = (freq_sel & CS48L32_SYSCLK_FREQ_MASK) >> CS48L32_SYSCLK_FREQ_SHIFT;
+
+ if (freq_sts != freq_sel) {
+ dev_err(component->dev, "SYSCLK FREQ (#%x) != FREQ STS (#%x)\n",
+ freq_sel, freq_sts);
+ return -ETIMEDOUT;
+ }
+ }
+
+ freq &= CS48L32_DSP_CLK_FREQ_MASK;
+ freq >>= CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ ret = regmap_write(dsp->cs_dsp.regmap,
+ dsp->cs_dsp.base + CS48L32_DSP_CLOCK_FREQ_OFFS, freq);
+ if (ret) {
+ dev_err(component->dev, "Failed to set HALO clock freq: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs48l32_dsp_freq_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_freq_update(w, CS48L32_SYSTEM_CLOCK2, CS48L32_SYSTEM_CLOCK1);
+ default:
+ return 0;
+ }
+}
+
+static irqreturn_t cs48l32_irq(int irq, void *data)
+{
+ static const unsigned int eint1_regs[] = {
+ CS48L32_IRQ1_EINT_9, CS48L32_IRQ1_MASK_9,
+ CS48L32_IRQ1_EINT_7, CS48L32_IRQ1_MASK_7
+ };
+ u32 reg_vals[4];
+ struct cs48l32_codec *cs48l32_codec = data;
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ irqreturn_t result = IRQ_NONE;
+ unsigned int eint_pending;
+ int i, ret;
+
+ static_assert(ARRAY_SIZE(eint1_regs) == ARRAY_SIZE(reg_vals));
+
+ ret = pm_runtime_resume_and_get(cs48l32_codec->core.dev);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "irq could not get pm runtime: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ ret = regmap_read(regmap, CS48L32_IRQ1_STATUS, &eint_pending);
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ1_STATUS failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+ if ((eint_pending & CS48L32_IRQ1_STS_MASK) == 0)
+ goto out;
+
+ ret = regmap_multi_reg_read(regmap, eint1_regs, reg_vals, ARRAY_SIZE(reg_vals));
+ if (ret) {
+ dev_warn(cs48l32_codec->core.dev, "Read IRQ regs failed: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(reg_vals); i += 2) {
+ reg_vals[i] &= ~reg_vals[i + 1];
+ regmap_write(regmap, eint1_regs[i], reg_vals[i]);
+ }
+
+ if (reg_vals[0] & CS48L32_DSP1_IRQ0_EINT1_MASK)
+ wm_adsp_compr_handle_irq(&cs48l32_codec->dsp);
+
+ if (reg_vals[2] & CS48L32_DSP1_MPU_ERR_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "MPU err IRQ\n");
+ wm_halo_bus_error(irq, &cs48l32_codec->dsp);
+ }
+
+ if (reg_vals[2] & CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK) {
+ dev_warn(cs48l32_codec->core.dev, "WDT expire IRQ\n");
+ wm_halo_wdt_expire(irq, &cs48l32_codec->dsp);
+ }
+
+ result = IRQ_HANDLED;
+
+out:
+ pm_runtime_put_autosuspend(cs48l32_codec->core.dev);
+
+ return result;
+}
+
+static int cs48l32_get_dspclk_setting(struct cs48l32_codec *cs48l32_codec, unsigned int freq,
+ int src, unsigned int *val)
+{
+ freq /= 15625; /* convert to 1/64ths of 1MHz */
+ *val |= freq << CS48L32_DSP_CLK_FREQ_SHIFT;
+
+ return 0;
+}
+
+static int cs48l32_get_sysclk_setting(unsigned int freq)
+{
+ switch (freq) {
+ case 0:
+ case 5644800:
+ case 6144000:
+ return CS48L32_SYSCLK_RATE_6MHZ;
+ case 11289600:
+ case 12288000:
+ return CS48L32_SYSCLK_RATE_12MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 22579200:
+ case 24576000:
+ return CS48L32_SYSCLK_RATE_24MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 45158400:
+ case 49152000:
+ return CS48L32_SYSCLK_RATE_49MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ case 90316800:
+ case 98304000:
+ return CS48L32_SYSCLK_RATE_98MHZ << CS48L32_SYSCLK_FREQ_SHIFT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs48l32_set_pdm_fllclk(struct snd_soc_component *component, int source)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val;
+
+ switch (source) {
+ case CS48L32_PDMCLK_SRC_IN1_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN2_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN3_PDMCLK:
+ case CS48L32_PDMCLK_SRC_IN4_PDMCLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM1_CLK:
+ case CS48L32_PDMCLK_SRC_AUXPDM2_CLK:
+ val = source << CS48L32_PDM_FLLCLK_SRC_SHIFT;
+ break;
+ default:
+ dev_err(cs48l32_codec->core.dev, "Invalid PDM FLLCLK src %d\n", source);
+ return -EINVAL;
+ }
+
+ return regmap_update_bits(regmap, CS48L32_INPUT_CONTROL2,
+ CS48L32_PDM_FLLCLK_SRC_MASK, val);
+}
+
+static int cs48l32_set_sysclk(struct snd_soc_component *component, int clk_id, int source,
+ unsigned int freq, int dir)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ char *name;
+ unsigned int reg;
+ unsigned int mask = CS48L32_SYSCLK_SRC_MASK;
+ unsigned int val = source << CS48L32_SYSCLK_SRC_SHIFT;
+ int clk_freq_sel, *clk;
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ name = "SYSCLK";
+ reg = CS48L32_SYSTEM_CLOCK1;
+ clk = &cs48l32_codec->sysclk;
+ clk_freq_sel = cs48l32_get_sysclk_setting(freq);
+ mask |= CS48L32_SYSCLK_FREQ_MASK | CS48L32_SYSCLK_FRAC_MASK;
+ break;
+ case CS48L32_CLK_DSPCLK:
+ name = "DSPCLK";
+ reg = CS48L32_DSP_CLOCK1;
+ clk = &cs48l32_codec->dspclk;
+ clk_freq_sel = cs48l32_get_dspclk_setting(cs48l32_codec, freq, source, &val);
+ mask |= CS48L32_DSP_CLK_FREQ_MASK;
+ break;
+ case CS48L32_CLK_PDM_FLLCLK:
+ return cs48l32_set_pdm_fllclk(component, source);
+ default:
+ return -EINVAL;
+ }
+
+ if (clk_freq_sel < 0) {
+ dev_err(cs48l32_codec->core.dev, "Failed to get %s setting for %dHZ\n", name, freq);
+ return clk_freq_sel;
+ }
+
+ *clk = freq;
+
+ if (freq == 0) {
+ dev_dbg(cs48l32_codec->core.dev, "%s cleared\n", name);
+ return 0;
+ }
+
+ val |= clk_freq_sel;
+
+ if (freq % 6144000)
+ val |= CS48L32_SYSCLK_FRAC_MASK;
+
+ dev_dbg(cs48l32_codec->core.dev, "%s set to %uHz", name, freq);
+
+ return regmap_update_bits(regmap, reg, mask, val);
+}
+
+static int cs48l32_is_enabled_fll(struct cs48l32_fll *fll, int base)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(regmap, base + CS48L32_FLL_CONTROL1_OFFS, &reg);
+ if (ret != 0) {
+ cs48l32_fll_err(fll, "Failed to read current state: %d\n", ret);
+ return ret;
+ }
+
+ return reg & CS48L32_FLL_EN_MASK;
+}
+
+static int cs48l32_wait_for_fll(struct cs48l32_fll *fll, bool requested)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ unsigned int val = 0;
+ int i;
+
+ cs48l32_fll_dbg(fll, "Waiting for FLL...\n");
+
+ for (i = 0; i < 30; i++) {
+ regmap_read(regmap, fll->sts_addr, &val);
+ if (!!(val & fll->sts_mask) == requested)
+ return 0;
+
+ switch (i) {
+ case 0 ... 5:
+ usleep_range(75, 125);
+ break;
+ case 6 ... 20:
+ usleep_range(750, 1250);
+ break;
+ default:
+ fsleep(20000);
+ break;
+ }
+ }
+
+ cs48l32_fll_warn(fll, "Timed out waiting for %s\n", requested ? "lock" : "unlock");
+
+ return -ETIMEDOUT;
+}
+
+static int cs48l32_fllhj_disable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ bool change;
+
+ cs48l32_fll_dbg(fll, "Disabling FLL\n");
+
+ /*
+ * Disable lockdet, but don't set ctrl_upd update bit. This allows the
+ * lock status bit to clear as normal, but should the FLL be enabled
+ * again due to a control clock being required, the lock won't re-assert
+ * as the FLL config registers are automatically applied when the FLL
+ * enables.
+ */
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL5_OFFS,
+ CS48L32_FLL_FRC_INTEG_UPD_MASK);
+ regmap_update_bits_check(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK,
+ 0,
+ &change);
+
+ cs48l32_wait_for_fll(fll, false);
+
+ /*
+ * ctrl_up gates the writes to all the fll's registers, setting it to 0
+ * here ensures that after a runtime suspend/resume cycle when one
+ * enables the fll then ctrl_up is the last bit that is configured
+ * by the fll enable code rather than the cache sync operation which
+ * would have updated it much earlier before writing out all fll
+ * registers
+ */
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ if (change)
+ pm_runtime_put_autosuspend(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_apply(struct cs48l32_fll *fll, int fin)
+{
+ struct regmap *regmap = fll->codec->core.regmap;
+ int refdiv, fref, fout, lockdet_thr, fbdiv, fllgcd;
+ bool frac = false;
+ unsigned int fll_n, min_n, max_n, ratio, theta, lambda, hp;
+ unsigned int gains, num;
+
+ cs48l32_fll_dbg(fll, "fin=%d, fout=%d\n", fin, fll->fout);
+
+ for (refdiv = 0; refdiv < 4; refdiv++) {
+ if ((fin / (1 << refdiv)) <= CS48L32_FLLHJ_MAX_THRESH)
+ break;
+ }
+
+ fref = fin / (1 << refdiv);
+ fout = fll->fout;
+ frac = fout % fref;
+
+ /*
+ * Use simple heuristic approach to find a configuration that
+ * should work for most input clocks.
+ */
+ if (fref < CS48L32_FLLHJ_LOW_THRESH) {
+ lockdet_thr = 2;
+ gains = CS48L32_FLLHJ_LOW_GAINS;
+
+ if (frac)
+ fbdiv = 256;
+ else
+ fbdiv = 4;
+ } else if (fref < CS48L32_FLLHJ_MID_THRESH) {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_MID_GAINS;
+ fbdiv = (frac) ? 16 : 2;
+ } else {
+ lockdet_thr = 8;
+ gains = CS48L32_FLLHJ_HIGH_GAINS;
+ fbdiv = 1;
+ }
+ /* Use high performance mode for fractional configurations. */
+ if (frac) {
+ hp = 3;
+ min_n = CS48L32_FLLHJ_FRAC_MIN_N;
+ max_n = CS48L32_FLLHJ_FRAC_MAX_N;
+ } else {
+ if (fref < CS48L32_FLLHJ_LP_INT_MODE_THRESH)
+ hp = 0;
+ else
+ hp = 1;
+
+ min_n = CS48L32_FLLHJ_INT_MIN_N;
+ max_n = CS48L32_FLLHJ_INT_MAX_N;
+ }
+
+ ratio = fout / fref;
+
+ cs48l32_fll_dbg(fll, "refdiv=%d, fref=%d, frac:%d\n", refdiv, fref, frac);
+
+ while (ratio / fbdiv < min_n) {
+ fbdiv /= 2;
+ if (fbdiv < min_n) {
+ cs48l32_fll_err(fll, "FBDIV (%u) < minimum N (%u)\n", fbdiv, min_n);
+ return -EINVAL;
+ }
+ }
+ while (frac && (ratio / fbdiv > max_n)) {
+ fbdiv *= 2;
+ if (fbdiv >= 1024) {
+ cs48l32_fll_err(fll, "FBDIV (%u) >= 1024\n", fbdiv);
+ return -EINVAL;
+ }
+ }
+
+ cs48l32_fll_dbg(fll, "lockdet=%d, hp=#%x, fbdiv:%d\n", lockdet_thr, hp, fbdiv);
+
+ /* Calculate N.K values */
+ fllgcd = gcd(fout, fbdiv * fref);
+ num = fout / fllgcd;
+ lambda = (fref * fbdiv) / fllgcd;
+ fll_n = num / lambda;
+ theta = num % lambda;
+
+ cs48l32_fll_dbg(fll, "fll_n=%d, gcd=%d, theta=%d, lambda=%d\n",
+ fll_n, fllgcd, theta, lambda);
+
+ /* Some sanity checks before any registers are written. */
+ if (fll_n < min_n || fll_n > max_n) {
+ cs48l32_fll_err(fll, "N not in valid %s mode range %d-%d: %d\n",
+ frac ? "fractional" : "integer", min_n, max_n, fll_n);
+ return -EINVAL;
+ }
+ if (fbdiv < 1 || (frac && fbdiv >= 1024) || (!frac && fbdiv >= 256)) {
+ cs48l32_fll_err(fll, "Invalid fbdiv for %s mode (%u)\n",
+ frac ? "fractional" : "integer", fbdiv);
+ return -EINVAL;
+ }
+
+ /* clear the ctrl_upd bit to guarantee we write to it later. */
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_THR_MASK |
+ CS48L32_FLL_PHASEDET_MASK |
+ CS48L32_FLL_REFCLK_DIV_MASK |
+ CS48L32_FLL_N_MASK |
+ CS48L32_FLL_CTRL_UPD_MASK,
+ (lockdet_thr << CS48L32_FLL_LOCKDET_THR_SHIFT) |
+ (1 << CS48L32_FLL_PHASEDET_SHIFT) |
+ (refdiv << CS48L32_FLL_REFCLK_DIV_SHIFT) |
+ (fll_n << CS48L32_FLL_N_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL3_OFFS,
+ CS48L32_FLL_LAMBDA_MASK |
+ CS48L32_FLL_THETA_MASK,
+ (lambda << CS48L32_FLL_LAMBDA_SHIFT) |
+ (theta << CS48L32_FLL_THETA_SHIFT));
+
+ regmap_update_bits(regmap,
+ fll->base + CS48L32_FLL_CONTROL4_OFFS,
+ (0xffff << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ CS48L32_FLL_HP_MASK |
+ CS48L32_FLL_FB_DIV_MASK,
+ (gains << CS48L32_FLL_FD_GAIN_COARSE_SHIFT) |
+ (hp << CS48L32_FLL_HP_SHIFT) |
+ (fbdiv << CS48L32_FLL_FB_DIV_SHIFT));
+
+ return 0;
+}
+
+static int cs48l32_fllhj_enable(struct cs48l32_fll *fll)
+{
+ struct cs48l32 *cs48l32 = &fll->codec->core;
+ int already_enabled = cs48l32_is_enabled_fll(fll, fll->base);
+ int ret;
+
+ if (already_enabled < 0)
+ return already_enabled;
+
+ if (!already_enabled)
+ pm_runtime_get_sync(cs48l32->dev);
+
+ cs48l32_fll_dbg(fll, "Enabling FLL, initially %s\n",
+ str_enabled_disabled(already_enabled));
+
+ /* FLLn_HOLD must be set before configuring any registers */
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ /* Apply refclk */
+ ret = cs48l32_fllhj_apply(fll, fll->ref_freq);
+ if (ret) {
+ cs48l32_fll_err(fll, "Failed to set FLL: %d\n", ret);
+ goto out;
+ }
+ regmap_update_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_REFCLK_SRC_MASK,
+ fll->ref_src << CS48L32_FLL_REFCLK_SRC_SHIFT);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_EN_MASK);
+
+out:
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL2_OFFS,
+ CS48L32_FLL_LOCKDET_MASK);
+
+ regmap_set_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_CTRL_UPD_MASK);
+
+ /* Release the hold so that flln locks to external frequency */
+ regmap_clear_bits(cs48l32->regmap,
+ fll->base + CS48L32_FLL_CONTROL1_OFFS,
+ CS48L32_FLL_HOLD_MASK);
+
+ if (!already_enabled)
+ cs48l32_wait_for_fll(fll, true);
+
+ return 0;
+}
+
+static int cs48l32_fllhj_validate(struct cs48l32_fll *fll,
+ unsigned int ref_in,
+ unsigned int fout)
+{
+ if (fout && !ref_in) {
+ cs48l32_fll_err(fll, "fllout set without valid input clk\n");
+ return -EINVAL;
+ }
+
+ if (fll->fout && fout != fll->fout) {
+ cs48l32_fll_err(fll, "Can't change output on active FLL\n");
+ return -EINVAL;
+ }
+
+ if (ref_in / CS48L32_FLL_MAX_REFDIV > CS48L32_FLLHJ_MAX_THRESH) {
+ cs48l32_fll_err(fll, "Can't scale %dMHz to <=13MHz\n", ref_in);
+ return -EINVAL;
+ }
+
+ if (fout > CS48L32_FLL_MAX_FOUT) {
+ cs48l32_fll_err(fll, "Fout=%dMHz exceeds maximum %dMHz\n",
+ fout, CS48L32_FLL_MAX_FOUT);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int cs48l32_fllhj_set_refclk(struct cs48l32_fll *fll, int source,
+ unsigned int fin, unsigned int fout)
+{
+ int ret = 0;
+
+ if (fll->ref_src == source && fll->ref_freq == fin && fll->fout == fout)
+ return 0;
+
+ if (fin && fout && cs48l32_fllhj_validate(fll, fin, fout))
+ return -EINVAL;
+
+ fll->ref_src = source;
+ fll->ref_freq = fin;
+ fll->fout = fout;
+
+ if (fout)
+ ret = cs48l32_fllhj_enable(fll);
+ else
+ cs48l32_fllhj_disable(fll);
+
+ return ret;
+}
+
+static int cs48l32_init_fll(struct cs48l32_fll *fll)
+{
+ fll->ref_src = CS48L32_FLL_SRC_NONE;
+
+ return 0;
+}
+
+static int cs48l32_set_fll(struct snd_soc_component *component, int fll_id,
+ int source, unsigned int fref, unsigned int fout)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (fll_id) {
+ case CS48L32_FLL1_REFCLK:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return cs48l32_fllhj_set_refclk(&cs48l32_codec->fll, source, fref, fout);
+}
+
+static int cs48l32_asp_dai_probe(struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int pin_reg, last_pin_reg, hiz_reg;
+
+ switch (dai->id) {
+ case 1:
+ pin_reg = CS48L32_GPIO3_CTRL1;
+ hiz_reg = CS48L32_ASP1_CONTROL3;
+ break;
+ case 2:
+ pin_reg = CS48L32_GPIO7_CTRL1;
+ hiz_reg = CS48L32_ASP2_CONTROL3;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ for (last_pin_reg = pin_reg + 12; pin_reg <= last_pin_reg; ++pin_reg)
+ regmap_clear_bits(regmap, pin_reg, CS48L32_GPIOX_CTRL1_FN_MASK);
+
+ /* DOUT high-impendance when not transmitting */
+ regmap_set_bits(regmap, hiz_reg, CS48L32_ASP_DOUT_HIZ_MASK);
+
+ return 0;
+}
+
+static int cs48l32_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int val = 0U;
+ unsigned int base = dai->driver->base;
+ unsigned int mask = CS48L32_ASP_FMT_MASK | CS48L32_ASP_BCLK_INV_MASK |
+ CS48L32_ASP_BCLK_MSTR_MASK |
+ CS48L32_ASP_FSYNC_INV_MASK |
+ CS48L32_ASP_FSYNC_MSTR_MASK;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val |= (CS48L32_ASP_FMT_DSP_MODE_A << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "DSP_B cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_DSP_MODE_B << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val |= (CS48L32_ASP_FMT_I2S_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_BP_FP) {
+ cs48l32_asp_err(dai, "LEFT_J cannot be clock consumer\n");
+ return -EINVAL;
+ }
+ val |= (CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE << CS48L32_ASP_FMT_SHIFT);
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported DAI format %d\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ case SND_SOC_DAIFMT_BC_FP:
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FC:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ break;
+ case SND_SOC_DAIFMT_BP_FP:
+ val |= CS48L32_ASP_BCLK_MSTR_MASK;
+ val |= CS48L32_ASP_FSYNC_MSTR_MASK;
+ break;
+ default:
+ cs48l32_asp_err(dai, "Unsupported clock direction %d\n",
+ fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= CS48L32_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= CS48L32_ASP_FSYNC_INV_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, base + CS48L32_ASP_CONTROL2, mask, val);
+
+ return 0;
+}
+
+static const struct {
+ u32 freq;
+ u32 id;
+} cs48l32_sclk_rates[] = {
+ { 128000, 12 },
+ { 176400, 13 },
+ { 192000, 14 },
+ { 256000, 15 },
+ { 352800, 16 },
+ { 384000, 17 },
+ { 512000, 18 },
+ { 705600, 19 },
+ { 768000, 21 },
+ { 1024000, 23 },
+ { 1411200, 25 },
+ { 1536000, 27 },
+ { 2048000, 29 },
+ { 2822400, 31 },
+ { 3072000, 33 },
+ { 4096000, 36 },
+ { 5644800, 38 },
+ { 6144000, 40 },
+ { 8192000, 47 },
+ { 11289600, 49 },
+ { 12288000, 51 },
+ { 22579200, 57 },
+ { 24576000, 59 },
+};
+
+#define CS48L32_48K_RATE_MASK 0x0e00fe
+#define CS48L32_44K1_RATE_MASK 0x00fe00
+#define CS48L32_RATE_MASK (CS48L32_48K_RATE_MASK | CS48L32_44K1_RATE_MASK)
+
+static const unsigned int cs48l32_sr_vals[] = {
+ 0,
+ 12000, /* CS48L32_48K_RATE_MASK */
+ 24000, /* CS48L32_48K_RATE_MASK */
+ 48000, /* CS48L32_48K_RATE_MASK */
+ 96000, /* CS48L32_48K_RATE_MASK */
+ 192000, /* CS48L32_48K_RATE_MASK */
+ 384000, /* CS48L32_48K_RATE_MASK */
+ 768000, /* CS48L32_48K_RATE_MASK */
+ 0,
+ 11025, /* CS48L32_44K1_RATE_MASK */
+ 22050, /* CS48L32_44K1_RATE_MASK */
+ 44100, /* CS48L32_44K1_RATE_MASK */
+ 88200, /* CS48L32_44K1_RATE_MASK */
+ 176400, /* CS48L32_44K1_RATE_MASK */
+ 352800, /* CS48L32_44K1_RATE_MASK */
+ 705600, /* CS48L32_44K1_RATE_MASK */
+ 0,
+ 8000, /* CS48L32_48K_RATE_MASK */
+ 16000, /* CS48L32_48K_RATE_MASK */
+ 32000, /* CS48L32_48K_RATE_MASK */
+};
+
+static const struct snd_pcm_hw_constraint_list cs48l32_constraint = {
+ .count = ARRAY_SIZE(cs48l32_sr_vals),
+ .list = cs48l32_sr_vals,
+};
+
+static int cs48l32_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base_rate;
+
+ if (!substream->runtime)
+ return 0;
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ base_rate = cs48l32_codec->sysclk;
+ break;
+ default:
+ return 0;
+ }
+
+ if (base_rate == 0)
+ dai_priv->constraint.mask = CS48L32_RATE_MASK;
+ else if (base_rate % 4000)
+ dai_priv->constraint.mask = CS48L32_44K1_RATE_MASK;
+ else
+ dai_priv->constraint.mask = CS48L32_48K_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &dai_priv->constraint);
+}
+
+static int cs48l32_hw_params_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int sr_val, sr_reg, rate;
+
+ rate = params_rate(params);
+ for (sr_val = 0; sr_val < ARRAY_SIZE(cs48l32_sr_vals); sr_val++)
+ if (cs48l32_sr_vals[sr_val] == rate)
+ break;
+
+ if (sr_val == ARRAY_SIZE(cs48l32_sr_vals)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ switch (dai_priv->clk) {
+ case CS48L32_CLK_SYSCLK_1:
+ sr_reg = CS48L32_SAMPLE_RATE1;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ sr_reg = CS48L32_SAMPLE_RATE2;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ sr_reg = CS48L32_SAMPLE_RATE3;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ sr_reg = CS48L32_SAMPLE_RATE4;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, sr_reg, CS48L32_SAMPLE_RATE_1_MASK, sr_val);
+
+ return 0;
+}
+
+static bool cs48l32_asp_cfg_changed(struct snd_soc_component *component,
+ unsigned int base, unsigned int sclk,
+ unsigned int slotws, unsigned int dataw)
+{
+ unsigned int val;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL1);
+ if (sclk != (val & CS48L32_ASP_BCLK_FREQ_MASK))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ if (slotws != (val & (CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL1);
+ if (dataw != (val & (CS48L32_ASP_TX_WL_MASK)))
+ return true;
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_DATA_CONTROL5);
+ if (dataw != (val & (CS48L32_ASP_RX_WL_MASK)))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int base = dai->driver->base;
+ int dai_id = dai->id - 1;
+ unsigned int rate = params_rate(params);
+ unsigned int dataw = snd_pcm_format_width(params_format(params));
+ unsigned int asp_state = 0;
+ int sclk, sclk_target;
+ unsigned int slotw, n_slots, n_slots_multiple, val;
+ int i, ret;
+
+ cs48l32_asp_dbg(dai, "hwparams in: ch:%u dataw:%u rate:%u\n",
+ params_channels(params), dataw, rate);
+ /*
+ * The following calculations hold only under the assumption that
+ * symmetric_[rates|channels|samplebits] are set to 1
+ */
+ if (cs48l32_codec->tdm_slots[dai_id]) {
+ n_slots = cs48l32_codec->tdm_slots[dai_id];
+ slotw = cs48l32_codec->tdm_width[dai_id];
+ } else {
+ n_slots = params_channels(params);
+ slotw = dataw;
+ }
+
+ val = snd_soc_component_read(component, base + CS48L32_ASP_CONTROL2);
+ val = (val & CS48L32_ASP_FMT_MASK) >> CS48L32_ASP_FMT_SHIFT;
+ if (val == CS48L32_ASP_FMT_I2S_MODE)
+ n_slots_multiple = 2;
+ else
+ n_slots_multiple = 1;
+
+ sclk_target = snd_soc_tdm_params_to_bclk(params, slotw, n_slots, n_slots_multiple);
+ if (sclk_target < 0) {
+ cs48l32_asp_err(dai, "Invalid parameters\n");
+ return sclk_target;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_sclk_rates); i++) {
+ if ((cs48l32_sclk_rates[i].freq >= sclk_target) &&
+ (cs48l32_sclk_rates[i].freq % rate == 0)) {
+ sclk = cs48l32_sclk_rates[i].id;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(cs48l32_sclk_rates)) {
+ cs48l32_asp_err(dai, "Unsupported sample rate %dHz\n", rate);
+ return -EINVAL;
+ }
+
+ cs48l32_asp_dbg(dai, "hwparams out: n_slots:%u dataw:%u slotw:%u bclk:%u bclkid:%u\n",
+ n_slots, dataw, slotw, sclk_target, sclk);
+
+ slotw = (slotw << CS48L32_ASP_TX_WIDTH_SHIFT) |
+ (slotw << CS48L32_ASP_RX_WIDTH_SHIFT);
+
+ if (!cs48l32_asp_cfg_changed(component, base, sclk, slotw, dataw))
+ return cs48l32_hw_params_rate(substream, params, dai);
+
+ /* ASP must be disabled while changing configuration */
+ asp_state = snd_soc_component_read(component, base + CS48L32_ASP_ENABLES1);
+ regmap_clear_bits(regmap, base + CS48L32_ASP_ENABLES1, 0xff00ff);
+
+ ret = cs48l32_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ goto restore_asp;
+
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_BCLK_FREQ_MASK,
+ sclk);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_CONTROL2,
+ CS48L32_ASP_RX_WIDTH_MASK | CS48L32_ASP_TX_WIDTH_MASK,
+ slotw);
+ regmap_update_bits_async(regmap,
+ base + CS48L32_ASP_DATA_CONTROL1,
+ CS48L32_ASP_TX_WL_MASK,
+ dataw);
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_DATA_CONTROL5,
+ CS48L32_ASP_RX_WL_MASK,
+ dataw);
+
+restore_asp:
+ /* Restore ASP TX/RX enable state */
+ regmap_update_bits(regmap,
+ base + CS48L32_ASP_ENABLES1,
+ 0xff00ff,
+ asp_state);
+ return ret;
+}
+
+static const char *cs48l32_dai_clk_str(int clk_id)
+{
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ case CS48L32_CLK_SYSCLK_2:
+ case CS48L32_CLK_SYSCLK_3:
+ case CS48L32_CLK_SYSCLK_4:
+ return "SYSCLK";
+ default:
+ return "Unknown clock";
+ }
+}
+
+static int cs48l32_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[dai->id - 1];
+ unsigned int base = dai->driver->base;
+ unsigned int current_asp_rate, target_asp_rate;
+ bool change_rate_domain = false;
+ int ret;
+
+ if (clk_id == dai_priv->clk)
+ return 0;
+
+ if (snd_soc_dai_active(dai)) {
+ cs48l32_asp_err(dai, "Can't change clock on active DAI\n");
+ return -EBUSY;
+ }
+
+ switch (clk_id) {
+ case CS48L32_CLK_SYSCLK_1:
+ target_asp_rate = 0U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_2:
+ target_asp_rate = 1U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_3:
+ target_asp_rate = 2U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ case CS48L32_CLK_SYSCLK_4:
+ target_asp_rate = 3U << CS48L32_ASP_RATE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dai_priv->clk = clk_id;
+ cs48l32_asp_dbg(dai, "Setting to %s\n", cs48l32_dai_clk_str(clk_id));
+
+ if (base) {
+ ret = regmap_read(cs48l32_codec->core.regmap,
+ base + CS48L32_ASP_CONTROL1,
+ &current_asp_rate);
+ if (ret != 0) {
+ cs48l32_asp_err(dai, "Failed to check rate: %d\n", ret);
+ return ret;
+ }
+
+ if ((current_asp_rate & CS48L32_ASP_RATE_MASK) !=
+ (target_asp_rate & CS48L32_ASP_RATE_MASK)) {
+ change_rate_domain = true;
+
+ mutex_lock(&cs48l32_codec->rate_lock);
+ /* Guard the rate change with SYSCLK cycles */
+ cs48l32_spin_sysclk(cs48l32_codec);
+ }
+
+ snd_soc_component_update_bits(component, base + CS48L32_ASP_CONTROL1,
+ CS48L32_ASP_RATE_MASK, target_asp_rate);
+
+ if (change_rate_domain) {
+ cs48l32_spin_sysclk(cs48l32_codec);
+ mutex_unlock(&cs48l32_codec->rate_lock);
+ }
+ }
+
+ return 0;
+}
+
+static void cs48l32_set_channels_to_mask(struct snd_soc_dai *dai,
+ unsigned int base,
+ int channels, unsigned int mask)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ int slot, i, j = 0, shift;
+ unsigned int frame_ctls[2] = {0, 0};
+
+ for (i = 0; i < channels; ++i) {
+ slot = ffs(mask) - 1;
+ if (slot < 0)
+ return;
+
+ if (i - (j * 4) >= 4) {
+ ++j;
+ if (j >= 2)
+ break;
+ }
+
+ shift = (8 * (i - j * 4));
+
+ frame_ctls[j] |= slot << shift;
+
+ mask &= ~(1 << slot); /* ? mask ^= 1 << slot ? */
+ }
+
+ regmap_write(regmap, base, frame_ctls[0]);
+ regmap_write(regmap, base + 0x4, frame_ctls[1]);
+
+ if (mask)
+ cs48l32_asp_warn(dai, "Too many channels in TDM mask\n");
+}
+
+static int cs48l32_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int base = dai->driver->base;
+ int rx_max_chan = dai->driver->playback.channels_max;
+ int tx_max_chan = dai->driver->capture.channels_max;
+
+ /* Only support TDM for the physical ASPs */
+ if (dai->id > CS48L32_MAX_ASP)
+ return -EINVAL;
+
+ if (slots == 0) {
+ tx_mask = (1 << tx_max_chan) - 1;
+ rx_mask = (1 << rx_max_chan) - 1;
+ }
+
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL1,
+ tx_max_chan, tx_mask);
+ cs48l32_set_channels_to_mask(dai, base + CS48L32_ASP_FRAME_CONTROL5,
+ rx_max_chan, rx_mask);
+
+ cs48l32_codec->tdm_width[dai->id - 1] = slot_width;
+ cs48l32_codec->tdm_slots[dai->id - 1] = slots;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs48l32_dai_ops = {
+ .probe = &cs48l32_asp_dai_probe,
+ .startup = &cs48l32_startup,
+ .set_fmt = &cs48l32_set_fmt,
+ .set_tdm_slot = &cs48l32_set_tdm_slot,
+ .hw_params = &cs48l32_hw_params,
+ .set_sysclk = &cs48l32_dai_set_sysclk,
+};
+
+static int cs48l32_sysclk_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ cs48l32_spin_sysclk(cs48l32_codec);
+
+ return 0;
+}
+
+static int cs48l32_in_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ if (w->shift % 2)
+ reg = CS48L32_IN1L_CONTROL2;
+ else
+ reg = CS48L32_IN1R_CONTROL2;
+
+ reg += (w->shift / 2) * (CS48L32_IN2L_CONTROL2 - CS48L32_IN1L_CONTROL2);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK);
+ break;
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending++;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(200, 300);
+
+ switch (w->shift) {
+ case CS48L32_IN1L_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1L_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+ case CS48L32_IN1R_EN_SHIFT:
+ snd_soc_component_update_bits(component,
+ CS48L32_ADC1R_ANA_CONTROL1,
+ CS48L32_ADC1x_INT_ENA_FRC_MASK,
+ 0);
+ break;
+
+ default:
+ break;
+ }
+ cs48l32_codec->in_up_pending--;
+ snd_soc_component_update_bits(component, reg, CS48L32_INx_MUTE_MASK, 0);
+
+ /* Uncached write-only register, no need for update_bits */
+ if (!cs48l32_codec->in_up_pending) {
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_component_update_bits(component, reg,
+ CS48L32_INx_MUTE_MASK, CS48L32_INx_MUTE_MASK);
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg,
+ CS48L32_IN_VU_MASK);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs48l32_in_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Uncached write-only register, no need for update_bits.
+ * Will fail if codec is off but that will be handled by cs48l32_in_ev
+ */
+ snd_soc_component_write(component, cs48l32_codec->in_vu_reg, CS48L32_IN_VU);
+
+ return ret;
+}
+
+static bool cs48l32_eq_filter_unstable(bool mode, __be16 in_a, __be16 in_b)
+{
+ s16 a = be16_to_cpu(in_a);
+ s16 b = be16_to_cpu(in_b);
+
+ if (!mode)
+ return abs(a) > CS48L32_EQ_MAX_COEFF;
+
+ if (abs(b) > CS48L32_EQ_MAX_COEFF)
+ return true;
+
+ if (abs((a << 16) / (CS48L32_EQ_MAX_COEFF + 1 - b)) >= ((CS48L32_EQ_MAX_COEFF + 1) << 4))
+ return true;
+
+ return false;
+}
+
+static int cs48l32_eq_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int mode = cs48l32_codec->eq_mode[w->shift];
+ unsigned int reg;
+ __be16 *data = &cs48l32_codec->eq_coefficients[w->shift][0];
+ int ret = 0;
+
+ reg = CS48L32_EQ1_BAND1_COEFF1;
+ reg += w->shift * (CS48L32_EQ2_BAND1_COEFF1 - CS48L32_EQ1_BAND1_COEFF1);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs48l32_eq_filter_unstable(!!mode, data[1], data[0]) ||
+ cs48l32_eq_filter_unstable(true, data[7], data[6]) ||
+ cs48l32_eq_filter_unstable(true, data[13], data[12]) ||
+ cs48l32_eq_filter_unstable(true, data[19], data[18]) ||
+ cs48l32_eq_filter_unstable(false, data[25], data[24])) {
+ dev_err(cs48l32_codec->core.dev, "Rejecting unstable EQ coefficients.\n");
+ ret = -EINVAL;
+ } else {
+ ret = regmap_raw_write(regmap, reg, data, CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ coefficients: %d\n", ret);
+ goto out;
+ }
+
+ ret = snd_soc_component_update_bits(component,
+ CS48L32_EQ_CONTROL2,
+ w->mask,
+ mode << w->shift);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error writing EQ mode: %d\n", ret);
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+out:
+ return ret;
+}
+
+static const struct snd_kcontrol_new cs48l32_snd_controls[] = {
+SOC_ENUM("IN1 OSR", cs48l32_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", cs48l32_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_PGA_VOL_SHIFT, 0x40, 0x5f, 0, cs48l32_ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", cs48l32_in_hpf_cut_enum),
+
+SOC_SINGLE_EXT("IN1L LP Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+SOC_SINGLE_EXT("IN1R LP Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_LP_MODE_SHIFT,
+ 1, 0, snd_soc_get_volsw, cs48l32_low_power_mode_put),
+
+SOC_SINGLE("IN1L HPF Switch", CS48L32_IN1L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", CS48L32_IN1R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2L HPF Switch", CS48L32_IN2L_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2R HPF Switch", CS48L32_IN2R_CONTROL1, CS48L32_INx_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_EXT_TLV("IN1L Digital Volume", CS48L32_IN1L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN1R Digital Volume", CS48L32_IN1R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2L Digital Volume", CS48L32_IN2L_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+SOC_SINGLE_EXT_TLV("IN2R Digital Volume", CS48L32_IN2R_CONTROL2,
+ CS48L32_INx_VOL_SHIFT, 0xbf, 0, snd_soc_get_volsw,
+ cs48l32_in_put_volsw, cs48l32_digital_tlv),
+
+SOC_ENUM("Input Ramp Up", cs48l32_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", cs48l32_in_vd_ramp),
+
+CS48L32_RATE_ENUM("Ultrasonic 1 Rate", cs48l32_us_output_rate[0]),
+CS48L32_RATE_ENUM("Ultrasonic 2 Rate", cs48l32_us_output_rate[1]),
+
+SOC_ENUM("Ultrasonic 1 Freq", cs48l32_us_freq[0]),
+SOC_ENUM("Ultrasonic 2 Freq", cs48l32_us_freq[1]),
+
+SOC_SINGLE_TLV("Ultrasonic 1 Volume", CS48L32_US1_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+SOC_SINGLE_TLV("Ultrasonic 2 Volume", CS48L32_US2_CONTROL, CS48L32_US1_GAIN_SHIFT,
+ 3, 0, cs48l32_us_tlv),
+
+SOC_ENUM("Ultrasonic 1 Detect Threshold", cs48l32_us_det_thr[0]),
+SOC_ENUM("Ultrasonic 2 Detect Threshold", cs48l32_us_det_thr[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Pulse Length", cs48l32_us_det_num[0]),
+SOC_ENUM("Ultrasonic 2 Detect Pulse Length", cs48l32_us_det_num[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Hold", cs48l32_us_det_hold[0]),
+SOC_ENUM("Ultrasonic 2 Detect Hold", cs48l32_us_det_hold[1]),
+
+SOC_ENUM("Ultrasonic 1 Detect Decay", cs48l32_us_det_dcy[0]),
+SOC_ENUM("Ultrasonic 2 Detect Decay", cs48l32_us_det_dcy[1]),
+
+SOC_SINGLE("Ultrasonic 1 Detect LPF Switch",
+ CS48L32_US1_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+SOC_SINGLE("Ultrasonic 2 Detect LPF Switch",
+ CS48L32_US2_DET_CONTROL, CS48L32_US1_DET_LPF_SHIFT, 1, 0),
+
+SOC_ENUM("Ultrasonic 1 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[0]),
+SOC_ENUM("Ultrasonic 2 Detect LPF Cut-off", cs48l32_us_det_lpf_cut[1]),
+
+CS48L32_MIXER_CONTROLS("EQ1", CS48L32_EQ1_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ2", CS48L32_EQ2_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ3", CS48L32_EQ3_INPUT1),
+CS48L32_MIXER_CONTROLS("EQ4", CS48L32_EQ4_INPUT1),
+
+SOC_ENUM_EXT("EQ1 Mode", cs48l32_eq_mode[0], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+
+CS48L32_EQ_COEFF_CONTROLS(EQ1),
+
+SOC_SINGLE_TLV("EQ1 B1 Volume", CS48L32_EQ1_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", CS48L32_EQ1_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", CS48L32_EQ1_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", CS48L32_EQ1_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", CS48L32_EQ1_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ2 Mode", cs48l32_eq_mode[1], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ2),
+SOC_SINGLE_TLV("EQ2 B1 Volume", CS48L32_EQ2_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", CS48L32_EQ2_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", CS48L32_EQ2_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", CS48L32_EQ2_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", CS48L32_EQ2_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ3 Mode", cs48l32_eq_mode[2], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ3),
+SOC_SINGLE_TLV("EQ3 B1 Volume", CS48L32_EQ3_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", CS48L32_EQ3_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", CS48L32_EQ3_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", CS48L32_EQ3_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", CS48L32_EQ3_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+SOC_ENUM_EXT("EQ4 Mode", cs48l32_eq_mode[3], cs48l32_eq_mode_get, cs48l32_eq_mode_put),
+CS48L32_EQ_COEFF_CONTROLS(EQ4),
+SOC_SINGLE_TLV("EQ4 B1 Volume", CS48L32_EQ4_GAIN1, 0, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", CS48L32_EQ4_GAIN1, 8, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", CS48L32_EQ4_GAIN1, 16, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", CS48L32_EQ4_GAIN1, 24, 24, 0, cs48l32_eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", CS48L32_EQ4_GAIN2, 0, 24, 0, cs48l32_eq_tlv),
+
+CS48L32_MIXER_CONTROLS("DRC1L", CS48L32_DRC1L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC1R", CS48L32_DRC1R_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2L", CS48L32_DRC2L_INPUT1),
+CS48L32_MIXER_CONTROLS("DRC2R", CS48L32_DRC2R_INPUT1),
+
+SND_SOC_BYTES_MASK("DRC1 Coefficients", CS48L32_DRC1_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+SND_SOC_BYTES_MASK("DRC2 Coefficients", CS48L32_DRC2_CONTROL1, 4,
+ BIT(CS48L32_DRC1R_EN_SHIFT) | BIT(CS48L32_DRC1L_EN_SHIFT)),
+
+CS48L32_MIXER_CONTROLS("LHPF1", CS48L32_LHPF1_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF2", CS48L32_LHPF2_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF3", CS48L32_LHPF3_INPUT1),
+CS48L32_MIXER_CONTROLS("LHPF4", CS48L32_LHPF4_INPUT1),
+
+CS48L32_LHPF_CONTROL("LHPF1 Coefficients", CS48L32_LHPF1_COEFF),
+CS48L32_LHPF_CONTROL("LHPF2 Coefficients", CS48L32_LHPF2_COEFF),
+CS48L32_LHPF_CONTROL("LHPF3 Coefficients", CS48L32_LHPF3_COEFF),
+CS48L32_LHPF_CONTROL("LHPF4 Coefficients", CS48L32_LHPF4_COEFF),
+
+SOC_ENUM("LHPF1 Mode", cs48l32_lhpf_mode[0]),
+SOC_ENUM("LHPF2 Mode", cs48l32_lhpf_mode[1]),
+SOC_ENUM("LHPF3 Mode", cs48l32_lhpf_mode[2]),
+SOC_ENUM("LHPF4 Mode", cs48l32_lhpf_mode[3]),
+
+CS48L32_RATE_CONTROL("Sample Rate 1", 1),
+CS48L32_RATE_CONTROL("Sample Rate 2", 2),
+CS48L32_RATE_CONTROL("Sample Rate 3", 3),
+CS48L32_RATE_CONTROL("Sample Rate 4", 4),
+
+CS48L32_RATE_ENUM("FX Rate", cs48l32_fx_rate),
+
+CS48L32_RATE_ENUM("ISRC1 FSL", cs48l32_isrc_fsl[0]),
+CS48L32_RATE_ENUM("ISRC2 FSL", cs48l32_isrc_fsl[1]),
+CS48L32_RATE_ENUM("ISRC3 FSL", cs48l32_isrc_fsl[2]),
+CS48L32_RATE_ENUM("ISRC1 FSH", cs48l32_isrc_fsh[0]),
+CS48L32_RATE_ENUM("ISRC2 FSH", cs48l32_isrc_fsh[1]),
+CS48L32_RATE_ENUM("ISRC3 FSH", cs48l32_isrc_fsh[2]),
+
+SOC_ENUM("AUXPDM1 Rate", cs48l32_auxpdm1_freq),
+SOC_ENUM("AUXPDM2 Rate", cs48l32_auxpdm2_freq),
+
+SOC_ENUM_EXT("IN1L Rate", cs48l32_input_rate[0], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN1R Rate", cs48l32_input_rate[1], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2L Rate", cs48l32_input_rate[2], snd_soc_get_enum_double, cs48l32_in_rate_put),
+SOC_ENUM_EXT("IN2R Rate", cs48l32_input_rate[3], snd_soc_get_enum_double, cs48l32_in_rate_put),
+
+CS48L32_RATE_ENUM("Noise Generator Rate", noise_gen_rate),
+
+SOC_SINGLE_TLV("Noise Generator Volume", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_GAIN_SHIFT, 0x12, 0, cs48l32_noise_tlv),
+
+CS48L32_MIXER_CONTROLS("ASP1TX1", CS48L32_ASP1TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX2", CS48L32_ASP1TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX3", CS48L32_ASP1TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX4", CS48L32_ASP1TX4_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX5", CS48L32_ASP1TX5_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX6", CS48L32_ASP1TX6_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX7", CS48L32_ASP1TX7_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP1TX8", CS48L32_ASP1TX8_INPUT1),
+
+CS48L32_MIXER_CONTROLS("ASP2TX1", CS48L32_ASP2TX1_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX2", CS48L32_ASP2TX2_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX3", CS48L32_ASP2TX3_INPUT1),
+CS48L32_MIXER_CONTROLS("ASP2TX4", CS48L32_ASP2TX4_INPUT1),
+
+WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+
+CS48L32_MIXER_CONTROLS("DSP1RX1", CS48L32_DSP1RX1_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX2", CS48L32_DSP1RX2_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX3", CS48L32_DSP1RX3_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX4", CS48L32_DSP1RX4_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX5", CS48L32_DSP1RX5_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX6", CS48L32_DSP1RX6_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX7", CS48L32_DSP1RX7_INPUT1),
+CS48L32_MIXER_CONTROLS("DSP1RX8", CS48L32_DSP1RX8_INPUT1),
+
+WM_ADSP_FW_CONTROL("DSP1", 0),
+
+CS48L32_DSP_RATE_CONTROL("DSP1RX1", 0),
+CS48L32_DSP_RATE_CONTROL("DSP1RX2", 1),
+CS48L32_DSP_RATE_CONTROL("DSP1RX3", 2),
+CS48L32_DSP_RATE_CONTROL("DSP1RX4", 3),
+CS48L32_DSP_RATE_CONTROL("DSP1RX5", 4),
+CS48L32_DSP_RATE_CONTROL("DSP1RX6", 5),
+CS48L32_DSP_RATE_CONTROL("DSP1RX7", 6),
+CS48L32_DSP_RATE_CONTROL("DSP1RX8", 7),
+CS48L32_DSP_RATE_CONTROL("DSP1TX1", 8),
+CS48L32_DSP_RATE_CONTROL("DSP1TX2", 9),
+CS48L32_DSP_RATE_CONTROL("DSP1TX3", 10),
+CS48L32_DSP_RATE_CONTROL("DSP1TX4", 11),
+CS48L32_DSP_RATE_CONTROL("DSP1TX5", 12),
+CS48L32_DSP_RATE_CONTROL("DSP1TX6", 13),
+CS48L32_DSP_RATE_CONTROL("DSP1TX7", 14),
+CS48L32_DSP_RATE_CONTROL("DSP1TX8", 15),
+};
+
+CS48L32_MIXER_ENUMS(EQ1, CS48L32_EQ1_INPUT1);
+CS48L32_MIXER_ENUMS(EQ2, CS48L32_EQ2_INPUT1);
+CS48L32_MIXER_ENUMS(EQ3, CS48L32_EQ3_INPUT1);
+CS48L32_MIXER_ENUMS(EQ4, CS48L32_EQ4_INPUT1);
+
+CS48L32_MIXER_ENUMS(DRC1L, CS48L32_DRC1L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC1R, CS48L32_DRC1R_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2L, CS48L32_DRC2L_INPUT1);
+CS48L32_MIXER_ENUMS(DRC2R, CS48L32_DRC2R_INPUT1);
+
+CS48L32_MIXER_ENUMS(LHPF1, CS48L32_LHPF1_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF2, CS48L32_LHPF2_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF3, CS48L32_LHPF3_INPUT1);
+CS48L32_MIXER_ENUMS(LHPF4, CS48L32_LHPF4_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP1TX1, CS48L32_ASP1TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX2, CS48L32_ASP1TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX3, CS48L32_ASP1TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX4, CS48L32_ASP1TX4_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX5, CS48L32_ASP1TX5_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX6, CS48L32_ASP1TX6_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX7, CS48L32_ASP1TX7_INPUT1);
+CS48L32_MIXER_ENUMS(ASP1TX8, CS48L32_ASP1TX8_INPUT1);
+
+CS48L32_MIXER_ENUMS(ASP2TX1, CS48L32_ASP2TX1_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX2, CS48L32_ASP2TX2_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX3, CS48L32_ASP2TX3_INPUT1);
+CS48L32_MIXER_ENUMS(ASP2TX4, CS48L32_ASP2TX4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1INT1, CS48L32_ISRC1INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT2, CS48L32_ISRC1INT2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT3, CS48L32_ISRC1INT3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1INT4, CS48L32_ISRC1INT4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC1DEC1, CS48L32_ISRC1DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC2, CS48L32_ISRC1DEC2_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC3, CS48L32_ISRC1DEC3_INPUT1);
+CS48L32_MUX_ENUMS(ISRC1DEC4, CS48L32_ISRC1DEC4_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2INT1, CS48L32_ISRC2INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2INT2, CS48L32_ISRC2INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC2DEC1, CS48L32_ISRC2DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC2DEC2, CS48L32_ISRC2DEC2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3INT1, CS48L32_ISRC3INT1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3INT2, CS48L32_ISRC3INT2_INPUT1);
+
+CS48L32_MUX_ENUMS(ISRC3DEC1, CS48L32_ISRC3DEC1_INPUT1);
+CS48L32_MUX_ENUMS(ISRC3DEC2, CS48L32_ISRC3DEC2_INPUT1);
+
+CS48L32_MIXER_ENUMS(DSP1RX1, CS48L32_DSP1RX1_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX2, CS48L32_DSP1RX2_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX3, CS48L32_DSP1RX3_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX4, CS48L32_DSP1RX4_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX5, CS48L32_DSP1RX5_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX6, CS48L32_DSP1RX6_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX7, CS48L32_DSP1RX7_INPUT1);
+CS48L32_MIXER_ENUMS(DSP1RX8, CS48L32_DSP1RX8_INPUT1);
+
+static int cs48l32_dsp_mem_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 cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs48l32_dsp_memory_enable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ case SND_SOC_DAPM_PRE_PMD:
+ cs48l32_dsp_memory_disable(cs48l32_codec, &cs48l32_dsp_sram_regs);
+ return 0;
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs48l32_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", CS48L32_SYSTEM_CLOCK1, CS48L32_SYSCLK_EN_SHIFT, 0,
+ cs48l32_sysclk_ev, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-cp", 20, 0),
+
+SND_SOC_DAPM_SUPPLY("VOUT_MIC", CS48L32_CHARGE_PUMP1, CS48L32_CP2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("VOUT_MIC_REGULATED", CS48L32_CHARGE_PUMP1, CS48L32_CP2_BYPASS_SHIFT,
+ 1, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1", CS48L32_MICBIAS_CTRL1, CS48L32_MICB1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1A", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1A_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1B", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1B_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS1C", CS48L32_MICBIAS_CTRL5, CS48L32_MICB1C_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_SUPPLY("DSP1MEM", SND_SOC_NOPM, 0, 0, cs48l32_dsp_mem_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+CS48L32_DSP_FREQ_WIDGET_EV("DSP1", 0, cs48l32_dsp_freq_ev),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+
+SND_SOC_DAPM_INPUT("IN1LN_1"),
+SND_SOC_DAPM_INPUT("IN1LN_2"),
+SND_SOC_DAPM_INPUT("IN1LP_1"),
+SND_SOC_DAPM_INPUT("IN1LP_2"),
+SND_SOC_DAPM_INPUT("IN1RN_1"),
+SND_SOC_DAPM_INPUT("IN1RN_2"),
+SND_SOC_DAPM_INPUT("IN1RP_1"),
+SND_SOC_DAPM_INPUT("IN1RP_2"),
+SND_SOC_DAPM_INPUT("IN1_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN1_PDMDATA"),
+
+SND_SOC_DAPM_INPUT("IN2_PDMCLK"),
+SND_SOC_DAPM_INPUT("IN2_PDMDATA"),
+
+SND_SOC_DAPM_MUX("Ultrasonic 1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[0]),
+SND_SOC_DAPM_MUX("Ultrasonic 2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_us_inmux[1]),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
+
+SND_SOC_DAPM_OUTPUT("DSP Trigger Out"),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &cs48l32_inmux[1]),
+
+SND_SOC_DAPM_MUX("IN1L Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mode", SND_SOC_NOPM, 0, 0, &cs48l32_dmode_mux[0]),
+
+SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS48L32_ASP1_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS48L32_ASP1_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS48L32_ASP1_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS48L32_ASP1_ENABLES1, 3, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX5", NULL, 4, CS48L32_ASP1_ENABLES1, 4, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX6", NULL, 5, CS48L32_ASP1_ENABLES1, 5, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX7", NULL, 6, CS48L32_ASP1_ENABLES1, 6, 0),
+SND_SOC_DAPM_AIF_OUT("ASP1TX8", NULL, 7, CS48L32_ASP1_ENABLES1, 7, 0),
+
+SND_SOC_DAPM_AIF_OUT("ASP2TX1", NULL, 0, CS48L32_ASP2_ENABLES1, 0, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX2", NULL, 1, CS48L32_ASP2_ENABLES1, 1, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX3", NULL, 2, CS48L32_ASP2_ENABLES1, 2, 0),
+SND_SOC_DAPM_AIF_OUT("ASP2TX4", NULL, 3, CS48L32_ASP2_ENABLES1, 3, 0),
+
+SND_SOC_DAPM_SWITCH("AUXPDM1 Output", CS48L32_AUXPDM_CONTROL1, 0, 0, &cs48l32_auxpdm_switch[0]),
+SND_SOC_DAPM_SWITCH("AUXPDM2 Output", CS48L32_AUXPDM_CONTROL1, 1, 0, &cs48l32_auxpdm_switch[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Input", SND_SOC_NOPM, 0, 0, &cs48l32_auxpdm_inmux[1]),
+
+SND_SOC_DAPM_MUX("AUXPDM1 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[0]),
+SND_SOC_DAPM_MUX("AUXPDM2 Analog Input", SND_SOC_NOPM, 0, 0,
+ &cs48l32_auxpdm_analog_inmux[1]),
+
+SND_SOC_DAPM_SWITCH("Ultrasonic 1 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[0]),
+SND_SOC_DAPM_SWITCH("Ultrasonic 2 Detect", CS48L32_US_CONTROL,
+ CS48L32_US1_DET_EN_SHIFT, 0, &cs48l32_us_switch[1]),
+
+/*
+ * mux_in widgets : arranged in the order of sources
+ * specified in CS48L32_MIXER_INPUT_ROUTES
+ */
+SND_SOC_DAPM_PGA("Tone Generator 1", CS48L32_TONE_GENERATOR1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", CS48L32_TONE_GENERATOR1, 1, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", CS48L32_COMFORT_NOISE_GENERATOR,
+ CS48L32_NOISE_GEN_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN1R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2L PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2L_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2R PGA", CS48L32_INPUT_CONTROL, CS48L32_IN2R_EN_SHIFT,
+ 0, NULL, 0, cs48l32_in_ev,
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS48L32_ASP1_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS48L32_ASP1_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX3", NULL, 2, CS48L32_ASP1_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX4", NULL, 3, CS48L32_ASP1_ENABLES1, 19, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX5", NULL, 4, CS48L32_ASP1_ENABLES1, 20, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX6", NULL, 5, CS48L32_ASP1_ENABLES1, 21, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX7", NULL, 6, CS48L32_ASP1_ENABLES1, 22, 0),
+SND_SOC_DAPM_AIF_IN("ASP1RX8", NULL, 7, CS48L32_ASP1_ENABLES1, 23, 0),
+
+SND_SOC_DAPM_AIF_IN("ASP2RX1", NULL, 0, CS48L32_ASP2_ENABLES1, 16, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX2", NULL, 1, CS48L32_ASP2_ENABLES1, 17, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX3", NULL, 2, CS48L32_ASP2_ENABLES1, 18, 0),
+SND_SOC_DAPM_AIF_IN("ASP2RX4", NULL, 3, CS48L32_ASP2_ENABLES1, 19, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_DEC4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT3_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", CS48L32_ISRC1_CONTROL2, CS48L32_ISRC1_INT4_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", CS48L32_ISRC2_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3DEC1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3DEC2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_DEC2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC3INT1", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT1_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC3INT2", CS48L32_ISRC3_CONTROL2, CS48L32_ISRC1_INT2_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("EQ1", CS48L32_EQ_CONTROL1, 0, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ2", CS48L32_EQ_CONTROL1, 1, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ3", CS48L32_EQ_CONTROL1, 2, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("EQ4", CS48L32_EQ_CONTROL1, 3, 0, NULL, 0, cs48l32_eq_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_PGA("DRC1L", CS48L32_DRC1_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", CS48L32_DRC1_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2L", CS48L32_DRC2_CONTROL1, CS48L32_DRC1L_EN_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("DRC2R", CS48L32_DRC2_CONTROL1, CS48L32_DRC1R_EN_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", CS48L32_LHPF_CONTROL1, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", CS48L32_LHPF_CONTROL1, 1, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", CS48L32_LHPF_CONTROL1, 2, 0, NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", CS48L32_LHPF_CONTROL1, 3, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Ultrasonic 1", CS48L32_US_CONTROL, 0, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Ultrasonic 2", CS48L32_US_CONTROL, 1, 0, NULL, 0),
+
+WM_ADSP2("DSP1", 0, wm_adsp_early_event),
+
+/* end of ordered widget list */
+
+CS48L32_MIXER_WIDGETS(EQ1, "EQ1"),
+CS48L32_MIXER_WIDGETS(EQ2, "EQ2"),
+CS48L32_MIXER_WIDGETS(EQ3, "EQ3"),
+CS48L32_MIXER_WIDGETS(EQ4, "EQ4"),
+
+CS48L32_MIXER_WIDGETS(DRC1L, "DRC1L"),
+CS48L32_MIXER_WIDGETS(DRC1R, "DRC1R"),
+CS48L32_MIXER_WIDGETS(DRC2L, "DRC2L"),
+CS48L32_MIXER_WIDGETS(DRC2R, "DRC2R"),
+
+SND_SOC_DAPM_SWITCH("DRC1 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[0]),
+SND_SOC_DAPM_SWITCH("DRC2 Activity Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_drc_activity_output_mux[1]),
+
+CS48L32_MIXER_WIDGETS(LHPF1, "LHPF1"),
+CS48L32_MIXER_WIDGETS(LHPF2, "LHPF2"),
+CS48L32_MIXER_WIDGETS(LHPF3, "LHPF3"),
+CS48L32_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+CS48L32_MIXER_WIDGETS(ASP1TX1, "ASP1TX1"),
+CS48L32_MIXER_WIDGETS(ASP1TX2, "ASP1TX2"),
+CS48L32_MIXER_WIDGETS(ASP1TX3, "ASP1TX3"),
+CS48L32_MIXER_WIDGETS(ASP1TX4, "ASP1TX4"),
+CS48L32_MIXER_WIDGETS(ASP1TX5, "ASP1TX5"),
+CS48L32_MIXER_WIDGETS(ASP1TX6, "ASP1TX6"),
+CS48L32_MIXER_WIDGETS(ASP1TX7, "ASP1TX7"),
+CS48L32_MIXER_WIDGETS(ASP1TX8, "ASP1TX8"),
+
+CS48L32_MIXER_WIDGETS(ASP2TX1, "ASP2TX1"),
+CS48L32_MIXER_WIDGETS(ASP2TX2, "ASP2TX2"),
+CS48L32_MIXER_WIDGETS(ASP2TX3, "ASP2TX3"),
+CS48L32_MIXER_WIDGETS(ASP2TX4, "ASP2TX4"),
+
+CS48L32_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+CS48L32_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+CS48L32_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+CS48L32_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+CS48L32_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+CS48L32_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+CS48L32_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+CS48L32_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+CS48L32_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+CS48L32_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+CS48L32_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+CS48L32_MUX_WIDGETS(ISRC3DEC1, "ISRC3DEC1"),
+CS48L32_MUX_WIDGETS(ISRC3DEC2, "ISRC3DEC2"),
+
+CS48L32_MUX_WIDGETS(ISRC3INT1, "ISRC3INT1"),
+CS48L32_MUX_WIDGETS(ISRC3INT2, "ISRC3INT2"),
+
+CS48L32_MIXER_WIDGETS(DSP1RX1, "DSP1RX1"),
+CS48L32_MIXER_WIDGETS(DSP1RX2, "DSP1RX2"),
+CS48L32_MIXER_WIDGETS(DSP1RX3, "DSP1RX3"),
+CS48L32_MIXER_WIDGETS(DSP1RX4, "DSP1RX4"),
+CS48L32_MIXER_WIDGETS(DSP1RX5, "DSP1RX5"),
+CS48L32_MIXER_WIDGETS(DSP1RX6, "DSP1RX6"),
+CS48L32_MIXER_WIDGETS(DSP1RX7, "DSP1RX7"),
+CS48L32_MIXER_WIDGETS(DSP1RX8, "DSP1RX8"),
+
+SND_SOC_DAPM_SWITCH("DSP1 Trigger Output", SND_SOC_NOPM, 0, 0,
+ &cs48l32_dsp_trigger_output_mux[0]),
+
+SND_SOC_DAPM_OUTPUT("AUXPDM1_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM1_DOUT"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_CLK"),
+SND_SOC_DAPM_OUTPUT("AUXPDM2_DOUT"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+
+SND_SOC_DAPM_OUTPUT("Ultrasonic Dummy Output"),
+};
+
+static const struct snd_soc_dapm_route cs48l32_dapm_routes[] = {
+ { "IN1LN_1", NULL, "SYSCLK" },
+ { "IN1LN_2", NULL, "SYSCLK" },
+ { "IN1LP_1", NULL, "SYSCLK" },
+ { "IN1LP_2", NULL, "SYSCLK" },
+ { "IN1RN_1", NULL, "SYSCLK" },
+ { "IN1RN_2", NULL, "SYSCLK" },
+ { "IN1RP_1", NULL, "SYSCLK" },
+ { "IN1RP_2", NULL, "SYSCLK" },
+
+ { "IN1_PDMCLK", NULL, "SYSCLK" },
+ { "IN1_PDMDATA", NULL, "SYSCLK" },
+ { "IN2_PDMCLK", NULL, "SYSCLK" },
+ { "IN2_PDMDATA", NULL, "SYSCLK" },
+
+ { "DSP1 Preloader", NULL, "DSP1MEM" },
+ { "DSP1", NULL, "DSP1FREQ" },
+
+ { "Audio Trace DSP", NULL, "DSP1" },
+ { "Voice Ctrl DSP", NULL, "DSP1" },
+
+ { "VOUT_MIC_REGULATED", NULL, "VOUT_MIC" },
+ { "MICBIAS1", NULL, "VOUT_MIC_REGULATED" },
+ { "MICBIAS1A", NULL, "MICBIAS1" },
+ { "MICBIAS1B", NULL, "MICBIAS1" },
+ { "MICBIAS1C", NULL, "MICBIAS1" },
+
+ { "Tone Generator 1", NULL, "SYSCLK" },
+ { "Tone Generator 2", NULL, "SYSCLK" },
+ { "Noise Generator", NULL, "SYSCLK" },
+
+ { "Tone Generator 1", NULL, "TONE" },
+ { "Tone Generator 2", NULL, "TONE" },
+ { "Noise Generator", NULL, "NOISE" },
+
+ { "ASP1 Capture", NULL, "ASP1TX1" },
+ { "ASP1 Capture", NULL, "ASP1TX2" },
+ { "ASP1 Capture", NULL, "ASP1TX3" },
+ { "ASP1 Capture", NULL, "ASP1TX4" },
+ { "ASP1 Capture", NULL, "ASP1TX5" },
+ { "ASP1 Capture", NULL, "ASP1TX6" },
+ { "ASP1 Capture", NULL, "ASP1TX7" },
+ { "ASP1 Capture", NULL, "ASP1TX8" },
+
+ { "ASP1RX1", NULL, "ASP1 Playback" },
+ { "ASP1RX2", NULL, "ASP1 Playback" },
+ { "ASP1RX3", NULL, "ASP1 Playback" },
+ { "ASP1RX4", NULL, "ASP1 Playback" },
+ { "ASP1RX5", NULL, "ASP1 Playback" },
+ { "ASP1RX6", NULL, "ASP1 Playback" },
+ { "ASP1RX7", NULL, "ASP1 Playback" },
+ { "ASP1RX8", NULL, "ASP1 Playback" },
+
+ { "ASP2 Capture", NULL, "ASP2TX1" },
+ { "ASP2 Capture", NULL, "ASP2TX2" },
+ { "ASP2 Capture", NULL, "ASP2TX3" },
+ { "ASP2 Capture", NULL, "ASP2TX4" },
+
+ { "ASP2RX1", NULL, "ASP2 Playback" },
+ { "ASP2RX2", NULL, "ASP2 Playback" },
+ { "ASP2RX3", NULL, "ASP2 Playback" },
+ { "ASP2RX4", NULL, "ASP2 Playback" },
+
+ { "ASP1 Playback", NULL, "SYSCLK" },
+ { "ASP2 Playback", NULL, "SYSCLK" },
+
+ { "ASP1 Capture", NULL, "SYSCLK" },
+ { "ASP2 Capture", NULL, "SYSCLK" },
+
+ { "IN1L Mux", "Analog 1", "IN1LN_1" },
+ { "IN1L Mux", "Analog 2", "IN1LN_2" },
+ { "IN1L Mux", "Analog 1", "IN1LP_1" },
+ { "IN1L Mux", "Analog 2", "IN1LP_2" },
+ { "IN1R Mux", "Analog 1", "IN1RN_1" },
+ { "IN1R Mux", "Analog 2", "IN1RN_2" },
+ { "IN1R Mux", "Analog 1", "IN1RP_1" },
+ { "IN1R Mux", "Analog 2", "IN1RP_2" },
+
+ { "IN1L PGA", NULL, "IN1L Mode" },
+ { "IN1R PGA", NULL, "IN1R Mode" },
+
+ { "IN1L Mode", "Analog", "IN1L Mux" },
+ { "IN1R Mode", "Analog", "IN1R Mux" },
+
+ { "IN1L Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1L Mode", "Digital", "IN1_PDMDATA" },
+ { "IN1R Mode", "Digital", "IN1_PDMCLK" },
+ { "IN1R Mode", "Digital", "IN1_PDMDATA" },
+
+ { "IN1L PGA", NULL, "VOUT_MIC" },
+ { "IN1R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "VOUT_MIC" },
+ { "IN2R PGA", NULL, "VOUT_MIC" },
+
+ { "IN2L PGA", NULL, "IN2_PDMCLK" },
+ { "IN2R PGA", NULL, "IN2_PDMCLK" },
+ { "IN2L PGA", NULL, "IN2_PDMDATA" },
+ { "IN2R PGA", NULL, "IN2_PDMDATA" },
+
+ { "Ultrasonic 1", NULL, "Ultrasonic 1 Input" },
+ { "Ultrasonic 2", NULL, "Ultrasonic 2 Input" },
+
+ { "Ultrasonic 1 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 1 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 1 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 1 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 2 Input", "IN1L", "IN1L PGA" },
+ { "Ultrasonic 2 Input", "IN1R", "IN1R PGA" },
+ { "Ultrasonic 2 Input", "IN2L", "IN2L PGA" },
+ { "Ultrasonic 2 Input", "IN2R", "IN2R PGA" },
+
+ { "Ultrasonic 1 Detect", "Switch", "Ultrasonic 1 Input" },
+ { "Ultrasonic 2 Detect", "Switch", "Ultrasonic 2 Input" },
+
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 1 Detect" },
+ { "Ultrasonic Dummy Output", NULL, "Ultrasonic 2 Detect" },
+
+ CS48L32_MIXER_ROUTES("ASP1TX1", "ASP1TX1"),
+ CS48L32_MIXER_ROUTES("ASP1TX2", "ASP1TX2"),
+ CS48L32_MIXER_ROUTES("ASP1TX3", "ASP1TX3"),
+ CS48L32_MIXER_ROUTES("ASP1TX4", "ASP1TX4"),
+ CS48L32_MIXER_ROUTES("ASP1TX5", "ASP1TX5"),
+ CS48L32_MIXER_ROUTES("ASP1TX6", "ASP1TX6"),
+ CS48L32_MIXER_ROUTES("ASP1TX7", "ASP1TX7"),
+ CS48L32_MIXER_ROUTES("ASP1TX8", "ASP1TX8"),
+
+ CS48L32_MIXER_ROUTES("ASP2TX1", "ASP2TX1"),
+ CS48L32_MIXER_ROUTES("ASP2TX2", "ASP2TX2"),
+ CS48L32_MIXER_ROUTES("ASP2TX3", "ASP2TX3"),
+ CS48L32_MIXER_ROUTES("ASP2TX4", "ASP2TX4"),
+
+ CS48L32_MIXER_ROUTES("EQ1", "EQ1"),
+ CS48L32_MIXER_ROUTES("EQ2", "EQ2"),
+ CS48L32_MIXER_ROUTES("EQ3", "EQ3"),
+ CS48L32_MIXER_ROUTES("EQ4", "EQ4"),
+
+ CS48L32_MIXER_ROUTES("DRC1L", "DRC1L"),
+ CS48L32_MIXER_ROUTES("DRC1R", "DRC1R"),
+ CS48L32_MIXER_ROUTES("DRC2L", "DRC2L"),
+ CS48L32_MIXER_ROUTES("DRC2R", "DRC2R"),
+
+ CS48L32_MIXER_ROUTES("LHPF1", "LHPF1"),
+ CS48L32_MIXER_ROUTES("LHPF2", "LHPF2"),
+ CS48L32_MIXER_ROUTES("LHPF3", "LHPF3"),
+ CS48L32_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+ CS48L32_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ CS48L32_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ CS48L32_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+ CS48L32_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+ CS48L32_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ CS48L32_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ CS48L32_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+ CS48L32_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+ CS48L32_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ CS48L32_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ CS48L32_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ CS48L32_MUX_ROUTES("ISRC3INT1", "ISRC3INT1"),
+ CS48L32_MUX_ROUTES("ISRC3INT2", "ISRC3INT2"),
+
+ CS48L32_MUX_ROUTES("ISRC3DEC1", "ISRC3DEC1"),
+ CS48L32_MUX_ROUTES("ISRC3DEC2", "ISRC3DEC2"),
+
+ CS48L32_DSP_ROUTES_1_8_SYSCLK("DSP1"),
+
+ { "DSP Trigger Out", NULL, "DSP1 Trigger Output" },
+
+ { "DSP1 Trigger Output", "Switch", "DSP1" },
+
+ { "AUXPDM1 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM1 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM2 Analog Input", "IN1L", "IN1L PGA" },
+ { "AUXPDM2 Analog Input", "IN1R", "IN1R PGA" },
+
+ { "AUXPDM1 Input", "Analog", "AUXPDM1 Analog Input" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM1 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM1 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM2 Input", "Analog", "AUXPDM2 Analog Input" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1L PGA" },
+ { "AUXPDM2 Input", "IN1 Digital", "IN1R PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2L PGA" },
+ { "AUXPDM2 Input", "IN2 Digital", "IN2R PGA" },
+
+ { "AUXPDM1 Output", "Switch", "AUXPDM1 Input" },
+ { "AUXPDM1_CLK", NULL, "AUXPDM1 Output" },
+ { "AUXPDM1_DOUT", NULL, "AUXPDM1 Output" },
+
+ { "AUXPDM2 Output", "Switch", "AUXPDM2 Input" },
+ { "AUXPDM2_CLK", NULL, "AUXPDM2 Output" },
+ { "AUXPDM2_DOUT", NULL, "AUXPDM2 Output" },
+
+ { "MICSUPP", NULL, "SYSCLK" },
+
+ { "DRC1 Signal Activity", NULL, "DRC1 Activity Output" },
+ { "DRC2 Signal Activity", NULL, "DRC2 Activity Output" },
+ { "DRC1 Activity Output", "Switch", "DRC1L" },
+ { "DRC1 Activity Output", "Switch", "DRC1R" },
+ { "DRC2 Activity Output", "Switch", "DRC2L" },
+ { "DRC2 Activity Output", "Switch", "DRC2R" },
+};
+
+static int cs48l32_compr_open(struct snd_soc_component *component,
+ struct snd_compr_stream *stream)
+{
+ struct snd_soc_pcm_runtime *rtd = stream->private_data;
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ if (strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-trace") &&
+ strcmp(snd_soc_rtd_to_codec(rtd, 0)->name, "cs48l32-dsp-voicectrl")) {
+ dev_err(cs48l32_codec->core.dev, "No suitable compressed stream for DAI '%s'\n",
+ snd_soc_rtd_to_codec(rtd, 0)->name);
+ return -EINVAL;
+ }
+
+ return wm_adsp_compr_open(&cs48l32_codec->dsp, stream);
+}
+
+static const struct snd_compress_ops cs48l32_compress_ops = {
+ .open = &cs48l32_compr_open,
+ .free = &wm_adsp_compr_free,
+ .set_params = &wm_adsp_compr_set_params,
+ .get_caps = &wm_adsp_compr_get_caps,
+ .trigger = &wm_adsp_compr_trigger,
+ .pointer = &wm_adsp_compr_pointer,
+ .copy = &wm_adsp_compr_copy,
+};
+
+static const struct snd_soc_dai_ops cs48l32_compress_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
+static struct snd_soc_dai_driver cs48l32_dai[] = {
+ {
+ .name = "cs48l32-asp1",
+ .id = 1,
+ .base = CS48L32_ASP1_ENABLES1,
+ .playback = {
+ .stream_name = "ASP1 Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-asp2",
+ .id = 2,
+ .base = CS48L32_ASP2_ENABLES1,
+ .playback = {
+ .stream_name = "ASP2 Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs48l32-cpu-trace",
+ .id = 3,
+ .capture = {
+ .stream_name = "Audio Trace CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-trace",
+ .id = 4,
+ .capture = {
+ .stream_name = "Audio Trace DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+ {
+ .name = "cs48l32-cpu-voicectrl",
+ .id = 5,
+ .capture = {
+ .stream_name = "Voice Ctrl CPU",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ .ops = &cs48l32_compress_dai_ops,
+ },
+ {
+ .name = "cs48l32-dsp-voicectrl",
+ .id = 6,
+ .capture = {
+ .stream_name = "Voice Ctrl DSP",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS48L32_RATES,
+ .formats = CS48L32_FORMATS,
+ },
+ },
+};
+
+static int cs48l32_init_inputs(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int ana_mode_l, ana_mode_r, dig_mode;
+ int i;
+
+ /*
+ * Initialize input modes from the A settings. For muxed inputs the
+ * B settings will be applied if the mux is changed
+ */
+ switch (cs48l32_codec->in_type[0][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_l = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_l = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ switch (cs48l32_codec->in_type[1][0]) {
+ default:
+ case CS48L32_IN_TYPE_DIFF:
+ ana_mode_r = 0;
+ break;
+ case CS48L32_IN_TYPE_SE:
+ ana_mode_r = 1 << CS48L32_INx_SRC_SHIFT;
+ break;
+ }
+
+ dev_dbg(cs48l32_codec->core.dev, "IN1_1 Analogue mode=#%x,#%x\n",
+ ana_mode_l, ana_mode_r);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1L_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_l);
+
+ regmap_update_bits(regmap,
+ CS48L32_IN1R_CONTROL1,
+ CS48L32_INx_SRC_MASK,
+ ana_mode_r);
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ dig_mode = cs48l32_codec->pdm_sup[i] << CS48L32_IN1_PDM_SUP_SHIFT;
+
+ dev_dbg(cs48l32_codec->core.dev, "IN%d PDM_SUP=#%x\n", i + 1, dig_mode);
+
+ regmap_update_bits(regmap,
+ CS48L32_INPUT1_CONTROL1 + (i * 0x40),
+ CS48L32_IN1_PDM_SUP_MASK, dig_mode);
+ }
+
+ return 0;
+}
+
+static int cs48l32_init_dai(struct cs48l32_codec *cs48l32_codec, int id)
+{
+ struct cs48l32_dai_priv *dai_priv = &cs48l32_codec->dai[id];
+
+ dai_priv->clk = CS48L32_CLK_SYSCLK_1;
+ dai_priv->constraint = cs48l32_constraint;
+
+ return 0;
+}
+
+static int cs48l32_init_eq(struct cs48l32_codec *cs48l32_codec)
+{
+ struct regmap *regmap = cs48l32_codec->core.regmap;
+ unsigned int reg = CS48L32_EQ1_BAND1_COEFF1, mode;
+ __be16 *data;
+ int i, ret;
+
+ ret = regmap_read(regmap, CS48L32_EQ_CONTROL2, &mode);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev, "Error reading EQ mode: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < 4; ++i) {
+ cs48l32_codec->eq_mode[i] = (mode >> i) & 0x1;
+
+ data = &cs48l32_codec->eq_coefficients[i][0];
+ ret = regmap_raw_read(regmap, reg + (i * 68), data,
+ CS48L32_EQ_BLOCK_SZ);
+ if (ret < 0) {
+ dev_err(cs48l32_codec->core.dev,
+ "Error reading EQ coefficients: %d\n", ret);
+ goto out;
+ }
+ }
+
+out:
+ return ret;
+}
+
+static int cs48l32_component_probe(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+ int i, ret;
+
+ snd_soc_component_init_regmap(component, cs48l32_codec->core.regmap);
+
+ ret = cs48l32_init_inputs(component);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_dai); i++)
+ cs48l32_init_dai(cs48l32_codec, i);
+
+ ret = cs48l32_init_eq(cs48l32_codec);
+ if (ret)
+ return ret;
+
+ wm_adsp2_component_probe(&cs48l32_codec->dsp, component);
+
+ /* Unmask DSP IRQs */
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_clear_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ return 0;
+}
+
+static void cs48l32_component_remove(struct snd_soc_component *component)
+{
+ struct cs48l32_codec *cs48l32_codec = snd_soc_component_get_drvdata(component);
+
+ /* Mask DSP IRQs */
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_7,
+ CS48L32_DSP1_MPU_ERR_EINT1_MASK | CS48L32_DSP1_WDT_EXPIRE_EINT1_MASK);
+ regmap_set_bits(cs48l32_codec->core.regmap, CS48L32_IRQ1_MASK_9,
+ CS48L32_DSP1_IRQ0_EINT1_MASK);
+
+ wm_adsp2_component_remove(&cs48l32_codec->dsp, component);
+}
+
+static const struct snd_soc_component_driver cs48l32_soc_component_drv = {
+ .probe = &cs48l32_component_probe,
+ .remove = &cs48l32_component_remove,
+ .set_sysclk = &cs48l32_set_sysclk,
+ .set_pll = &cs48l32_set_fll,
+ .name = "cs48l32-codec",
+ .compress_ops = &cs48l32_compress_ops,
+ .controls = cs48l32_snd_controls,
+ .num_controls = ARRAY_SIZE(cs48l32_snd_controls),
+ .dapm_widgets = cs48l32_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs48l32_dapm_widgets),
+ .dapm_routes = cs48l32_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs48l32_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int cs48l32_prop_read_u32_array(struct cs48l32_codec *cs48l32_codec,
+ const char *propname,
+ u32 *dest,
+ int n_max)
+{
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ int ret;
+
+ ret = device_property_read_u32_array(cs48l32->dev, propname, dest, n_max);
+ if (ret == -EINVAL)
+ return -ENOENT;
+
+ if (ret < 0)
+ return dev_err_probe(cs48l32->dev, ret, "%s malformed\n", propname);
+
+ return 0;
+}
+
+static void cs48l32_prop_get_in_type(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,in-type";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT * CS48L32_MAX_IN_MUX_WAYS];
+ int i, in_idx, mux_way_idx, ret;
+
+ static_assert(ARRAY_SIZE(tmp) ==
+ ARRAY_SIZE(cs48l32_codec->in_type) * ARRAY_SIZE(cs48l32_codec->in_type[0]));
+
+ ret = cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+ if (ret < 0)
+ return;
+
+ in_idx = 0;
+ mux_way_idx = 0;
+ for (i = 0; i < ARRAY_SIZE(tmp); ++i) {
+ switch (tmp[i]) {
+ case CS48L32_IN_TYPE_DIFF:
+ case CS48L32_IN_TYPE_SE:
+ cs48l32_codec->in_type[in_idx][mux_way_idx] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, tmp[i]);
+ break;
+ }
+
+ /*
+ * Property array is [mux_way][in_channel]. Swap to
+ * [in_channel][mux_way] for convenience.
+ */
+ if (++in_idx == ARRAY_SIZE(cs48l32_codec->in_type)) {
+ in_idx = 0;
+ ++mux_way_idx;
+ }
+ }
+}
+
+static void cs48l32_prop_get_pdm_sup(struct cs48l32_codec *cs48l32_codec)
+{
+ const char *propname = "cirrus,pdm-sup";
+ u32 tmp[CS48L32_MAX_ANALOG_INPUT];
+ int i;
+
+ static_assert(ARRAY_SIZE(tmp) == ARRAY_SIZE(cs48l32_codec->pdm_sup));
+
+ cs48l32_prop_read_u32_array(cs48l32_codec, propname, tmp, ARRAY_SIZE(tmp));
+
+ for (i = 0; i < ARRAY_SIZE(cs48l32_codec->pdm_sup); i++) {
+ switch (tmp[i]) {
+ case CS48L32_PDM_SUP_VOUT_MIC:
+ case CS48L32_PDM_SUP_MICBIAS1:
+ cs48l32_codec->pdm_sup[i] = tmp[i];
+ break;
+ default:
+ dev_warn(cs48l32_codec->core.dev, "Illegal %s value %d ignored\n",
+ propname, cs48l32_codec->pdm_sup[i]);
+ break;
+ }
+ }
+}
+
+static void cs48l32_handle_properties(struct cs48l32_codec *cs48l32_codec)
+{
+ cs48l32_prop_get_in_type(cs48l32_codec);
+ cs48l32_prop_get_pdm_sup(cs48l32_codec);
+}
+
+static int cs48l32_request_interrupt(struct cs48l32_codec *cs48l32_codec)
+{
+ int irq = cs48l32_codec->core.irq;
+ int ret;
+
+ if (irq < 1)
+ return 0;
+
+ /*
+ * Don't use devm because this must be freed before destroying the
+ * rest of the driver
+ */
+ ret = request_threaded_irq(irq, NULL, cs48l32_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs48l32", cs48l32_codec);
+ if (ret)
+ return dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to get IRQ\n");
+
+ return 0;
+}
+
+static int cs48l32_create_codec_component(struct cs48l32_codec *cs48l32_codec)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ ASSERT_STRUCT_OFFSET(struct cs48l32_codec, dsp, 0);
+ static_assert(ARRAY_SIZE(cs48l32_dai) == ARRAY_SIZE(cs48l32_codec->dai));
+
+ cs48l32_handle_properties(cs48l32_codec);
+
+ dsp = &cs48l32_codec->dsp;
+ dsp->part = "cs48l32";
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->cs_dsp.dev = cs48l32_codec->core.dev;
+ dsp->cs_dsp.regmap = cs48l32_codec->core.regmap;
+ dsp->cs_dsp.base = CS48L32_DSP1_CLOCK_FREQ;
+ dsp->cs_dsp.base_sysinfo = CS48L32_DSP1_SYS_INFO_ID;
+ dsp->cs_dsp.mem = cs48l32_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs48l32_dsp1_regions);
+ dsp->pre_run = cs48l32_dsp_pre_run;
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0)
+ return ret;
+
+ cs48l32_codec->fll.codec = cs48l32_codec;
+ cs48l32_codec->fll.id = 1;
+ cs48l32_codec->fll.base = CS48L32_FLL1_CONTROL1;
+ cs48l32_codec->fll.sts_addr = CS48L32_IRQ1_STS_6;
+ cs48l32_codec->fll.sts_mask = CS48L32_FLL1_LOCK_STS1_MASK;
+ cs48l32_init_fll(&cs48l32_codec->fll);
+
+ ret = cs48l32_request_interrupt(cs48l32_codec);
+ if (ret)
+ goto err_dsp;
+
+ ret = devm_snd_soc_register_component(cs48l32_codec->core.dev,
+ &cs48l32_soc_component_drv,
+ cs48l32_dai,
+ ARRAY_SIZE(cs48l32_dai));
+ if (ret < 0) {
+ dev_err_probe(cs48l32_codec->core.dev, ret, "Failed to register component\n");
+ goto err_dsp;
+ }
+
+ return 0;
+
+err_dsp:
+ wm_adsp2_remove(&cs48l32_codec->dsp);
+
+ return ret;
+}
+
+static int cs48l32_wait_for_boot(struct cs48l32 *cs48l32)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read_poll_timeout(cs48l32->regmap, CS48L32_IRQ1_EINT_2, val,
+ ((val < 0xffffffff) && (val & CS48L32_BOOT_DONE_EINT1_MASK)),
+ 1000, CS48L32_BOOT_TIMEOUT_US);
+ if (ret) {
+ dev_err(cs48l32->dev, "BOOT_DONE timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_MCU_CTRL1, &val);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to read MCU_CTRL1: %d\n", ret);
+ return ret;
+ }
+
+ if (val & BIT(CS48L32_MCU_STS_SHIFT)) {
+ dev_err(cs48l32->dev, "MCU boot failed\n");
+ return -EIO;
+ }
+
+ pm_runtime_mark_last_busy(cs48l32->dev);
+
+ return 0;
+}
+
+static int cs48l32_soft_reset(struct cs48l32 *cs48l32)
+{
+ int ret;
+
+ ret = regmap_write(cs48l32->regmap, CS48L32_SFT_RESET, CS48L32_SFT_RESET_MAGIC);
+ if (ret != 0) {
+ dev_err(cs48l32->dev, "Failed to write soft reset: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ return 0;
+}
+
+static void cs48l32_enable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio)
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 0);
+}
+
+static void cs48l32_disable_hard_reset(struct cs48l32 *cs48l32)
+{
+ if (cs48l32->reset_gpio) {
+ gpiod_set_raw_value_cansleep(cs48l32->reset_gpio, 1);
+ usleep_range(CS48L32_HARD_RESET_MIN_US, CS48L32_HARD_RESET_MIN_US + 1000);
+ }
+}
+
+static int cs48l32_runtime_resume(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+ unsigned int val;
+ int ret;
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to enable VDD_D: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(CS48L32_SOFT_RESET_US, CS48L32_SOFT_RESET_US + 1000);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret)
+ goto err;
+
+ /* Check whether registers reset during suspend */
+ regmap_read(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, &val);
+ if (!val)
+ regcache_mark_dirty(cs48l32->regmap);
+ else
+ dev_dbg(cs48l32->dev, "Did not reset during suspend\n");
+
+ ret = regcache_sync(cs48l32->regmap);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return ret;
+}
+
+static int cs48l32_runtime_suspend(struct device *dev)
+{
+ struct cs48l32_codec *cs48l32_codec = dev_get_drvdata(dev);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Flag to detect if the registers reset during suspend */
+ regmap_write(cs48l32->regmap, CS48L32_CTRL_IF_DEBUG3, 1);
+
+ regcache_cache_only(cs48l32->regmap, true);
+ regulator_disable(cs48l32->vdd_d);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs48l32_pm_ops = {
+ RUNTIME_PM_OPS(cs48l32_runtime_suspend, cs48l32_runtime_resume, NULL)
+};
+
+static int cs48l32_configure_clk32k(struct cs48l32 *cs48l32)
+{
+ int ret = 0;
+
+ ret = clk_prepare_enable(cs48l32->mclk1);
+ if (ret)
+ return dev_err_probe(cs48l32->dev, ret, "Failed to enable 32k clock\n");
+
+ ret = regmap_update_bits(cs48l32->regmap, CS48L32_CLOCK32K,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_CLK_32K_SRC_MASK,
+ CS48L32_CLK_32K_EN_MASK | CS48L32_32K_MCLK1);
+ if (ret) {
+ clk_disable_unprepare(cs48l32->mclk1);
+ return dev_err_probe(cs48l32->dev, ret, "Failed to init 32k clock\n");
+ }
+
+ return 0;
+}
+
+static int cs48l32_get_clocks(struct cs48l32 *cs48l32)
+{
+ cs48l32->mclk1 = devm_clk_get_optional(cs48l32->dev, "mclk1");
+ if (IS_ERR(cs48l32->mclk1))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(cs48l32->mclk1),
+ "Failed to get mclk1\n");
+
+ return 0;
+}
+
+static int cs48l32_get_reset_gpio(struct cs48l32 *cs48l32)
+{
+ struct gpio_desc *reset;
+
+ reset = devm_gpiod_get_optional(cs48l32->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset))
+ return dev_err_probe(cs48l32->dev, PTR_ERR(reset), "Failed to request /RESET\n");
+
+ /* ACPI can override the GPIOD_OUT_LOW so ensure it starts low */
+ gpiod_set_raw_value_cansleep(reset, 0);
+
+ cs48l32->reset_gpio = reset;
+
+ return 0;
+}
+
+static int cs48l32_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ struct cs48l32_codec *cs48l32_codec;
+ struct cs48l32 *cs48l32;
+ unsigned int hwid, rev, otp_rev;
+ int i, ret;
+
+ cs48l32_codec = devm_kzalloc(&spi->dev, sizeof(*cs48l32_codec), GFP_KERNEL);
+ if (!cs48l32_codec)
+ return -ENOMEM;
+
+ cs48l32 = &cs48l32_codec->core;
+ cs48l32->dev = dev;
+ cs48l32->irq = spi->irq;
+ mutex_init(&cs48l32_codec->rate_lock);
+ cs48l32_codec->in_vu_reg = CS48L32_INPUT_CONTROL3;
+
+ dev_set_drvdata(cs48l32->dev, cs48l32_codec);
+
+ ret = cs48l32_create_regmap(spi, cs48l32);
+ if (ret)
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate regmap\n");
+
+ regcache_cache_only(cs48l32->regmap, true);
+
+ ret = cs48l32_get_reset_gpio(cs48l32);
+ if (ret)
+ return ret;
+
+ ret = cs48l32_get_clocks(cs48l32);
+ if (ret)
+ return ret;
+
+ static_assert(ARRAY_SIZE(cs48l32_core_supplies) == ARRAY_SIZE(cs48l32->core_supplies));
+ for (i = 0; i < ARRAY_SIZE(cs48l32->core_supplies); i++)
+ cs48l32->core_supplies[i].supply = cs48l32_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(cs48l32->core_supplies),
+ cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request core supplies\n");
+
+ cs48l32->vdd_d = devm_regulator_get(cs48l32->dev, "vdd-d");
+ if (IS_ERR(cs48l32->vdd_d))
+ return dev_err_probe(dev, PTR_ERR(cs48l32->vdd_d), "Failed to request vdd-d\n");
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable core supplies\n");
+
+ ret = regulator_enable(cs48l32->vdd_d);
+ if (ret) {
+ dev_err(dev, "Failed to enable vdd-d: %d\n", ret);
+ goto err_enable;
+ }
+
+ cs48l32_disable_hard_reset(cs48l32);
+
+ regcache_cache_only(cs48l32->regmap, false);
+
+ /* If we don't have a reset GPIO use a soft reset */
+ if (!cs48l32->reset_gpio) {
+ ret = cs48l32_soft_reset(cs48l32);
+ if (ret)
+ goto err_reset;
+ }
+
+ ret = cs48l32_wait_for_boot(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Device failed initial boot: %d\n", ret);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_DEVID, &hwid);
+ if (ret) {
+ dev_err(dev, "Failed to read ID register: %d\n", ret);
+ goto err_reset;
+ }
+ hwid &= CS48L32_DEVID_MASK;
+
+ switch (hwid) {
+ case CS48L32_SILICON_ID:
+ break;
+ default:
+ ret = -ENODEV;
+ dev_err_probe(cs48l32->dev, ret, "Unknown device ID: %#x\n", hwid);
+ goto err_reset;
+ }
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_REVID, &rev);
+ if (ret) {
+ dev_err(dev, "Failed to read revision register: %d\n", ret);
+ goto err_reset;
+ }
+ rev &= CS48L32_AREVID_MASK | CS48L32_MTLREVID_MASK;
+
+ ret = regmap_read(cs48l32->regmap, CS48L32_OTPID, &otp_rev);
+ if (ret) {
+ dev_err(dev, "Failed to read OTP revision register: %d\n", ret);
+ goto err_reset;
+ }
+ otp_rev &= CS48L32_OTPID_MASK;
+
+ dev_info(dev, "CS48L%x revision %X%u OTP%u\n", hwid & 0xff,
+ rev >> CS48L32_AREVID_SHIFT, rev & CS48L32_MTLREVID_MASK, otp_rev);
+
+ /* Apply hardware patch */
+ ret = cs48l32_apply_patch(cs48l32);
+ if (ret) {
+ dev_err(cs48l32->dev, "Failed to apply patch %d\n", ret);
+ goto err_reset;
+ }
+
+ /* BOOT_DONE interrupt is unmasked by default, so mask it */
+ ret = regmap_set_bits(cs48l32->regmap, CS48L32_IRQ1_MASK_2, CS48L32_BOOT_DONE_EINT1_MASK);
+
+ ret = cs48l32_configure_clk32k(cs48l32);
+ if (ret)
+ goto err_reset;
+
+ pm_runtime_set_active(cs48l32->dev);
+ pm_runtime_set_autosuspend_delay(cs48l32->dev, 100);
+ pm_runtime_use_autosuspend(cs48l32->dev);
+ pm_runtime_enable(cs48l32->dev);
+
+ ret = cs48l32_create_codec_component(cs48l32_codec);
+ if (ret)
+ goto err_clk32k;
+
+ return 0;
+
+err_clk32k:
+ clk_disable_unprepare(cs48l32->mclk1);
+err_reset:
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_disable(cs48l32->vdd_d);
+err_enable:
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ return ret;
+}
+
+static void cs48l32_spi_remove(struct spi_device *spi)
+{
+ struct cs48l32_codec *cs48l32_codec = spi_get_drvdata(spi);
+ struct cs48l32 *cs48l32 = &cs48l32_codec->core;
+
+ /* Remove IRQ handler before destroying anything else */
+ if (cs48l32->irq >= 1)
+ free_irq(cs48l32->irq, cs48l32_codec);
+
+ pm_runtime_disable(cs48l32->dev);
+ regulator_disable(cs48l32->vdd_d);
+ clk_disable_unprepare(cs48l32->mclk1);
+ cs48l32_enable_hard_reset(cs48l32);
+ regulator_bulk_disable(ARRAY_SIZE(cs48l32->core_supplies), cs48l32->core_supplies);
+
+ mutex_destroy(&cs48l32_codec->rate_lock);
+}
+
+static const struct of_device_id cs48l32_of_match[] = {
+ { .compatible = "cirrus,cs48l32", },
+ {},
+};
+
+static const struct spi_device_id cs48l32_spi_ids[] = {
+ { "cs48l32", },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, cs48l32_spi_ids);
+
+static struct spi_driver cs48l32_spi_driver = {
+ .driver = {
+ .name = "cs48l32",
+ .pm = pm_ptr(&cs48l32_pm_ops),
+ .of_match_table = cs48l32_of_match,
+ },
+ .probe = &cs48l32_spi_probe,
+ .remove = &cs48l32_spi_remove,
+ .id_table = cs48l32_spi_ids,
+};
+module_spi_driver(cs48l32_spi_driver);
+
+MODULE_DESCRIPTION("CS48L32 ASoC codec driver");
+MODULE_AUTHOR("Stuart Henderson <stuarth@opensource.cirrus.com>");
+MODULE_AUTHOR("Piotr Stankiewicz <piotrs@opensource.cirrus.com>");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs48l32.h b/sound/soc/codecs/cs48l32.h
new file mode 100644
index 000000000000..c1b4e13feae4
--- /dev/null
+++ b/sound/soc/codecs/cs48l32.h
@@ -0,0 +1,403 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Cirrus Logic CS48L32 audio DSP.
+ *
+ * Copyright (C) 2016-2018, 2020, 2022, 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+#ifndef SND_SOC_CS48L32_H
+#define SND_SOC_CS48L32_H
+
+#include <linux/bits.h>
+#include <sound/soc.h>
+#include "wm_adsp.h"
+
+#define CS48L32_SILICON_ID 0x48a32
+
+#define CS48L32_32K_MCLK1 0
+
+#define CS48L32_SFT_RESET_MAGIC 0x5a000000
+#define CS48L32_SOFT_RESET_US 2000
+#define CS48L32_HARD_RESET_MIN_US 1000
+
+#define CS48L32_SEEN_BOOT_DONE BIT(0)
+#define CS48L32_BOOT_TIMEOUT_US 25000
+
+#define CS48L32_ASP_ENABLES1 0x00
+#define CS48L32_ASP_CONTROL1 0x04
+#define CS48L32_ASP_CONTROL2 0x08
+#define CS48L32_ASP_CONTROL3 0x0c
+#define CS48L32_ASP_FRAME_CONTROL1 0x10
+#define CS48L32_ASP_FRAME_CONTROL2 0x14
+#define CS48L32_ASP_FRAME_CONTROL5 0x20
+#define CS48L32_ASP_FRAME_CONTROL6 0x24
+#define CS48L32_ASP_DATA_CONTROL1 0x30
+#define CS48L32_ASP_DATA_CONTROL5 0x40
+#define CS48L32_SYSCLK_RATE_6MHZ 0
+#define CS48L32_SYSCLK_RATE_12MHZ 1
+#define CS48L32_SYSCLK_RATE_24MHZ 2
+#define CS48L32_SYSCLK_RATE_49MHZ 3
+#define CS48L32_SYSCLK_RATE_98MHZ 4
+#define CS48L32_FLLHJ_INT_MAX_N 1023
+#define CS48L32_FLLHJ_INT_MIN_N 1
+#define CS48L32_FLLHJ_FRAC_MAX_N 255
+#define CS48L32_FLLHJ_FRAC_MIN_N 2
+#define CS48L32_FLLHJ_LP_INT_MODE_THRESH 100000
+#define CS48L32_FLLHJ_LOW_THRESH 192000
+#define CS48L32_FLLHJ_MID_THRESH 1152000
+#define CS48L32_FLLHJ_MAX_THRESH 13000000
+#define CS48L32_FLLHJ_LOW_GAINS 0x23f0
+#define CS48L32_FLLHJ_MID_GAINS 0x22f2
+#define CS48L32_FLLHJ_HIGH_GAINS 0x21f0
+#define CS48L32_FLL_MAX_FOUT 50000000
+#define CS48L32_FLL_MAX_REFDIV 8
+#define CS48L32_FLL_CONTROL1_OFFS 0x00
+#define CS48L32_FLL_CONTROL2_OFFS 0x04
+#define CS48L32_FLL_CONTROL3_OFFS 0x08
+#define CS48L32_FLL_CONTROL4_OFFS 0x0c
+#define CS48L32_FLL_CONTROL5_OFFS 0x10
+#define CS48L32_FLL_CONTROL6_OFFS 0x14
+#define CS48L32_FLL_DIGITAL_TEST2_OFFS 0x34
+#define CS48L32_FLL_GPIO_CLOCK_OFFS 0xa0
+#define CS48L32_DSP_CLOCK_FREQ_OFFS 0x00000
+#define CS48L32_ASP_FMT_DSP_MODE_A 0
+#define CS48L32_ASP_FMT_DSP_MODE_B 1
+#define CS48L32_ASP_FMT_I2S_MODE 2
+#define CS48L32_ASP_FMT_LEFT_JUSTIFIED_MODE 3
+#define CS48L32_HALO_SAMPLE_RATE_RX1 0x00080
+#define CS48L32_HALO_SAMPLE_RATE_TX1 0x00280
+#define CS48L32_HALO_DSP_RATE_MASK 0x1f
+
+#define CS48L32_PDMCLK_SRC_IN1_PDMCLK 0x0
+#define CS48L32_PDMCLK_SRC_IN2_PDMCLK 0x1
+#define CS48L32_PDMCLK_SRC_IN3_PDMCLK 0x2
+#define CS48L32_PDMCLK_SRC_IN4_PDMCLK 0x3
+#define CS48L32_PDMCLK_SRC_AUXPDM1_CLK 0x8
+#define CS48L32_PDMCLK_SRC_AUXPDM2_CLK 0x9
+
+#define CS48L32_MAX_DAI 6
+#define CS48L32_MAX_INPUT 4
+#define CS48L32_MAX_ANALOG_INPUT 2
+#define CS48L32_MAX_IN_MUX_WAYS 2
+#define CS48L32_MAX_ASP 2
+
+#define CS48L32_EQ_BLOCK_SZ 60
+#define CS48L32_N_EQ_BLOCKS 4
+
+#define CS48L32_DSP_N_RX_CHANNELS 8
+#define CS48L32_DSP_N_TX_CHANNELS 8
+
+#define CS48L32_LHPF_MAX_COEFF 4095
+#define CS48L32_EQ_MAX_COEFF 4095
+
+#define CS48L32_MIXER_CONTROLS(name, base) \
+ SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 2 Volume", base + 4, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 3 Volume", base + 8, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name " Input 4 Volume", base + 12, \
+ CS48L32_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs48l32_mixer_tlv)
+
+#define CS48L32_MUX_ENUM_DECL(name, reg) \
+ SOC_VALUE_ENUM_SINGLE_DECL( \
+ name, reg, 0, CS48L32_MIXER_SRC_MASK, \
+ cs48l32_mixer_texts, cs48l32_mixer_values)
+
+#define CS48L32_MUX_CTL_DECL(name) \
+ const struct snd_kcontrol_new name##_mux = SOC_DAPM_ENUM("Route", name##_enum)
+
+#define CS48L32_MUX_ENUMS(name, base_reg) \
+ static CS48L32_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static CS48L32_MUX_CTL_DECL(name)
+
+#define CS48L32_MIXER_ENUMS(name, base_reg) \
+ CS48L32_MUX_ENUMS(name##_in1, base_reg); \
+ CS48L32_MUX_ENUMS(name##_in2, base_reg + 4); \
+ CS48L32_MUX_ENUMS(name##_in3, base_reg + 8); \
+ CS48L32_MUX_ENUMS(name##_in4, base_reg + 12)
+
+#define CS48L32_MUX(name, ctrl) SND_SOC_DAPM_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+
+#define CS48L32_MUX_WIDGETS(name, name_str) CS48L32_MUX(name_str " Input 1", &name##_mux)
+
+#define CS48L32_MIXER_WIDGETS(name, name_str) \
+ CS48L32_MUX(name_str " Input 1", &name##_in1_mux), \
+ CS48L32_MUX(name_str " Input 2", &name##_in2_mux), \
+ CS48L32_MUX(name_str " Input 3", &name##_in3_mux), \
+ CS48L32_MUX(name_str " Input 4", &name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define CS48L32_MUX_ROUTES(widget, name) \
+ { widget, NULL, name " Input 1" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1")
+
+#define CS48L32_MIXER_ROUTES(widget, name) \
+ { widget, NULL, name " Mixer" }, \
+ { name " Mixer", NULL, name " Input 1" }, \
+ { name " Mixer", NULL, name " Input 2" }, \
+ { name " Mixer", NULL, name " Input 3" }, \
+ { name " Mixer", NULL, name " Input 4" }, \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 1"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 2"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 3"), \
+ CS48L32_MIXER_INPUT_ROUTES(name " Input 4")
+
+#define CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+ { name, NULL, name " Preloader" }, \
+ { name, NULL, "SYSCLK" }, \
+ { name " Preload", NULL, name " Preloader" }, \
+ CS48L32_MIXER_ROUTES(name, name "RX1"), \
+ CS48L32_MIXER_ROUTES(name, name "RX2"), \
+ CS48L32_MIXER_ROUTES(name, name "RX3"), \
+ CS48L32_MIXER_ROUTES(name, name "RX4"), \
+ CS48L32_MIXER_ROUTES(name, name "RX5"), \
+ CS48L32_MIXER_ROUTES(name, name "RX6"), \
+ CS48L32_MIXER_ROUTES(name, name "RX7"), \
+ CS48L32_MIXER_ROUTES(name, name "RX8") \
+
+#define CS48L32_DSP_ROUTES_1_8(name) \
+ { name, NULL, "DSPCLK" }, \
+ CS48L32_DSP_ROUTES_1_8_SYSCLK(name) \
+
+#define CS48L32_RATE_CONTROL(name, domain) SOC_ENUM(name, cs48l32_sample_rate[(domain) - 1])
+
+#define CS48L32_RATE_ENUM(name, enum) \
+ SOC_ENUM_EXT(name, enum, snd_soc_get_enum_double, cs48l32_rate_put)
+
+#define CS48L32_DSP_RATE_CONTROL(name, num) \
+ SOC_ENUM_EXT(name " Rate", cs48l32_dsp_rate_enum[num], \
+ cs48l32_dsp_rate_get, cs48l32_dsp_rate_put)
+
+#define CS48L32_EQ_COEFF_CONTROL(xname, xreg, xbase, xshift) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = cs48l32_eq_coeff_info, .get = cs48l32_eq_coeff_get, \
+ .put = cs48l32_eq_coeff_put, .private_value = \
+ (unsigned long)&(struct cs48l32_eq_control) { .reg = xreg,\
+ .shift = xshift, .block_base = xbase, .max = 65535 } }
+
+#define CS48L32_EQ_REG_NAME_PASTER(eq, band, type) \
+ CS48L32_ ## eq ## _ ## band ## _ ## type
+
+#define CS48L32_EQ_BAND_COEFF_CONTROLS(name, band) \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " C", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, COEFF2), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " " #band " PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, band, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_EQ_COEFF_CONTROLS(name) \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND1), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND2), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND3), \
+ CS48L32_EQ_BAND_COEFF_CONTROLS(name, BAND4), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 A", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 B", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, COEFF1), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 16), \
+ CS48L32_EQ_COEFF_CONTROL(#name " BAND5 PG", \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND5, PG), \
+ CS48L32_EQ_REG_NAME_PASTER(name, BAND1, COEFF1), \
+ 0)
+
+#define CS48L32_LHPF_CONTROL(xname, xbase) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+ .info = snd_soc_bytes_info, .get = snd_soc_bytes_get, \
+ .put = cs48l32_lhpf_coeff_put, .private_value = \
+ ((unsigned long)&(struct soc_bytes) { .base = xbase, \
+ .num_regs = 1 }) }
+
+/* these have a subseq number so they run after SYSCLK and DSPCLK widgets */
+#define CS48L32_DSP_FREQ_WIDGET_EV(name, num, event) \
+ SND_SOC_DAPM_SUPPLY_S(name "FREQ", 100, SND_SOC_NOPM, num, 0, \
+ event, SND_SOC_DAPM_POST_PMU)
+
+#define CS48L32_RATES SNDRV_PCM_RATE_KNOT
+
+#define CS48L32_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS48L32_MIXER_INPUT_ROUTES(name) \
+ { name, "Tone Generator 1", "Tone Generator 1" }, \
+ { name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Noise Generator", "Noise Generator" }, \
+ { name, "IN1L", "IN1L PGA" }, \
+ { name, "IN1R", "IN1R PGA" }, \
+ { name, "IN2L", "IN2L PGA" }, \
+ { name, "IN2R", "IN2R PGA" }, \
+ { name, "ASP1RX1", "ASP1RX1" }, \
+ { name, "ASP1RX2", "ASP1RX2" }, \
+ { name, "ASP1RX3", "ASP1RX3" }, \
+ { name, "ASP1RX4", "ASP1RX4" }, \
+ { name, "ASP1RX5", "ASP1RX5" }, \
+ { name, "ASP1RX6", "ASP1RX6" }, \
+ { name, "ASP1RX7", "ASP1RX7" }, \
+ { name, "ASP1RX8", "ASP1RX8" }, \
+ { name, "ASP2RX1", "ASP2RX1" }, \
+ { name, "ASP2RX2", "ASP2RX2" }, \
+ { name, "ASP2RX3", "ASP2RX3" }, \
+ { name, "ASP2RX4", "ASP2RX4" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+ { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC1INT3", "ISRC1INT3" }, \
+ { name, "ISRC1INT4", "ISRC1INT4" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
+ { name, "ISRC3DEC1", "ISRC3DEC1" }, \
+ { name, "ISRC3DEC2", "ISRC3DEC2" }, \
+ { name, "ISRC3INT1", "ISRC3INT1" }, \
+ { name, "ISRC3INT2", "ISRC3INT2" }, \
+ { name, "EQ1", "EQ1" }, \
+ { name, "EQ2", "EQ2" }, \
+ { name, "EQ3", "EQ3" }, \
+ { name, "EQ4", "EQ4" }, \
+ { name, "DRC1L", "DRC1L" }, \
+ { name, "DRC1R", "DRC1R" }, \
+ { name, "DRC2L", "DRC2L" }, \
+ { name, "DRC2R", "DRC2R" }, \
+ { name, "LHPF1", "LHPF1" }, \
+ { name, "LHPF2", "LHPF2" }, \
+ { name, "LHPF3", "LHPF3" }, \
+ { name, "LHPF4", "LHPF4" }, \
+ { name, "Ultrasonic 1", "Ultrasonic 1" }, \
+ { name, "Ultrasonic 2", "Ultrasonic 2" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }, \
+ { name, "DSP1.7", "DSP1" }, \
+ { name, "DSP1.8", "DSP1" }
+
+struct cs48l32_enum {
+ struct soc_enum mixer_enum;
+ int val;
+};
+
+struct cs48l32_eq_control {
+ unsigned int reg;
+ unsigned int shift;
+ unsigned int block_base;
+ unsigned int max;
+};
+
+struct cs48l32_dai_priv {
+ int clk;
+ struct snd_pcm_hw_constraint_list constraint;
+};
+
+struct cs48l32_dsp_power_reg_block {
+ unsigned int start;
+ unsigned int end;
+};
+
+struct cs48l32_dsp_power_regs {
+ const unsigned int *pwd;
+ unsigned int n_pwd;
+ const struct cs48l32_dsp_power_reg_block *ext;
+ unsigned int n_ext;
+};
+
+struct cs48l32;
+struct cs48l32_codec;
+struct spi_device;
+
+struct cs48l32_fll_cfg {
+ int n;
+ unsigned int theta;
+ unsigned int lambda;
+ int refdiv;
+ int fratio;
+ int gain;
+ int alt_gain;
+};
+
+struct cs48l32_fll {
+ struct cs48l32_codec *codec;
+ int id;
+ unsigned int base;
+
+ unsigned int sts_addr;
+ unsigned int sts_mask;
+ unsigned int fout;
+ int ref_src;
+ unsigned int ref_freq;
+
+ struct cs48l32_fll_cfg ref_cfg;
+};
+
+struct cs48l32_codec {
+ struct wm_adsp dsp; /* must be first */
+ struct cs48l32 core;
+ int sysclk;
+ int dspclk;
+ struct cs48l32_dai_priv dai[CS48L32_MAX_DAI];
+ struct cs48l32_fll fll;
+
+ unsigned int in_up_pending;
+ unsigned int in_vu_reg;
+
+ struct mutex rate_lock;
+
+ u8 dsp_dma_rates[CS48L32_DSP_N_RX_CHANNELS + CS48L32_DSP_N_TX_CHANNELS];
+
+ u8 in_type[CS48L32_MAX_ANALOG_INPUT][CS48L32_MAX_IN_MUX_WAYS];
+ u8 pdm_sup[CS48L32_MAX_ANALOG_INPUT];
+ u8 tdm_width[CS48L32_MAX_ASP];
+ u8 tdm_slots[CS48L32_MAX_ASP];
+
+ unsigned int eq_mode[CS48L32_N_EQ_BLOCKS];
+ __be16 eq_coefficients[CS48L32_N_EQ_BLOCKS][CS48L32_EQ_BLOCK_SZ / 2];
+
+ const struct cs48l32_dsp_power_regs *dsp_power_regs;
+};
+
+#define cs48l32_fll_err(_fll, fmt, ...) \
+ dev_err(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_warn(_fll, fmt, ...) \
+ dev_warn(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+#define cs48l32_fll_dbg(_fll, fmt, ...) \
+ dev_dbg(_fll->codec->core.dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+
+#define cs48l32_asp_err(_dai, fmt, ...) \
+ dev_err(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_warn(_dai, fmt, ...) \
+ dev_warn(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+#define cs48l32_asp_dbg(_dai, fmt, ...) \
+ dev_dbg(_dai->component->dev, "ASP%d: " fmt, _dai->id, ##__VA_ARGS__)
+
+int cs48l32_apply_patch(struct cs48l32 *cs48l32);
+int cs48l32_create_regmap(struct spi_device *spi, struct cs48l32 *cs48l32);
+int cs48l32_enable_asp1_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_enable_asp2_pins(struct cs48l32_codec *cs48l32_codec);
+int cs48l32_micvdd_voltage_index(u32 voltage);
+int cs48l32_micbias1_voltage_index(u32 voltage);
+
+#endif
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index 5f2f67e3bd29..a7539e1a1893 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -3033,7 +3033,7 @@ static const struct snd_soc_component_driver soc_component_dev_da7218 = {
* Regmap configs
*/
-static struct reg_default da7218_reg_defaults[] = {
+static const struct reg_default da7218_reg_defaults[] = {
{ DA7218_SYSTEM_ACTIVE, 0x00 },
{ DA7218_CIF_CTRL, 0x00 },
{ DA7218_SPARE1, 0x00 },
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 3958e88a2445..1742f91c788c 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -1982,8 +1982,8 @@ static unsigned long da7219_wclk_recalc_rate(struct clk_hw *hw,
}
}
-static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int da7219_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv,
@@ -1992,28 +1992,30 @@ static long da7219_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
if (!da7219->master)
return -EINVAL;
- if (rate < 11025)
- return 8000;
- else if (rate < 12000)
- return 11025;
- else if (rate < 16000)
- return 12000;
- else if (rate < 22050)
- return 16000;
- else if (rate < 24000)
- return 22050;
- else if (rate < 32000)
- return 24000;
- else if (rate < 44100)
- return 32000;
- else if (rate < 48000)
- return 44100;
- else if (rate < 88200)
- return 48000;
- else if (rate < 96000)
- return 88200;
+ if (req->rate < 11025)
+ req->rate = 8000;
+ else if (req->rate < 12000)
+ req->rate = 11025;
+ else if (req->rate < 16000)
+ req->rate = 12000;
+ else if (req->rate < 22050)
+ req->rate = 16000;
+ else if (req->rate < 24000)
+ req->rate = 22050;
+ else if (req->rate < 32000)
+ req->rate = 24000;
+ else if (req->rate < 44100)
+ req->rate = 32000;
+ else if (req->rate < 48000)
+ req->rate = 44100;
+ else if (req->rate < 88200)
+ req->rate = 48000;
+ else if (req->rate < 96000)
+ req->rate = 88200;
else
- return 96000;
+ req->rate = 96000;
+
+ return 0;
}
static int da7219_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -2070,15 +2072,15 @@ static unsigned long da7219_bclk_get_factor(unsigned long rate,
return 256;
}
-static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int da7219_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct da7219_priv *da7219 =
container_of(hw, struct da7219_priv,
dai_clks_hw[DA7219_DAI_BCLK_IDX]);
unsigned long factor;
- if (!*parent_rate || !da7219->master)
+ if (!req->best_parent_rate || !da7219->master)
return -EINVAL;
/*
@@ -2088,9 +2090,11 @@ static long da7219_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
* parent WCLK rate set and find the appropriate multiplier of BCLK to
* get the rounded down BCLK value.
*/
- factor = da7219_bclk_get_factor(rate, *parent_rate);
+ factor = da7219_bclk_get_factor(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
- return *parent_rate * factor;
+ return 0;
}
static int da7219_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -2116,12 +2120,12 @@ static const struct clk_ops da7219_dai_clk_ops[DA7219_DAI_NUM_CLKS] = {
.unprepare = da7219_wclk_unprepare,
.is_prepared = da7219_wclk_is_prepared,
.recalc_rate = da7219_wclk_recalc_rate,
- .round_rate = da7219_wclk_round_rate,
+ .determine_rate = da7219_wclk_determine_rate,
.set_rate = da7219_wclk_set_rate,
},
[DA7219_DAI_BCLK_IDX] = {
.recalc_rate = da7219_bclk_recalc_rate,
- .round_rate = da7219_bclk_round_rate,
+ .determine_rate = da7219_bclk_determine_rate,
.set_rate = da7219_bclk_set_rate,
},
};
@@ -2312,7 +2316,7 @@ static void da7219_handle_pdata(struct snd_soc_component *component)
* Regmap configs
*/
-static struct reg_default da7219_reg_defaults[] = {
+static const struct reg_default da7219_reg_defaults[] = {
{ DA7219_MIC_1_SELECT, 0x00 },
{ DA7219_CIF_TIMEOUT_CTRL, 0x01 },
{ DA7219_SR_24_48, 0x00 },
@@ -2443,7 +2447,7 @@ static const struct regmap_config da7219_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-static struct reg_sequence da7219_rev_aa_patch[] = {
+static const struct reg_sequence da7219_rev_aa_patch[] = {
{ DA7219_REFERENCES, 0x08 },
};
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 066d92b54312..78c4e68f6002 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -1079,8 +1079,7 @@ static void es8326_init(struct snd_soc_component *component)
regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
regmap_write(es8326->regmap, ES8326_INTOUT_IO,
es8326->interrupt_clk);
- regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
- (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
new file mode 100644
index 000000000000..36b0ebdce514
--- /dev/null
+++ b/sound/soc/codecs/es8375.c
@@ -0,0 +1,794 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8375.c -- ES8375 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.com)
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+#include <linux/acpi.h>
+#include "es8375.h"
+
+struct es8375_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ struct regulator_bulk_data core_supply[2];
+ unsigned int mclk_freq;
+ int mastermode;
+ u8 mclk_src;
+ u8 vddd;
+ enum snd_soc_bias_level bias_level;
+};
+
+static const char * const es8375_core_supplies[] = {
+ "vddd",
+ "vdda",
+};
+
+static const DECLARE_TLV_DB_SCALE(es8375_adc_osr_gain_tlv, -3100, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_automute_attn_tlv, 0, 100, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_adc_dmic_volume_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_volume_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_vppscale_tlv, -388, 12, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_dac_automute_attn_tlv, 0, 400, 0);
+static const DECLARE_TLV_DB_SCALE(es8375_automute_ng_tlv, -9600, 600, 0);
+
+static const char *const es8375_ramprate_txt[] = {
+ "0.125dB/LRCK",
+ "0.125dB/2LRCK",
+ "0.125dB/4LRCK",
+ "0.125dB/8LRCK",
+ "0.125dB/16LRCK",
+ "0.125dB/32LRCK",
+ "0.125dB/64LRCK",
+ "0.125dB/128LRCK",
+ "disable softramp",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_ramprate, ES8375_ADC2,
+ ADC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_ramprate, ES8375_DAC2,
+ DAC_RAMPRATE_SHIFT_0, es8375_ramprate_txt);
+
+static const char *const es8375_automute_ws_txt[] = {
+ "256 samples",
+ "512 samples",
+ "1024 samples",
+ "2048 samples",
+ "4096 samples",
+ "8192 samples",
+ "16384 samples",
+ "32768 samples",
+};
+static SOC_ENUM_SINGLE_DECL(es8375_adc_automute_ws, ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_WS_SHIFT_3, es8375_automute_ws_txt);
+static SOC_ENUM_SINGLE_DECL(es8375_dac_automute_ws, ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_WS_SHIFT_5, es8375_automute_ws_txt);
+
+static const char *const es8375_dmic_pol_txt[] = {
+ "Low",
+ "High",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_dmic_pol, ES8375_ADC1,
+ DMIC_POL_SHIFT_4, es8375_dmic_pol_txt);
+
+static const char *const es8375_adc_hpf_txt[] = {
+ "Freeze Offset",
+ "Dynamic HPF",
+};
+
+static SOC_ENUM_SINGLE_DECL(es8375_adc_hpf, ES8375_HPF1,
+ ADC_HPF_SHIFT_5, es8375_adc_hpf_txt);
+
+static const char *const es8375_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+static const struct soc_enum es8375_dmic_mux_enum =
+ SOC_ENUM_SINGLE(ES8375_ADC1, ADC_SRC_SHIFT_7,
+ ARRAY_SIZE(es8375_dmic_mux_txt), es8375_dmic_mux_txt);
+
+static const struct snd_kcontrol_new es8375_dmic_mux_controls =
+ SOC_DAPM_ENUM("ADC MUX", es8375_dmic_mux_enum);
+
+static const struct snd_kcontrol_new es8375_snd_controls[] = {
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8375_ADC_OSR_GAIN,
+ ADC_OSR_GAIN_SHIFT_0, ES8375_ADC_OSR_GAIN_MAX, 0,
+ es8375_adc_osr_gain_tlv),
+ SOC_SINGLE("ADC Invert Switch", ES8375_ADC1, ADC_INV_SHIFT_6, 1, 0),
+ SOC_SINGLE("ADC RAM Clear", ES8375_ADC1, ADC_RAMCLR_SHIFT_5, 1, 0),
+ SOC_ENUM("DMIC Polarity", es8375_dmic_pol),
+ SOC_SINGLE_TLV("DMIC Volume", ES8375_ADC1,
+ DMIC_GAIN_SHIFT_2, ES8375_DMIC_GAIN_MAX,
+ 0, es8375_adc_dmic_volume_tlv),
+ SOC_ENUM("ADC Ramp Rate", es8375_adc_ramprate),
+ SOC_SINGLE_TLV("ADC Volume", ES8375_ADC_VOLUME,
+ ADC_VOLUME_SHIFT_0, ES8375_ADC_VOLUME_MAX,
+ 0, es8375_adc_volume_tlv),
+ SOC_SINGLE("ADC Automute Switch", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_SHIFT_7, 1, 0),
+ SOC_ENUM("ADC Automute Winsize", es8375_adc_automute_ws),
+ SOC_SINGLE_TLV("ADC Automute Noise Gate", ES8375_ADC_AUTOMUTE,
+ ADC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_SINGLE_TLV("ADC Automute Volume", ES8375_ADC_AUTOMUTE_ATTN,
+ ADC_AUTOMUTE_ATTN_SHIFT_0, ES8375_ADC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_adc_automute_attn_tlv),
+ SOC_ENUM("ADC HPF", es8375_adc_hpf),
+
+ SOC_SINGLE("DAC DSM Mute Switch", ES8375_DAC1, DAC_DSMMUTE_SHIFT_7, 1, 0),
+ SOC_SINGLE("DAC DEM Mute Switch", ES8375_DAC1, DAC_DEMMUTE_SHIFT_6, 1, 0),
+ SOC_SINGLE("DAC Invert Switch", ES8375_DAC1, DAC_INV_SHIFT_5, 1, 0),
+ SOC_SINGLE("DAC RAM Clear", ES8375_DAC1, DAC_RAMCLR_SHIFT_4, 1, 0),
+ SOC_ENUM("DAC Ramp Rate", es8375_dac_ramprate),
+ SOC_SINGLE_TLV("DAC Volume", ES8375_DAC_VOLUME,
+ DAC_VOLUME_SHIFT_0, ES8375_DAC_VOLUME_MAX,
+ 0, es8375_dac_volume_tlv),
+ SOC_SINGLE_TLV("DAC VPP Scale", ES8375_DAC_VPPSCALE,
+ DAC_VPPSCALE_SHIFT_0, ES8375_DAC_VPPSCALE_MAX,
+ 0, es8375_dac_vppscale_tlv),
+ SOC_SINGLE("DAC Automute Switch", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_EN_SHIFT_7, 1, 0),
+ SOC_SINGLE_TLV("DAC Automute Noise Gate", ES8375_DAC_AUTOMUTE1,
+ DAC_AUTOMUTE_NG_SHIFT_0, ES8375_AUTOMUTE_NG_MAX,
+ 0, es8375_automute_ng_tlv),
+ SOC_ENUM("DAC Automute Winsize", es8375_dac_automute_ws),
+ SOC_SINGLE_TLV("DAC Automute Volume", ES8375_DAC_AUTOMUTE,
+ DAC_AUTOMUTE_ATTN_SHIFT_0, ES8375_DAC_AUTOMUTE_ATTN_MAX,
+ 0, es8375_dac_automute_attn_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8375_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGA", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_ADC("Mono ADC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, ES8375_SDP2,
+ ES8375_ADC_P2S_MUTE_SHIFT_5, 1),
+
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8375_dmic_mux_controls),
+
+ SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, ES8375_SDP,
+ SND_SOC_NOPM, 0),
+ SND_SOC_DAPM_DAC("Mono DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+};
+
+static const struct snd_soc_dapm_route es8375_dapm_routes[] = {
+ {"ADC MUX", "AMIC", "MIC1"},
+ {"ADC MUX", "DMIC", "DMIC"},
+ {"PGA", NULL, "ADC MUX"},
+ {"Mono ADC", NULL, "PGA"},
+ {"AIF1TX", NULL, "Mono ADC"},
+
+ {"Mono DAC", NULL, "AIF1RX"},
+ {"OUT", NULL, "Mono DAC"},
+};
+
+struct _coeff_div {
+ u16 mclk_lrck_ratio;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0B;
+ u8 Reg0x19;
+ u8 dvdd_vol;
+ u8 dmic_sel;
+};
+
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x95, 0x00, 0x1F, 2, 2},
+ {32, 512000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {32, 1536000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {36, 288000, 8000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x95, 0x00, 0x1F, 2, 2},
+ {36, 576000, 16000, 0x05, 0x34, 0xDD, 0x55, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {36, 1728000, 48000, 0x05, 0x33, 0xD5, 0x55, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {48, 384000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 768000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x17, 0x20, 0x94, 0x00, 0x28, 2, 2},
+ {48, 2304000, 48000, 0x05, 0x11, 0x53, 0x55, 0x17, 0x20, 0x92, 0x00, 0x28, 2, 2},
+ {50, 400000, 8000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 800000, 16000, 0x05, 0x14, 0x5D, 0x55, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {50, 2400000, 48000, 0x05, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {64, 512000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {64, 1024000, 16000, 0x05, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {64, 3072000, 48000, 0x05, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {72, 576000, 8000, 0x05, 0x14, 0x5D, 0x33, 0x23, 0x08, 0x94, 0x00, 0x1F, 2, 2},
+ {72, 1152000, 16000, 0x05, 0x13, 0x55, 0x33, 0x23, 0x08, 0x93, 0x00, 0x1F, 2, 2},
+ {72, 3456000, 48000, 0x05, 0x11, 0x53, 0x33, 0x23, 0x08, 0x92, 0x00, 0x1F, 2, 2},
+ {96, 768000, 8000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x94, 0x00, 0x1F, 2, 2},
+ {96, 1536000, 16000, 0x15, 0x34, 0xDD, 0x55, 0x1F, 0x00, 0x93, 0x00, 0x1F, 2, 2},
+ {96, 4608000, 48000, 0x15, 0x33, 0xD5, 0x55, 0x1F, 0x00, 0x92, 0x00, 0x1F, 2, 2},
+ {100, 800000, 8000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x94, 0x00, 0x27, 2, 2},
+ {100, 1600000, 16000, 0x05, 0x03, 0x35, 0x33, 0x18, 0x24, 0x93, 0x00, 0x27, 2, 2},
+ {100, 4800000, 48000, 0x03, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x00, 0x27, 2, 2},
+ {128, 1024000, 8000, 0x05, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x93, 0x01, 0x1F, 2, 2},
+ {128, 2048000, 16000, 0x03, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {128, 6144000, 48000, 0x03, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 1152000, 8000, 0x05, 0x03, 0x35, 0x11, 0x23, 0x08, 0x93, 0x01, 0x1F, 2, 2},
+ {144, 2304000, 16000, 0x03, 0x01, 0x33, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {144, 6912000, 48000, 0x03, 0x00, 0x31, 0x11, 0x23, 0x08, 0x92, 0x01, 0x1F, 2, 2},
+ {192, 1536000, 8000, 0x15, 0x14, 0x5D, 0x33, 0x1F, 0x00, 0x93, 0x02, 0x1F, 2, 2},
+ {192, 3072000, 16000, 0x15, 0x13, 0x55, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {192, 9216000, 48000, 0x15, 0x11, 0x53, 0x33, 0x1F, 0x00, 0x92, 0x02, 0x1F, 2, 2},
+ {250, 12000000, 48000, 0x25, 0x11, 0x53, 0x55, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {256, 2048000, 8000, 0x0D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 4096000, 16000, 0x0B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {256, 12288000, 48000, 0x0B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x03, 0x1F, 2, 2},
+ {384, 3072000, 8000, 0x15, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 6144000, 16000, 0x13, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {384, 18432000, 48000, 0x13, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x05, 0x1F, 2, 2},
+ {400, 19200000, 48000, 0x1B, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {500, 24000000, 48000, 0x23, 0x00, 0x31, 0x33, 0x18, 0x24, 0x92, 0x04, 0x27, 2, 2},
+ {512, 4096000, 8000, 0x1D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 8192000, 16000, 0x1B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {512, 24576000, 48000, 0x1B, 0x00, 0x31, 0x11, 0x1F, 0x00, 0x92, 0x07, 0x1F, 2, 2},
+ {768, 6144000, 8000, 0x2D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {768, 12288000, 16000, 0x2B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0B, 0x1F, 2, 2},
+ {1024, 8192000, 8000, 0x3D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1024, 16384000, 16000, 0x3B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 9216000, 8000, 0x45, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1152, 18432000, 16000, 0x43, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x0F, 0x1F, 2, 2},
+ {1200, 9600000, 8000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1200, 19200000, 16000, 0x5D, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x11, 0x27, 2, 2},
+ {1536, 12288000, 8000, 0x5D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {1536, 24576000, 16000, 0x5B, 0x01, 0x33, 0x11, 0x1F, 0x00, 0x92, 0x17, 0x1F, 2, 2},
+ {2048, 16384000, 8000, 0x7D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x1F, 0x1F, 2, 2},
+ {2304, 18432000, 8000, 0x8D, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x23, 0x1F, 2, 2},
+ {2400, 19200000, 8000, 0xBD, 0x03, 0x35, 0x33, 0x18, 0x24, 0x92, 0x25, 0x27, 2, 2},
+ {3072, 24576000, 8000, 0xBD, 0x03, 0x35, 0x11, 0x1F, 0x00, 0x92, 0x2F, 0x1F, 2, 2},
+ {32, 3072000, 96000, 0x05, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {64, 6144000, 96000, 0x03, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {96, 9216000, 96000, 0x15, 0x11, 0x53, 0x55, 0x0F, 0x00, 0x92, 0x00, 0x37, 2, 2},
+ {128, 12288000, 96000, 0x0B, 0x00, 0x31, 0x33, 0x0F, 0x00, 0x92, 0x01, 0x37, 2, 2},
+};
+
+static inline int get_coeff(u8 vddd, u8 dmic, int mclk, int rate)
+{
+ int i;
+ u8 dmic_det, vddd_det;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) {
+ vddd_det = ~(coeff_div[i].dvdd_vol ^ vddd) & 0x01;
+ dmic_det = ~(coeff_div[i].dmic_sel ^ dmic) & 0x01;
+ vddd_det |= ~(coeff_div[i].dvdd_vol % 2) & 0x01;
+ dmic_det |= ~(coeff_div[i].dmic_sel % 2) & 0x01;
+
+ if (vddd_det && dmic_det)
+ return i;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int es8375_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 es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int par_width = params_width(params);
+ u8 dmic_enable, iface = 0;
+ unsigned int regv;
+ int coeff, ret;
+
+ if (es8375->mclk_src == ES8375_BCLK_PIN) {
+ regmap_update_bits(es8375->regmap,
+ ES8375_MCLK_SEL, 0x80, 0x80);
+
+ es8375->mclk_freq = 2 * (unsigned int)par_width * params_rate(params);
+ }
+
+ regmap_read(es8375->regmap, ES8375_ADC1, &regv);
+ dmic_enable = regv >> 7 & 0x01;
+
+ ret = regulator_get_voltage(es8375->core_supply[ES8375_SUPPLY_VD].consumer);
+ switch (ret) {
+ case 1800000 ... 2000000:
+ es8375->vddd = ES8375_1V8;
+ break;
+ case 2500000 ... 3300000:
+ es8375->vddd = ES8375_3V3;
+ break;
+ default:
+ es8375->vddd = ES8375_3V3;
+ break;
+ }
+
+ coeff = get_coeff(es8375->vddd, dmic_enable, es8375->mclk_freq, params_rate(params));
+ if (coeff < 0) {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ return coeff;
+ }
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4,
+ coeff_div[coeff].Reg0x04);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5,
+ coeff_div[coeff].Reg0x05);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6,
+ coeff_div[coeff].Reg0x06);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7,
+ coeff_div[coeff].Reg0x07);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8,
+ coeff_div[coeff].Reg0x08);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9,
+ coeff_div[coeff].Reg0x09);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10,
+ coeff_div[coeff].Reg0x0A);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR11,
+ coeff_div[coeff].Reg0x0B);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN,
+ coeff_div[coeff].Reg0x19);
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ iface |= 0x0c;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ iface |= 0x04;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ iface |= 0x10;
+ break;
+ }
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x1c, iface);
+
+ return 0;
+}
+
+static int es8375_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+ unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mclk_freq = freq;
+
+ return 0;
+}
+
+static int es8375_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int iface, codeciface;
+
+ regmap_read(es8375->regmap, ES8375_SDP, &codeciface);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ es8375->mastermode = 1;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8375->mastermode = 0;
+ regmap_update_bits(es8375->regmap, ES8375_RESET1,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ codeciface &= 0xFC;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ codeciface &= 0xFC;
+ codeciface |= 0x01;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ codeciface &= 0xDC;
+ codeciface |= 0x03;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ codeciface &= 0xDC;
+ codeciface |= 0x23;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_read(es8375->regmap, ES8375_CLK_MGR3, &iface);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ iface &= 0xFE;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ iface |= 0x01;
+ codeciface |= 0x20;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ iface |= 0x01;
+ codeciface &= 0xDF;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ iface &= 0xFE;
+ codeciface |= 0x20;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, iface);
+ regmap_write(es8375->regmap, ES8375_SDP, codeciface);
+
+ return 0;
+}
+
+static int es8375_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(component->dev, "unable to prepare mclk\n");
+ return ret;
+ }
+ regmap_write(es8375->regmap, ES8375_CSM1, 0xA6);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ clk_disable_unprepare(es8375->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+static int es8375_mute(struct snd_soc_dai *dai, int mute, int stream)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+ } else {
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x00);
+ else
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x00);
+ }
+
+ return 0;
+}
+
+#define es8375_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8375_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops es8375_ops = {
+ .hw_params = es8375_hw_params,
+ .mute_stream = es8375_mute,
+ .set_sysclk = es8375_set_sysclk,
+ .set_fmt = es8375_set_dai_fmt,
+};
+
+static struct snd_soc_dai_driver es8375_dai = {
+ .name = "ES8375 HiFi",
+ .playback = {
+ .stream_name = "AIF1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8375_RATES,
+ .formats = es8375_FORMATS,
+ },
+ .ops = &es8375_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8375_init(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x95);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_DIV_SPKCLK, 0x18);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x02);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x05);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x82);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE2, 0x20);
+ regmap_write(es8375->regmap, ES8375_VMID_CHARGE3, 0x20);
+ regmap_write(es8375->regmap, ES8375_DAC_CAL, 0x28);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK1, 0xFC);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE0);
+ regmap_write(es8375->regmap, ES8375_VMID_SEL, 0xFE);
+ regmap_write(es8375->regmap, ES8375_ANALOG1, 0xB8);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x03);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0x16);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ msleep(80);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x86);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR4, 0x0B);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR5, 0x00);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR6, 0x31);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR7, 0x11);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR8, 0x1F);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR9, 0x00);
+ regmap_write(es8375->regmap, ES8375_ADC_OSR_GAIN, 0x1F);
+ regmap_write(es8375->regmap, ES8375_ADC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC2, 0x00);
+ regmap_write(es8375->regmap, ES8375_DAC_OTP, 0x88);
+ regmap_write(es8375->regmap, ES8375_ANALOG_SPK2, 0xE7);
+ regmap_write(es8375->regmap, ES8375_ANALOG2, 0xF0);
+ regmap_write(es8375->regmap, ES8375_ANALOG3, 0x40);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR2, 0xFE);
+
+ regmap_update_bits(es8375->regmap, ES8375_SDP, 0x40, 0x40);
+ regmap_update_bits(es8375->regmap, ES8375_SDP2, 0x20, 0x20);
+}
+
+static int es8375_suspend(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x96);
+ regcache_cache_only(es8375->regmap, true);
+ regcache_mark_dirty(es8375->regmap);
+ return 0;
+}
+
+static int es8375_resume(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+ unsigned int reg;
+
+ regcache_cache_only(es8375->regmap, false);
+ regcache_cache_bypass(es8375->regmap, true);
+ regmap_read(es8375->regmap, ES8375_CLK_MGR2, &reg);
+ regcache_cache_bypass(es8375->regmap, false);
+
+ if (reg == 0x00)
+ es8375_init(component);
+ else
+ es8375_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8375->regmap);
+
+ return 0;
+}
+
+static int es8375_codec_probe(struct snd_soc_component *component)
+{
+ struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
+
+ es8375->mastermode = 0;
+
+ es8375_init(component);
+
+ return 0;
+}
+
+static bool es8375_writeable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8375_CHIP_VERSION:
+ case ES8375_CHIP_ID0:
+ case ES8375_CHIP_ID1:
+ case ES8375_SPK_OFFSET:
+ case ES8375_FLAGS2:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config es8375_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES8375_REG_MAX,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .writeable_reg = es8375_writeable_register,
+};
+
+static struct snd_soc_component_driver es8375_codec_driver = {
+ .probe = es8375_codec_probe,
+ .suspend = es8375_suspend,
+ .resume = es8375_resume,
+ .set_bias_level = es8375_set_bias_level,
+ .controls = es8375_snd_controls,
+ .num_controls = ARRAY_SIZE(es8375_snd_controls),
+ .dapm_widgets = es8375_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8375_dapm_widgets),
+ .dapm_routes = es8375_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8375_dapm_routes),
+
+ .idle_bias_on = 1,
+ .suspend_bias_off = 1,
+};
+
+static int es8375_read_device_properities(struct device *dev, struct es8375_priv *es8375)
+{
+ int ret, i;
+
+ ret = device_property_read_u8(dev, "everest,mclk-src", &es8375->mclk_src);
+ if (ret != 0)
+ es8375->mclk_src = ES8375_MCLK_SOURCE;
+ dev_dbg(dev, "mclk-src %x", es8375->mclk_src);
+
+ for (i = 0; i < ARRAY_SIZE(es8375_core_supplies); i++)
+ es8375->core_supply[i].supply = es8375_core_supplies[i];
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ es8375->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(es8375->mclk))
+ return dev_err_probe(dev, PTR_ERR(es8375->mclk), "unable to get mclk\n");
+
+ if (!es8375->mclk)
+ dev_warn(dev, "assuming static mclk\n");
+
+ ret = clk_prepare_enable(es8375->mclk);
+ if (ret) {
+ dev_err(dev, "unable to enable mclk\n");
+ return ret;
+ }
+ ret = regulator_bulk_enable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ if (ret) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ clk_disable_unprepare(es8375->mclk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int es8375_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8375_priv *es8375;
+ struct device *dev = &i2c_client->dev;
+ int ret;
+ unsigned int val;
+
+ es8375 = devm_kzalloc(&i2c_client->dev, sizeof(*es8375), GFP_KERNEL);
+ if (!es8375)
+ return -ENOMEM;
+
+ es8375->regmap = devm_regmap_init_i2c(i2c_client,
+ &es8375_regmap_config);
+ if (IS_ERR(es8375->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8375->regmap),
+ "regmap_init() failed\n");
+
+ i2c_set_clientdata(i2c_client, es8375);
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID1, &val);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n",
+ i2c_client->addr);
+ return ret;
+ }
+
+ if (val != 0x83) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = regmap_read(es8375->regmap, ES8375_CHIP_ID0, &val);
+ if (val != 0x75) {
+ dev_err(&i2c_client->dev, "device at addr %X is not an es8375\n",
+ i2c_client->addr);
+ return -ENODEV;
+ }
+
+ ret = es8375_read_device_properities(dev, es8375);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev, "get an error from dts info %X\n", ret);
+ return ret;
+ }
+
+ return devm_snd_soc_register_component(&i2c_client->dev, &es8375_codec_driver,
+ &es8375_dai, 1);
+}
+
+static void es8375_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8375_priv *es8375;
+
+ es8375 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3C);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR3, 0x48);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x80);
+ regmap_write(es8375->regmap, ES8375_CSM1, 0x3E);
+ regmap_write(es8375->regmap, ES8375_CLK_MGR10, 0x15);
+ regmap_write(es8375->regmap, ES8375_SYS_CTRL2, 0x0C);
+ regmap_write(es8375->regmap, ES8375_RESET1, 0x00);
+ regmap_write(es8375->regmap, ES8375_CSM2, 0x00);
+
+ regulator_bulk_disable(ARRAY_SIZE(es8375_core_supplies), es8375->core_supply);
+ clk_disable_unprepare(es8375->mclk);
+}
+
+static const struct i2c_device_id es8375_id[] = {
+ {"es8375"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8375_id);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id es8375_acpi_match[] = {
+ {"ESSX8375", 0},
+ {},
+};
+
+MODULE_DEVICE_TABLE(acpi, es8375_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8375_of_match[] = {
+ {.compatible = "everest,es8375",},
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, es8375_of_match);
+#endif
+
+static struct i2c_driver es8375_i2c_driver = {
+ .driver = {
+ .name = "es8375",
+ .of_match_table = of_match_ptr(es8375_of_match),
+ .acpi_match_table = ACPI_PTR(es8375_acpi_match),
+ },
+ .shutdown = es8375_i2c_shutdown,
+ .probe = es8375_i2c_probe,
+ .id_table = es8375_id,
+};
+module_i2c_driver(es8375_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES8375 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8375.h b/sound/soc/codecs/es8375.h
new file mode 100644
index 000000000000..11e3ceec9b68
--- /dev/null
+++ b/sound/soc/codecs/es8375.h
@@ -0,0 +1,123 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8375.h -- ES8375 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8375.h by Michael Zhang
+*/
+#ifndef _ES8375_H
+#define _ES8375_H
+
+// Registors
+#define ES8375_RESET1 0x00
+#define ES8375_MCLK_SEL 0x01
+#define ES8375_CLK_MGR2 0x02
+#define ES8375_CLK_MGR3 0x03
+#define ES8375_CLK_MGR4 0x04
+#define ES8375_CLK_MGR5 0x05
+#define ES8375_CLK_MGR6 0x06
+#define ES8375_CLK_MGR7 0x07
+#define ES8375_CLK_MGR8 0x08
+#define ES8375_CLK_MGR9 0x09
+#define ES8375_CLK_MGR10 0x0A
+#define ES8375_CLK_MGR11 0x0B
+#define ES8375_CLK_MGR12 0x0C
+#define ES8375_DIV_SPKCLK 0x0E
+#define ES8375_CSM1 0x0F
+#define ES8375_CSM2 0x10
+#define ES8375_VMID_CHARGE2 0x11
+#define ES8375_VMID_CHARGE3 0x12
+#define ES8375_SDP 0x15
+#define ES8375_SDP2 0x16
+#define ES8375_ADC1 0x17
+#define ES8375_ADC2 0x18
+#define ES8375_ADC_OSR_GAIN 0x19
+#define ES8375_ADC_VOLUME 0x1A
+#define ES8375_ADC_AUTOMUTE 0x1B
+#define ES8375_ADC_AUTOMUTE_ATTN 0x1C
+#define ES8375_HPF1 0x1D
+#define ES8375_DAC1 0x1F
+#define ES8375_DAC2 0x20
+#define ES8375_DAC_VOLUME 0x21
+#define ES8375_DAC_VPPSCALE 0x22
+#define ES8375_DAC_AUTOMUTE1 0x23
+#define ES8375_DAC_AUTOMUTE 0x24
+#define ES8375_DAC_CAL 0x25
+#define ES8375_DAC_OTP 0x27
+#define ES8375_ANALOG_SPK1 0x28
+#define ES8375_ANALOG_SPK2 0x29
+#define ES8375_VMID_SEL 0x2D
+#define ES8375_ANALOG1 0x2E
+#define ES8375_ANALOG2 0x32
+#define ES8375_ANALOG3 0x37
+#define ES8375_ADC2DAC_CLKTRI 0xF8
+#define ES8375_SYS_CTRL2 0xF9
+#define ES8375_FLAGS2 0xFB
+#define ES8375_SPK_OFFSET 0xFC
+#define ES8375_CHIP_ID1 0xFD
+#define ES8375_CHIP_ID0 0xFE
+#define ES8375_CHIP_VERSION 0xFF
+
+// Bit Shifts
+#define ADC_OSR_GAIN_SHIFT_0 0
+#define ADC_RAMPRATE_SHIFT_0 0
+#define ADC_VOLUME_SHIFT_0 0
+#define ADC_AUTOMUTE_NG_SHIFT_0 0
+#define ADC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DAC_RAMPRATE_SHIFT_0 0
+#define DAC_VOLUME_SHIFT_0 0
+#define DAC_VPPSCALE_SHIFT_0 0
+#define DAC_AUTOMUTE_NG_SHIFT_0 0
+#define DAC_AUTOMUTE_ATTN_SHIFT_0 0
+#define DMIC_GAIN_SHIFT_2 2
+#define ADC_AUTOMUTE_WS_SHIFT_3 3
+#define DMIC_POL_SHIFT_4 4
+#define DAC_RAMCLR_SHIFT_4 4
+#define ES8375_EN_MODL_SHIFT_4 4
+#define ADC_RAMCLR_SHIFT_5 5
+#define ADC_HPF_SHIFT_5 5
+#define DAC_INV_SHIFT_5 5
+#define DAC_AUTOMUTE_WS_SHIFT_5 5
+#define ES8375_EN_PGAL_SHIFT_5 5
+#define ES8375_ADC_P2S_MUTE_SHIFT_5 5
+#define ADC_INV_SHIFT_6 6
+#define DAC_DEMMUTE_SHIFT_6 6
+#define ES8375_DAC_S2P_MUTE_SHIFT_6 6
+#define ADC_SRC_SHIFT_7 7
+#define ADC_AUTOMUTE_SHIFT_7 7
+#define DAC_DSMMUTE_SHIFT_7 7
+#define DAC_AUTOMUTE_EN_SHIFT_7 7
+
+// Function values
+#define ES8375_ADC_OSR_GAIN_MAX 0x3F
+#define ES8375_DMIC_GAIN_MAX 0x04
+#define ES8375_ADC_AUTOMUTE_ATTN_MAX 0x1F
+#define ES8375_AUTOMUTE_NG_MAX 0x07
+#define ES8375_ADC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VOLUME_MAX 0xFF
+#define ES8375_DAC_VPPSCALE_MAX 0x3F
+#define ES8375_DAC_AUTOMUTE_ATTN_MAX 0x17
+#define ES8375_REG_MAX 0xFF
+
+enum ES8375_supplies {
+ ES8375_SUPPLY_VD = 0,
+ ES8375_SUPPLY_VA,
+};
+
+// Properties
+#define ES8375_3V3 1
+#define ES8375_1V8 0
+
+#define ES8375_MCLK_PIN 0
+#define ES8375_BCLK_PIN 1
+#define ES8375_MCLK_SOURCE ES8375_MCLK_PIN
+
+#define DMIC_POSITIVE_EDGE 0
+#define DMIC_NEGATIVE_EDGE 1
+#define DMIC_POL DMIC_POSITIVE_EDGE
+
+#define PA_SHUTDOWN 0
+#define PA_ENABLE 1
+
+#endif
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
new file mode 100644
index 000000000000..ba1763f36f17
--- /dev/null
+++ b/sound/soc/codecs/es8389.c
@@ -0,0 +1,962 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * es8389.c -- ES8389 ALSA SoC Audio Codec
+ *
+ * Copyright Everest Semiconductor Co., Ltd
+ *
+ * Authors: Michael Zhang (zhangyi@everest-semi.com)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include <sound/soc.h>
+
+#include "es8389.h"
+
+
+/* codec private data */
+
+struct es8389_private {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int sysclk;
+ int mastermode;
+
+ u8 mclk_src;
+ enum snd_soc_bias_level bias_level;
+};
+
+static bool es8389_volatile_register(struct device *dev,
+ unsigned int reg)
+{
+ if ((reg <= 0xff))
+ return true;
+ else
+ return false;
+}
+
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
+static const DECLARE_TLV_DB_SCALE(pga_vol_tlv, 0, 300, 0);
+static const DECLARE_TLV_DB_SCALE(mix_vol_tlv, -9500, 100, 0);
+static const DECLARE_TLV_DB_SCALE(alc_target_tlv, -3200, 200, 0);
+static const DECLARE_TLV_DB_SCALE(alc_max_level, -3200, 200, 0);
+
+static int es8389_dmic_set(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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int val;
+ bool changed1, changed2;
+
+ val = ucontrol->value.integer.value[0];
+ if (val > 1)
+ return -EINVAL;
+
+ if (val) {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0xC0, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x03, &changed2);
+ } else {
+ regmap_update_bits_check(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00, &changed1);
+ regmap_update_bits_check(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00, &changed2);
+ }
+
+ if (changed1 & changed2)
+ return snd_soc_dapm_mux_update_power(dapm, kcontrol, val, e, NULL);
+ else
+ return 0;
+}
+
+static const char *const alc[] = {
+ "ALC OFF",
+ "ADCR ALC ON",
+ "ADCL ALC ON",
+ "ADCL & ADCL ALC ON",
+};
+
+static const char *const ramprate[] = {
+ "0.125db/1 LRCK",
+ "0.125db/4 LRCK",
+ "0.125db/8 LRCK",
+ "0.125db/16 LRCK",
+ "0.125db/32 LRCK",
+ "0.125db/64 LRCK",
+ "0.125db/128 LRCK",
+ "0.125db/256 LRCK",
+ "0.125db/512 LRCK",
+ "0.125db/1024 LRCK",
+ "0.125db/2048 LRCK",
+ "0.125db/4096 LRCK",
+ "0.125db/8192 LRCK",
+ "0.125db/16384 LRCK",
+ "0.125db/32768 LRCK",
+ "0.125db/65536 LRCK",
+};
+
+static const char *const winsize[] = {
+ "2 LRCK",
+ "4 LRCK",
+ "8 LRCK",
+ "16 LRCK",
+ "32 LRCK",
+ "64 LRCK",
+ "128 LRCK",
+ "256 LRCK",
+ "512 LRCK",
+ "1024 LRCK",
+ "2048 LRCK",
+ "4096 LRCK",
+ "8192 LRCK",
+ "16384 LRCK",
+ "32768 LRCK",
+ "65536 LRCK",
+};
+
+static const struct soc_enum alc_enable =
+ SOC_ENUM_SINGLE(ES8389_ALC_ON, 5, 4, alc);
+static const struct soc_enum alc_ramprate =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 4, 16, ramprate);
+static const struct soc_enum alc_winsize =
+ SOC_ENUM_SINGLE(ES8389_ALC_CTL, 0, 16, winsize);
+
+static const char *const es8389_outl_mux_txt[] = {
+ "Normal",
+ "DAC2 channel to DAC1 channel",
+};
+
+static const char *const es8389_outr_mux_txt[] = {
+ "Normal",
+ "DAC1 channel to DAC2 channel",
+};
+
+static const char *const es8389_dmic_mux_txt[] = {
+ "AMIC",
+ "DMIC",
+};
+
+static const char *const es8389_pga1_texts[] = {
+ "DifferentialL", "Line 1P", "Line 2P"
+};
+
+static const char *const es8389_pga2_texts[] = {
+ "DifferentialR", "Line 2N", "Line 1N"
+};
+
+static const unsigned int es8389_pga_values[] = {
+ 1, 5, 6
+};
+
+static const struct soc_enum es8389_outl_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 5,
+ ARRAY_SIZE(es8389_outl_mux_txt), es8389_outl_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outl_mux_controls =
+ SOC_DAPM_ENUM("OUTL MUX", es8389_outl_mux_enum);
+
+static const struct soc_enum es8389_outr_mux_enum =
+ SOC_ENUM_SINGLE(ES8389_DAC_MIX, 4,
+ ARRAY_SIZE(es8389_outr_mux_txt), es8389_outr_mux_txt);
+
+static const struct snd_kcontrol_new es8389_outr_mux_controls =
+ SOC_DAPM_ENUM("OUTR MUX", es8389_outr_mux_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+ es8389_dmic_mux_enum, ES8389_DMIC_EN, 6, es8389_dmic_mux_txt);
+
+static const struct soc_enum es8389_pgal_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC1_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga1_texts), es8389_pga1_texts,
+ es8389_pga_values);
+
+static const struct soc_enum es8389_pgar_enum =
+ SOC_VALUE_ENUM_SINGLE(ES8389_MIC2_GAIN, 4, 7,
+ ARRAY_SIZE(es8389_pga2_texts), es8389_pga2_texts,
+ es8389_pga_values);
+
+static const struct snd_kcontrol_new es8389_dmic_mux_controls =
+ SOC_DAPM_ENUM_EXT("ADC MUX", es8389_dmic_mux_enum,
+ snd_soc_dapm_get_enum_double, es8389_dmic_set);
+
+static const struct snd_kcontrol_new es8389_left_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACR DACL Mixer", ES8389_DAC_MIX, 3, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_right_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL DACR Mixer", ES8389_DAC_MIX, 2, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_leftadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCL DACL Mixer", ES8389_DAC_MIX, 1, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_rightadc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("ADCR DACR Mixer", ES8389_DAC_MIX, 0, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_adc_mixer_controls[] = {
+ SOC_DAPM_SINGLE("DACL ADCL Mixer", ES8389_ADC_RESET, 7, 1, 0),
+ SOC_DAPM_SINGLE("DACR ADCR Mixer", ES8389_ADC_RESET, 6, 1, 0),
+};
+
+static const struct snd_kcontrol_new es8389_snd_controls[] = {
+ SOC_SINGLE_TLV("ADCL Capture Volume", ES8389_ADCL_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCR Capture Volume", ES8389_ADCR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("ADCL PGA Volume", ES8389_MIC1_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+ SOC_SINGLE_TLV("ADCR PGA Volume", ES8389_MIC2_GAIN, 0, 0x0E, 0, pga_vol_tlv),
+
+ SOC_ENUM("PGAL Select", es8389_pgal_enum),
+ SOC_ENUM("PGAR Select", es8389_pgar_enum),
+ SOC_ENUM("ALC Capture Switch", alc_enable),
+ SOC_SINGLE_TLV("ALC Capture Target Level", ES8389_ALC_TARGET,
+ 0, 0x0f, 0, alc_target_tlv),
+ SOC_SINGLE_TLV("ALC Capture Max Gain", ES8389_ALC_GAIN,
+ 0, 0x0f, 0, alc_max_level),
+ SOC_ENUM("ADC Ramp Rate", alc_ramprate),
+ SOC_ENUM("ALC Capture Winsize", alc_winsize),
+ SOC_DOUBLE("ADC OSR Volume ON Switch", ES8389_ADC_MUTE, 6, 7, 1, 0),
+ SOC_SINGLE_TLV("ADC OSR Volume", ES8389_OSR_VOL, 0, 0xFF, 0, adc_vol_tlv),
+ SOC_DOUBLE("ADC OUTPUT Invert Switch", ES8389_ADC_HPF2, 5, 6, 1, 0),
+
+ SOC_SINGLE_TLV("DACL Playback Volume", ES8389_DACL_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_SINGLE_TLV("DACR Playback Volume", ES8389_DACR_VOL, 0, 0xFF, 0, dac_vol_tlv),
+ SOC_DOUBLE("DAC OUTPUT Invert Switch", ES8389_DAC_INV, 5, 6, 1, 0),
+ SOC_SINGLE_TLV("ADC2DAC Mixer Volume", ES8389_MIX_VOL, 0, 0x7F, 0, mix_vol_tlv),
+};
+
+static const struct snd_soc_dapm_widget es8389_dapm_widgets[] = {
+ /*Input Side*/
+ SND_SOC_DAPM_INPUT("INPUT1"),
+ SND_SOC_DAPM_INPUT("INPUT2"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+ SND_SOC_DAPM_PGA("PGAL", SND_SOC_NOPM, 4, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("PGAR", SND_SOC_NOPM, 4, 0, NULL, 0),
+
+ /*ADCs*/
+ SND_SOC_DAPM_ADC("ADCL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_OUT("I2S OUT", "I2S Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("I2S IN", "I2S Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ /*DACs*/
+ SND_SOC_DAPM_DAC("DACL", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, SND_SOC_NOPM, 0, 0),
+
+ /*Output Side*/
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_PGA("IF DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACL3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DACR3", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* Digital Interface Select */
+ SND_SOC_DAPM_MIXER("IF DACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_left_mixer_controls[0],
+ ARRAY_SIZE(es8389_left_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF DACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_right_mixer_controls[0],
+ ARRAY_SIZE(es8389_right_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACL Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_leftadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_leftadc_mixer_controls)),
+ SND_SOC_DAPM_MIXER("IF ADCDACR Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_rightadc_mixer_controls[0],
+ ARRAY_SIZE(es8389_rightadc_mixer_controls)),
+
+ SND_SOC_DAPM_MIXER("ADC Mixer", SND_SOC_NOPM, 0, 0,
+ &es8389_adc_mixer_controls[0],
+ ARRAY_SIZE(es8389_adc_mixer_controls)),
+ SND_SOC_DAPM_MUX("ADC MUX", SND_SOC_NOPM, 0, 0, &es8389_dmic_mux_controls),
+
+ SND_SOC_DAPM_MUX("OUTL MUX", SND_SOC_NOPM, 0, 0, &es8389_outl_mux_controls),
+ SND_SOC_DAPM_MUX("OUTR MUX", SND_SOC_NOPM, 0, 0, &es8389_outr_mux_controls),
+};
+
+
+static const struct snd_soc_dapm_route es8389_dapm_routes[] = {
+ {"PGAL", NULL, "INPUT1"},
+ {"PGAR", NULL, "INPUT2"},
+
+ {"ADCL", NULL, "PGAL"},
+ {"ADCR", NULL, "PGAR"},
+
+ {"ADC Mixer", "DACL ADCL Mixer", "DACL"},
+ {"ADC Mixer", "DACR ADCR Mixer", "DACR"},
+ {"ADC Mixer", NULL, "ADCL"},
+ {"ADC Mixer", NULL, "ADCR"},
+
+ {"ADC MUX", "AMIC", "ADC Mixer"},
+ {"ADC MUX", "DMIC", "DMIC"},
+
+ {"I2S OUT", NULL, "ADC MUX"},
+
+ {"DACL", NULL, "I2S IN"},
+ {"DACR", NULL, "I2S IN"},
+
+ {"IF DACL1", NULL, "DACL"},
+ {"IF DACR1", NULL, "DACR"},
+ {"IF DACL2", NULL, "DACL"},
+ {"IF DACR2", NULL, "DACR"},
+ {"IF DACL3", NULL, "DACL"},
+ {"IF DACR3", NULL, "DACR"},
+
+ {"IF DACL Mixer", NULL, "IF DACL2"},
+ {"IF DACL Mixer", "DACR DACL Mixer", "IF DACR1"},
+ {"IF DACR Mixer", NULL, "IF DACR2"},
+ {"IF DACR Mixer", "DACL DACR Mixer", "IF DACL1"},
+
+ {"IF ADCDACL Mixer", NULL, "IF DACL Mixer"},
+ {"IF ADCDACL Mixer", "ADCL DACL Mixer", "IF DACL3"},
+ {"IF ADCDACR Mixer", NULL, "IF DACR Mixer"},
+ {"IF ADCDACR Mixer", "ADCR DACR Mixer", "IF DACR3"},
+
+ {"OUTL MUX", "Normal", "IF ADCDACL Mixer"},
+ {"OUTL MUX", "DAC2 channel to DAC1 channel", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "Normal", "IF ADCDACR Mixer"},
+ {"OUTR MUX", "DAC1 channel to DAC2 channel", "IF ADCDACL Mixer"},
+
+ {"HPOL", NULL, "OUTL MUX"},
+ {"HPOR", NULL, "OUTR MUX"},
+
+};
+
+struct _coeff_div {
+ u16 fs;
+ u32 mclk;
+ u32 rate;
+ u8 Reg0x04;
+ u8 Reg0x05;
+ u8 Reg0x06;
+ u8 Reg0x07;
+ u8 Reg0x08;
+ u8 Reg0x09;
+ u8 Reg0x0A;
+ u8 Reg0x0F;
+ u8 Reg0x11;
+ u8 Reg0x21;
+ u8 Reg0x22;
+ u8 Reg0x26;
+ u8 Reg0x30;
+ u8 Reg0x41;
+ u8 Reg0x42;
+ u8 Reg0x43;
+ u8 Reg0xF0;
+ u8 Reg0xF1;
+ u8 Reg0x16;
+ u8 Reg0x18;
+ u8 Reg0x19;
+};
+
+/* codec hifi mclk clock divider coefficients */
+static const struct _coeff_div coeff_div[] = {
+ {32, 256000, 8000, 0x00, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {36, 288000, 8000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {48, 384000, 8000, 0x02, 0x5F, 0x04, 0xC0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {64, 512000, 8000, 0x00, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {72, 576000, 8000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {96, 768000, 8000, 0x02, 0x57, 0x84, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {128, 1024000, 8000, 0x00, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {192, 1536000, 8000, 0x02, 0x4D, 0x24, 0xC0, 0x03, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {256, 2048000, 8000, 0x01, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {288, 2304000, 8000, 0x01, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {384, 3072000, 8000, 0x02, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {512, 4096000, 8000, 0x00, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {768, 6144000, 8000, 0x05, 0x45, 0x04, 0xD0, 0x03, 0xC1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1024, 8192000, 8000, 0x01, 0x41, 0x06, 0xE0, 0x00, 0xD1, 0xB0, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1536, 12288000, 8000, 0x02, 0x41, 0x04, 0xE0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1625, 13000000, 8000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2048, 16384000, 8000, 0x03, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2304, 18432000, 8000, 0x11, 0x45, 0x25, 0xF0, 0x00, 0xD1, 0xB0, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3072, 24576000, 8000, 0x05, 0x44, 0x01, 0xC0, 0x00, 0xD2, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {32, 512000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {36, 576000, 16000, 0x00, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {48, 768000, 16000, 0x02, 0x57, 0x04, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {50, 800000, 16000, 0x00, 0x7E, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {64, 1024000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {72, 1152000, 16000, 0x00, 0x45, 0x24, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {96, 1536000, 16000, 0x02, 0x55, 0x84, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {128, 2048000, 16000, 0x00, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {144, 2304000, 16000, 0x00, 0x51, 0x00, 0xC0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x23, 0x8F, 0xB7, 0xC0, 0x1F, 0x8F, 0x01, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {192, 3072000, 16000, 0x02, 0x65, 0x25, 0xE0, 0x00, 0xE1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {256, 4096000, 16000, 0x00, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {300, 4800000, 16000, 0x02, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {384, 6144000, 16000, 0x02, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {512, 8192000, 16000, 0x01, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {750, 12000000, 16000, 0x0E, 0x7E, 0x01, 0xC9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {768, 12288000, 16000, 0x02, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1024, 16384000, 16000, 0x03, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1152, 18432000, 16000, 0x08, 0x51, 0x04, 0xD0, 0x01, 0xC1, 0x90, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1200, 19200000, 16000, 0x0B, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1500, 24000000, 16000, 0x0E, 0x26, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1536, 24576000, 16000, 0x05, 0x41, 0x04, 0xC0, 0x01, 0xD1, 0x90, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0xFF, 0x7F, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {1625, 26000000, 16000, 0x40, 0x6E, 0x05, 0xC8, 0x01, 0xC2, 0x90, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x12, 0x31, 0x0E},
+ {800, 19200000, 24000, 0x07, 0x66, 0x01, 0xD9, 0x00, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0xC7, 0x95, 0x00, 0x12, 0x00, 0x1A, 0x49, 0x14},
+ {600, 19200000, 32000, 0x05, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x23, 0x61, 0x1B},
+ {32, 1411200, 44100, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 2822400, 44100, 0x00, 0x51, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 5644800, 44100, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 11289600, 44100, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 22579200, 44100, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {32, 1536000, 48000, 0x00, 0x45, 0xA4, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {48, 2304000, 48000, 0x02, 0x55, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {50, 2400000, 48000, 0x00, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {64, 3072000, 48000, 0x00, 0x51, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {100, 4800000, 48000, 0x00, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {125, 6000000, 48000, 0x04, 0x6E, 0x05, 0xC8, 0x10, 0xC2, 0x80, 0x00, 0x01, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 6144000, 48000, 0x00, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {200, 9600000, 48000, 0x01, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x00, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {250, 12000000, 48000, 0x04, 0x76, 0x01, 0xC8, 0x10, 0xC2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {256, 12288000, 48000, 0x01, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {384, 18432000, 48000, 0x02, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0x40, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {400, 19200000, 48000, 0x03, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0x40, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {500, 24000000, 48000, 0x04, 0x46, 0x01, 0xD8, 0x10, 0xD2, 0x80, 0xC0, 0x00, 0x18, 0x95, 0xD0, 0xC0, 0x63, 0x95, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {512, 24576000, 48000, 0x03, 0x41, 0x04, 0xD0, 0x10, 0xD1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {800, 38400000, 48000, 0x18, 0x45, 0x04, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x1F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x00, 0x12, 0x00, 0x35, 0x91, 0x28},
+ {128, 11289600, 88200, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0x40, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x32, 0x89, 0x25},
+ {64, 6144000, 96000, 0x00, 0x41, 0x00, 0xD0, 0x10, 0xD1, 0x80, 0x00, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 12288000, 96000, 0x00, 0x50, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {256, 24576000, 96000, 0x00, 0x40, 0x00, 0xC0, 0x10, 0xC1, 0x80, 0xC0, 0x00, 0x9F, 0x7F, 0xBF, 0xC0, 0x7F, 0x7F, 0x80, 0x12, 0xC0, 0x35, 0x91, 0x28},
+ {128, 24576000, 192000, 0x00, 0x50, 0x00, 0xC0, 0x18, 0xC1, 0x81, 0xC0, 0x00, 0x8F, 0x7F, 0xEF, 0xC0, 0x3F, 0x7F, 0x80, 0x12, 0xC0, 0x3F, 0xF9, 0x3F},
+
+ {50, 400000, 8000, 0x00, 0x75, 0x05, 0xC8, 0x01, 0xC1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {600, 4800000, 8000, 0x05, 0x65, 0x25, 0xF9, 0x00, 0xD1, 0x90, 0x10, 0x00, 0x18, 0xC7, 0xD0, 0xC0, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {1500, 12000000, 8000, 0x0E, 0x25, 0x25, 0xE8, 0x00, 0xD1, 0x90, 0x40, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {2400, 19200000, 8000, 0x0B, 0x01, 0x00, 0xD0, 0x00, 0xD1, 0x80, 0x90, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3000, 24000000, 8000, 0x0E, 0x24, 0x05, 0xD0, 0x00, 0xC2, 0x80, 0xC0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0x8F, 0xC7, 0x01, 0x12, 0x00, 0x09, 0x19, 0x07},
+ {3250, 26000000, 8000, 0x40, 0x05, 0xA4, 0xC0, 0x00, 0xD1, 0x80, 0xD0, 0x00, 0x31, 0xC7, 0xC5, 0x00, 0xC7, 0xC7, 0x00, 0x12, 0x00, 0x09, 0x19, 0x07},
+};
+
+static inline int get_coeff(int mclk, int rate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(coeff_div); i++) {
+ if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk)
+ return i;
+ }
+ return -EINVAL;
+}
+
+/*
+ * if PLL not be used, use internal clk1 for mclk,otherwise, use internal clk2 for PLL source.
+ */
+static int es8389_set_dai_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389->sysclk = freq;
+
+ return 0;
+}
+
+static int es8389_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_update_bits(es8389->regmap, ES8389_PTDM_SLOT,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+ regmap_update_bits(es8389->regmap, ES8389_DAC_RAMP,
+ ES8389_TDM_SLOT, (slots << ES8389_TDM_SHIFT));
+
+ return 0;
+}
+
+static int es8389_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ u8 state = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFP:
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_MODE,
+ ES8389_MASTER_MODE_EN, ES8389_MASTER_MODE_EN);
+ es8389->mastermode = 1;
+ break;
+ case SND_SOC_DAIFMT_CBC_CFC:
+ es8389->mastermode = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ state |= ES8389_DAIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ dev_err(component->dev, "component driver does not support right justified\n");
+ return -EINVAL;
+ case SND_SOC_DAIFMT_LEFT_J:
+ state |= ES8389_DAIFMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ state |= ES8389_DAIFMT_DSP_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ state |= ES8389_DAIFMT_DSP_B;
+ break;
+ default:
+ break;
+ }
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DAIFMT_MASK, state);
+
+ return 0;
+}
+
+static int es8389_pcm_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 es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ int coeff;
+ u8 state = 0;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ state |= ES8389_S16_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S20_3LE:
+ state |= ES8389_S20_3_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S18_3LE:
+ state |= ES8389_S18_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ state |= ES8389_S24_LE;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ state |= ES8389_S32_LE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, ES8389_DATA_LEN_MASK, state);
+
+ if (es8389->mclk_src == ES8389_SCLK_PIN) {
+ regmap_update_bits(es8389->regmap, ES8389_MASTER_CLK,
+ ES8389_MCLK_SOURCE, es8389->mclk_src);
+ es8389->sysclk = params_channels(params) * params_width(params) * params_rate(params);
+ }
+
+ coeff = get_coeff(es8389->sysclk, params_rate(params));
+ if (coeff >= 0) {
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, coeff_div[coeff].Reg0x04);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, coeff_div[coeff].Reg0x05);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, coeff_div[coeff].Reg0x06);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, coeff_div[coeff].Reg0x07);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, coeff_div[coeff].Reg0x08);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, coeff_div[coeff].Reg0x09);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, coeff_div[coeff].Reg0x0A);
+ regmap_update_bits(es8389->regmap, ES8389_OSC_CLK,
+ 0xC0, coeff_div[coeff].Reg0x0F);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV2, coeff_div[coeff].Reg0x11);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, coeff_div[coeff].Reg0x21);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, coeff_div[coeff].Reg0x22);
+ regmap_write(es8389->regmap, ES8389_OSR_VOL, coeff_div[coeff].Reg0x26);
+ regmap_update_bits(es8389->regmap, ES8389_SYSTEM30,
+ 0xC0, coeff_div[coeff].Reg0x30);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, coeff_div[coeff].Reg0x41);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, coeff_div[coeff].Reg0x42);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_MISC,
+ 0x81, coeff_div[coeff].Reg0x43);
+ regmap_update_bits(es8389->regmap, ES8389_CHIP_MISC,
+ 0x72, coeff_div[coeff].Reg0xF0);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, coeff_div[coeff].Reg0xF1);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, coeff_div[coeff].Reg0x16);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, coeff_div[coeff].Reg0x18);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, coeff_div[coeff].Reg0x19);
+ } else {
+ dev_warn(component->dev, "Clock coefficients do not match");
+ }
+
+ return 0;
+}
+
+static int es8389_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret)
+ return ret;
+
+ regmap_update_bits(es8389->regmap, ES8389_HPSW, 0x20, 0x20);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xD9);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x8F);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xE4);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC3);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x0a);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x0a);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0X00);
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF1, 0x0f, 0x04);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_HPF2, 0x0f, 0x04);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xD4);
+ usleep_range(70000, 72000);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x59);
+ regmap_write(es8389->regmap, ES8389_ADC_EN, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x7E);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x80);
+ usleep_range(8000, 8500);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_INV, 0x80, 0x00);
+
+ clk_disable_unprepare(es8389->mclk);
+ break;
+ case SND_SOC_BIAS_OFF:
+ break;
+ }
+ return 0;
+}
+
+
+
+static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ if (mute) {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x03);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x03);
+ }
+ } else {
+ if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE,
+ 0x03, 0x00);
+ } else {
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE,
+ 0x03, 0x00);
+ }
+ }
+
+ return 0;
+}
+
+#define es8389_RATES SNDRV_PCM_RATE_8000_96000
+
+#define es8389_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static const struct snd_soc_dai_ops es8389_ops = {
+ .hw_params = es8389_pcm_hw_params,
+ .set_fmt = es8389_set_dai_fmt,
+ .set_sysclk = es8389_set_dai_sysclk,
+ .set_tdm_slot = es8389_set_tdm_slot,
+ .mute_stream = es8389_mute,
+};
+
+static struct snd_soc_dai_driver es8389_dai = {
+ .name = "ES8389 HiFi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = es8389_RATES,
+ .formats = es8389_FORMATS,
+ },
+ .ops = &es8389_ops,
+ .symmetric_rate = 1,
+};
+
+static void es8389_init(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x7E);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0x38);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF1, 0x64);
+ regmap_write(es8389->regmap, ES8389_ADC_HPF2, 0x04);
+ regmap_write(es8389->regmap, ES8389_DAC_INV, 0x03);
+
+ regmap_write(es8389->regmap, ES8389_VMID, 0x2A);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0xC9);
+ regmap_write(es8389->regmap, ES8389_ANA_VSEL, 0x4F);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL2, 0x06);
+ regmap_write(es8389->regmap, ES8389_LOW_POWER1, 0x00);
+ regmap_write(es8389->regmap, ES8389_DMIC_EN, 0x16);
+
+ regmap_write(es8389->regmap, ES8389_PGA_SW, 0xAA);
+ regmap_write(es8389->regmap, ES8389_MOD_SW1, 0x66);
+ regmap_write(es8389->regmap, ES8389_MOD_SW2, 0x99);
+ regmap_write(es8389->regmap, ES8389_ADC_MODE, (0x00 | ES8389_TDM_MODE));
+ regmap_update_bits(es8389->regmap, ES8389_DMIC_EN, 0xC0, 0x00);
+ regmap_update_bits(es8389->regmap, ES8389_ADC_MODE, 0x03, 0x00);
+
+ regmap_update_bits(es8389->regmap, ES8389_MIC1_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_update_bits(es8389->regmap, ES8389_MIC2_GAIN,
+ ES8389_MIC_SEL_MASK, ES8389_MIC_DEFAULT);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xC4);
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x08);
+ regmap_write(es8389->regmap, ES8389_CSM_STATE1, 0x00);
+ regmap_write(es8389->regmap, ES8389_SYSTEM12, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM13, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM14, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM15, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM16, 0x35);
+ regmap_write(es8389->regmap, ES8389_SYSTEM17, 0x09);
+ regmap_write(es8389->regmap, ES8389_SYSTEM18, 0x91);
+ regmap_write(es8389->regmap, ES8389_SYSTEM19, 0x28);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1A, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1B, 0x01);
+ regmap_write(es8389->regmap, ES8389_SYSTEM1C, 0x11);
+
+ regmap_write(es8389->regmap, ES8389_CHIP_MISC, 0x13);
+ regmap_write(es8389->regmap, ES8389_MASTER_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_DIV1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUL, 0x10);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_MUX2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL1, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL2, 0xC0);
+ regmap_write(es8389->regmap, ES8389_CLK_CTL3, 0x80);
+ regmap_write(es8389->regmap, ES8389_SCLK_DIV, 0x04);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV1, 0x01);
+ regmap_write(es8389->regmap, ES8389_LRCK_DIV2, 0x00);
+ regmap_write(es8389->regmap, ES8389_OSC_CLK, 0x00);
+ regmap_write(es8389->regmap, ES8389_ADC_OSR, 0x1F);
+ regmap_write(es8389->regmap, ES8389_ADC_DSP, 0x7F);
+ regmap_write(es8389->regmap, ES8389_ADC_MUTE, 0xC0);
+ regmap_write(es8389->regmap, ES8389_SYSTEM30, 0xF4);
+ regmap_write(es8389->regmap, ES8389_DAC_DSM_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_DSP_OSR, 0x7F);
+ regmap_write(es8389->regmap, ES8389_DAC_MISC, 0x10);
+ regmap_write(es8389->regmap, ES8389_DAC_RAMP, 0x0F);
+ regmap_write(es8389->regmap, ES8389_SYSTEM4C, 0xC0);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CLK_OFF1, 0xC1);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x01);
+ regmap_write(es8389->regmap, ES8389_DAC_RESET, 0x02);
+
+ regmap_update_bits(es8389->regmap, ES8389_ADC_FORMAT_MUTE, 0x03, 0x03);
+ regmap_update_bits(es8389->regmap, ES8389_DAC_FORMAT_MUTE, 0x03, 0x03);
+}
+
+static int es8389_suspend(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+ regcache_cache_only(es8389->regmap, true);
+ regcache_mark_dirty(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_resume(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+ unsigned int regv;
+
+ regcache_cache_only(es8389->regmap, false);
+ regcache_cache_bypass(es8389->regmap, true);
+ regmap_read(es8389->regmap, ES8389_RESET, &regv);
+ regcache_cache_bypass(es8389->regmap, false);
+
+ if (regv == 0xff)
+ es8389_init(component);
+ else
+ es8389_set_bias_level(component, SND_SOC_BIAS_ON);
+
+ regcache_sync(es8389->regmap);
+
+ return 0;
+}
+
+static int es8389_probe(struct snd_soc_component *component)
+{
+ int ret;
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ ret = device_property_read_u8(component->dev, "everest,mclk-src", &es8389->mclk_src);
+ if (ret != 0) {
+ dev_dbg(component->dev, "mclk-src return %d", ret);
+ es8389->mclk_src = ES8389_MCLK_SOURCE;
+ }
+
+ es8389->mclk = devm_clk_get(component->dev, "mclk");
+ if (IS_ERR(es8389->mclk))
+ return dev_err_probe(component->dev, PTR_ERR(es8389->mclk),
+ "ES8389 is unable to get mclk\n");
+
+ if (!es8389->mclk)
+ dev_err(component->dev, "%s, assuming static mclk\n", __func__);
+
+ ret = clk_prepare_enable(es8389->mclk);
+ if (ret) {
+ dev_err(component->dev, "%s, unable to enable mclk\n", __func__);
+ return ret;
+ }
+
+ es8389_init(component);
+ es8389_set_bias_level(component, SND_SOC_BIAS_STANDBY);
+
+ return 0;
+}
+
+static void es8389_remove(struct snd_soc_component *component)
+{
+ struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_es8389 = {
+ .probe = es8389_probe,
+ .remove = es8389_remove,
+ .suspend = es8389_suspend,
+ .resume = es8389_resume,
+ .set_bias_level = es8389_set_bias_level,
+
+ .controls = es8389_snd_controls,
+ .num_controls = ARRAY_SIZE(es8389_snd_controls),
+ .dapm_widgets = es8389_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8389_dapm_widgets),
+ .dapm_routes = es8389_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8389_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+};
+
+static const struct regmap_config es8389_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = ES8389_MAX_REGISTER,
+
+ .volatile_reg = es8389_volatile_register,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static void es8389_i2c_shutdown(struct i2c_client *i2c)
+{
+ struct es8389_private *es8389;
+
+ es8389 = i2c_get_clientdata(i2c);
+
+ regmap_write(es8389->regmap, ES8389_MASTER_MODE, 0x28);
+ regmap_write(es8389->regmap, ES8389_HPSW, 0x00);
+ regmap_write(es8389->regmap, ES8389_VMID, 0x00);
+ regmap_write(es8389->regmap, ES8389_RESET, 0x00);
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0xCC);
+ usleep_range(500000, 550000);//500MS
+ regmap_write(es8389->regmap, ES8389_CSM_JUMP, 0x00);
+ regmap_write(es8389->regmap, ES8389_ANA_CTL1, 0x08);
+ regmap_write(es8389->regmap, ES8389_ISO_CTL, 0xC1);
+ regmap_write(es8389->regmap, ES8389_PULL_DOWN, 0x00);
+}
+
+static int es8389_i2c_probe(struct i2c_client *i2c_client)
+{
+ struct es8389_private *es8389;
+ int ret;
+
+ es8389 = devm_kzalloc(&i2c_client->dev, sizeof(*es8389), GFP_KERNEL);
+ if (es8389 == NULL)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, es8389);
+ es8389->regmap = devm_regmap_init_i2c(i2c_client, &es8389_regmap);
+ if (IS_ERR(es8389->regmap))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8389->regmap),
+ "regmap_init() failed\n");
+
+ ret = devm_snd_soc_register_component(&i2c_client->dev,
+ &soc_codec_dev_es8389,
+ &es8389_dai,
+ 1);
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es8389_if_dt_ids[] = {
+ { .compatible = "everest,es8389", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es8389_if_dt_ids);
+#endif
+
+static const struct i2c_device_id es8389_i2c_id[] = {
+ {"es8389"},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es8389_i2c_id);
+
+static struct i2c_driver es8389_i2c_driver = {
+ .driver = {
+ .name = "es8389",
+ .of_match_table = of_match_ptr(es8389_if_dt_ids),
+ },
+ .shutdown = es8389_i2c_shutdown,
+ .probe = es8389_i2c_probe,
+ .id_table = es8389_i2c_id,
+};
+module_i2c_driver(es8389_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC es8389 driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8389.h b/sound/soc/codecs/es8389.h
new file mode 100644
index 000000000000..123d1e4b2d53
--- /dev/null
+++ b/sound/soc/codecs/es8389.h
@@ -0,0 +1,140 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+* ES8389.h -- ES8389 ALSA SoC Audio Codec
+*
+* Authors:
+*
+* Based on ES8374.h by Michael Zhang
+*/
+
+#ifndef _ES8389_H
+#define _ES8389_H
+
+/*
+* ES8389_REGISTER NAME_REG_REGISTER ADDRESS
+*/
+#define ES8389_RESET 0x00 /*reset digital,csm,clock manager etc.*/
+
+/*
+* Clock Scheme Register definition
+*/
+#define ES8389_MASTER_MODE 0x01
+#define ES8389_MASTER_CLK 0x02
+#define ES8389_CLK_OFF1 0x03
+#define ES8389_CLK_DIV1 0x04
+#define ES8389_CLK_MUL 0x05
+#define ES8389_CLK_MUX1 0x06
+#define ES8389_CLK_MUX2 0x07
+#define ES8389_CLK_CTL1 0x08
+#define ES8389_CLK_CTL2 0x09
+#define ES8389_CLK_CTL3 0x0A
+#define ES8389_SCLK_DIV 0x0B
+#define ES8389_LRCK_DIV1 0x0C
+#define ES8389_LRCK_DIV2 0x0D
+#define ES8389_CLK_OFF2 0x0E
+#define ES8389_OSC_CLK 0x0F
+#define ES8389_CSM_JUMP 0x10
+#define ES8389_CLK_DIV2 0x11
+#define ES8389_SYSTEM12 0x12
+#define ES8389_SYSTEM13 0x13
+#define ES8389_SYSTEM14 0x14
+#define ES8389_SYSTEM15 0x15
+#define ES8389_SYSTEM16 0x16
+#define ES8389_SYSTEM17 0x17
+#define ES8389_SYSTEM18 0x18
+#define ES8389_SYSTEM19 0x19
+#define ES8389_SYSTEM1A 0x1A
+#define ES8389_SYSTEM1B 0x1B
+#define ES8389_SYSTEM1C 0x1C
+#define ES8389_ADC_FORMAT_MUTE 0x20
+#define ES8389_ADC_OSR 0x21
+#define ES8389_ADC_DSP 0x22
+#define ES8389_ADC_MODE 0x23
+#define ES8389_ADC_HPF1 0x24
+#define ES8389_ADC_HPF2 0x25
+#define ES8389_OSR_VOL 0x26
+#define ES8389_ADCL_VOL 0x27
+#define ES8389_ADCR_VOL 0x28
+#define ES8389_ALC_CTL 0x29
+#define ES8389_PTDM_SLOT 0x2A
+#define ES8389_ALC_ON 0x2B
+#define ES8389_ALC_TARGET 0x2C
+#define ES8389_ALC_GAIN 0x2D
+#define ES8389_SYSTEM2E 0x2E
+#define ES8389_ADC_MUTE 0x2F
+#define ES8389_SYSTEM30 0x30
+#define ES8389_ADC_RESET 0x31
+#define ES8389_DAC_FORMAT_MUTE 0x40
+#define ES8389_DAC_DSM_OSR 0x41
+#define ES8389_DAC_DSP_OSR 0x42
+#define ES8389_DAC_MISC 0x43
+#define ES8389_DAC_MIX 0x44
+#define ES8389_DAC_INV 0x45
+#define ES8389_DACL_VOL 0x46
+#define ES8389_DACR_VOL 0x47
+#define ES8389_MIX_VOL 0x48
+#define ES8389_DAC_RAMP 0x49
+#define ES8389_SYSTEM4C 0x4C
+#define ES8389_DAC_RESET 0x4D
+#define ES8389_VMID 0x60
+#define ES8389_ANA_CTL1 0x61
+#define ES8389_ANA_VSEL 0x62
+#define ES8389_ANA_CTL2 0x63
+#define ES8389_ADC_EN 0x64
+#define ES8389_HPSW 0x69
+#define ES8389_LOW_POWER1 0x6B
+#define ES8389_LOW_POWER2 0x6C
+#define ES8389_DMIC_EN 0x6D
+#define ES8389_PGA_SW 0x6E
+#define ES8389_MOD_SW1 0x6F
+#define ES8389_MOD_SW2 0x70
+#define ES8389_MOD_SW3 0x71
+#define ES8389_MIC1_GAIN 0x72
+#define ES8389_MIC2_GAIN 0x73
+
+#define ES8389_CHIP_MISC 0xF0
+#define ES8389_CSM_STATE1 0xF1
+#define ES8389_PULL_DOWN 0xF2
+#define ES8389_ISO_CTL 0xF3
+#define ES8389_CSM_STATE2 0xF4
+
+#define ES8389_CHIP_ID0 0xFD
+#define ES8389_CHIP_ID1 0xFE
+
+#define ES8389_MAX_REGISTER 0xFF
+
+#define ES8389_MIC_SEL_MASK (7 << 4)
+#define ES8389_MIC_DEFAULT (1 << 4)
+
+#define ES8389_MASTER_MODE_EN (1 << 0)
+
+#define ES8389_TDM_OFF (0 << 0)
+#define ES8389_STDM_ON (1 << 7)
+#define ES8389_PTDM_ON (1 << 6)
+
+#define ES8389_TDM_MODE ES8389_TDM_OFF
+#define ES8389_TDM_SLOT (0x70 << 0)
+#define ES8389_TDM_SHIFT 4
+
+#define ES8389_MCLK_SOURCE (1 << 6)
+#define ES8389_MCLK_PIN (1 << 6)
+#define ES8389_SCLK_PIN (0 << 6)
+
+/* ES8389_FMT */
+#define ES8389_S24_LE (0 << 5)
+#define ES8389_S20_3_LE (1 << 5)
+#define ES8389_S18_LE (2 << 5)
+#define ES8389_S16_LE (3 << 5)
+#define ES8389_S32_LE (4 << 5)
+#define ES8389_DATA_LEN_MASK (7 << 5)
+
+#define ES8389_DAIFMT_MASK (7 << 2)
+#define ES8389_DAIFMT_I2S 0
+#define ES8389_DAIFMT_LEFT_J (1 << 2)
+#define ES8389_DAIFMT_DSP_A (1 << 3)
+#define ES8389_DAIFMT_DSP_B (3 << 3)
+
+#define ES8389_STATE_ON (13 << 0)
+#define ES8389_STATE_STANDBY (7 << 0)
+
+#endif
diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c
index ddc00927313c..ede980cc6050 100644
--- a/sound/soc/codecs/hda.c
+++ b/sound/soc/codecs/hda.c
@@ -152,7 +152,7 @@ int hda_codec_probe_complete(struct hda_codec *codec)
ret = snd_hda_codec_build_controls(codec);
if (ret < 0) {
dev_err(&hdev->dev, "unable to create controls %d\n", ret);
- goto out;
+ return ret;
}
/* Bus suspended codecs as it does not manage their pm */
@@ -160,9 +160,8 @@ int hda_codec_probe_complete(struct hda_codec *codec)
/* rpm was forbidden in snd_hda_codec_device_new() */
snd_hda_codec_set_power_save(codec, 2000);
snd_hda_codec_register(codec);
-out:
+
/* Complement pm_runtime_get_sync(bus) in probe */
- pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
return ret;
@@ -173,10 +172,10 @@ EXPORT_SYMBOL_GPL(hda_codec_probe_complete);
static int hda_codec_probe(struct snd_soc_component *component)
{
struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hdac_device *hdev = &codec->core;
struct hdac_bus *bus = hdev->bus;
struct hdac_ext_link *hlink;
- hda_codec_patch_t patch;
int ret;
#ifdef CONFIG_PM
@@ -214,14 +213,12 @@ static int hda_codec_probe(struct snd_soc_component *component)
goto err;
}
- patch = (hda_codec_patch_t)codec->preset->driver_data;
- if (!patch) {
- dev_err(&hdev->dev, "no patch specified\n");
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
ret = -EINVAL;
goto err;
}
- ret = patch(codec);
+ ret = driver->ops->probe(codec, codec->preset);
if (ret < 0) {
dev_err(&hdev->dev, "codec init failed: %d\n", ret);
goto err;
@@ -252,8 +249,8 @@ static int hda_codec_probe(struct snd_soc_component *component)
complete_err:
hda_codec_unregister_dais(codec, component);
parse_pcms_err:
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
err:
snd_hda_codec_cleanup_for_unbind(codec);
device_new_err:
@@ -262,7 +259,6 @@ device_new_err:
snd_hdac_ext_bus_link_put(bus, hlink);
- pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
return ret;
}
@@ -271,6 +267,7 @@ device_new_err:
static void hda_codec_remove(struct snd_soc_component *component)
{
struct hda_codec *codec = dev_to_hda_codec(component->dev);
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hdac_device *hdev = &codec->core;
struct hdac_bus *bus = hdev->bus;
struct hdac_ext_link *hlink;
@@ -281,8 +278,8 @@ static void hda_codec_remove(struct snd_soc_component *component)
hda_codec_unregister_dais(codec, component);
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
snd_hda_codec_cleanup_for_unbind(codec);
pm_runtime_put_noidle(&hdev->dev);
@@ -300,7 +297,6 @@ static void hda_codec_remove(struct snd_soc_component *component)
* not be called due to early error, leaving bus uc unbalanced
*/
if (!was_registered) {
- pm_runtime_mark_last_busy(bus->dev);
pm_runtime_put_autosuspend(bus->dev);
}
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 29c88de5508b..afd8edf10fdc 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -409,8 +409,8 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
snd_soc_component_get_dapm(component);
struct hdac_device *hdev = &hda_pvt->codec->core;
struct hda_codec *hcodec = hda_pvt->codec;
+ struct hda_codec_driver *driver = hda_codec_to_driver(hcodec);
struct hdac_ext_link *hlink;
- hda_codec_patch_t patch;
int ret;
hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
@@ -484,15 +484,15 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
goto error_pm;
}
- patch = (hda_codec_patch_t)hcodec->preset->driver_data;
- if (patch) {
- ret = patch(hcodec);
- if (ret < 0) {
- dev_err(&hdev->dev, "%s: patch failed %d\n", __func__, ret);
- goto error_regmap;
- }
- } else {
- dev_dbg(&hdev->dev, "%s: no patch file found\n", __func__);
+ if (WARN_ON(!(driver->ops && driver->ops->probe))) {
+ ret = -EINVAL;
+ goto error_regmap;
+ }
+
+ ret = driver->ops->probe(hcodec, hcodec->preset);
+ if (ret < 0) {
+ dev_err(&hdev->dev, "%s: probe failed %d\n", __func__, ret);
+ goto error_regmap;
}
ret = snd_hda_codec_parse_pcms(hcodec);
@@ -531,8 +531,8 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
return 0;
error_patch:
- if (hcodec->patch_ops.free)
- hcodec->patch_ops.free(hcodec);
+ if (driver->ops->remove)
+ driver->ops->remove(hcodec);
error_regmap:
snd_hdac_regmap_exit(hdev);
error_pm:
@@ -548,6 +548,7 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
snd_soc_component_get_drvdata(component);
struct hdac_device *hdev = &hda_pvt->codec->core;
struct hda_codec *codec = hda_pvt->codec;
+ struct hda_codec_driver *driver = hda_codec_to_driver(codec);
struct hdac_ext_link *hlink = NULL;
hlink = snd_hdac_ext_bus_get_hlink_by_name(hdev->bus, dev_name(&hdev->dev));
@@ -559,8 +560,8 @@ static void hdac_hda_codec_remove(struct snd_soc_component *component)
pm_runtime_disable(&hdev->dev);
snd_hdac_ext_bus_link_put(hdev->bus, hlink);
- if (codec->patch_ops.free)
- codec->patch_ops.free(codec);
+ if (driver->ops->remove)
+ driver->ops->remove(codec);
snd_hda_codec_cleanup_for_unbind(codec);
}
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 1139a2754ca3..e50afd0bfcec 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -24,8 +24,6 @@
#include <sound/hda_i915.h>
#include <sound/pcm_drm_eld.h>
#include <sound/hda_chmap.h>
-#include "../../hda/local.h"
-#include "hdac_hdmi.h"
#define NAME_SIZE 32
@@ -221,8 +219,8 @@ static int hdac_hdmi_get_port_len(struct hdac_device *hdev, hda_nid_t nid)
unsigned int caps;
unsigned int type, param;
- caps = get_wcaps(hdev, nid);
- type = get_wcaps_type(caps);
+ caps = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL) || (type != AC_WID_PIN))
return 0;
@@ -492,10 +490,10 @@ static int hdac_hdmi_query_port_connlist(struct hdac_device *hdev,
struct hdac_hdmi_pin *pin,
struct hdac_hdmi_port *port)
{
- if (!(get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) {
+ if (!(snd_hdac_get_wcaps(hdev, pin->nid) & AC_WCAP_CONN_LIST)) {
dev_warn(&hdev->dev,
"HDMI: pin %d wcaps %#x does not support connection list\n",
- pin->nid, get_wcaps(hdev, pin->nid));
+ pin->nid, snd_hdac_get_wcaps(hdev, pin->nid));
return -EINVAL;
}
@@ -660,8 +658,8 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdev, struct hdac_hdmi_cvt *cvt)
struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
int err;
- chans = get_wcaps(hdev, cvt->nid);
- chans = get_wcaps_channels(chans);
+ chans = snd_hdac_get_wcaps(hdev, cvt->nid);
+ chans = snd_hdac_get_wcaps_channels(chans);
cvt->params.channels_min = 2;
@@ -743,7 +741,7 @@ static void hdac_hdmi_set_power_state(struct hdac_device *hdev,
int count;
unsigned int state;
- if (get_wcaps(hdev, nid) & AC_WCAP_POWER) {
+ if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_POWER) {
if (!snd_hdac_check_power_state(hdev, nid, pwr_state)) {
for (count = 0; count < 10; count++) {
snd_hdac_codec_read(hdev, nid, 0,
@@ -761,7 +759,7 @@ static void hdac_hdmi_set_power_state(struct hdac_device *hdev,
static void hdac_hdmi_set_amp(struct hdac_device *hdev,
hda_nid_t nid, int val)
{
- if (get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP)
+ if (snd_hdac_get_wcaps(hdev, nid) & AC_WCAP_OUT_AMP)
snd_hdac_codec_write(hdev, nid, 0,
AC_VERB_SET_AMP_GAIN_MUTE, val);
}
@@ -1232,7 +1230,8 @@ static int hdac_hdmi_parse_eld(struct hdac_device *hdev,
>> DRM_ELD_VER_SHIFT;
if (ver != ELD_VER_CEA_861D && ver != ELD_VER_PARTIAL) {
- dev_err(&hdev->dev, "HDMI: Unknown ELD version %d\n", ver);
+ dev_err_ratelimited(&hdev->dev,
+ "HDMI: Unknown ELD version %d\n", ver);
return -EINVAL;
}
@@ -1240,7 +1239,8 @@ static int hdac_hdmi_parse_eld(struct hdac_device *hdev,
DRM_ELD_MNL_MASK) >> DRM_ELD_MNL_SHIFT;
if (mnl > ELD_MAX_MNL) {
- dev_err(&hdev->dev, "HDMI: MNL Invalid %d\n", mnl);
+ dev_err_ratelimited(&hdev->dev,
+ "HDMI: MNL Invalid %d\n", mnl);
return -EINVAL;
}
@@ -1299,8 +1299,8 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin,
if (!port->eld.monitor_present || !port->eld.eld_valid) {
- dev_err(&hdev->dev, "%s: disconnect for pin:port %d:%d\n",
- __func__, pin->nid, port->id);
+ dev_dbg(&hdev->dev, "%s: disconnect for pin:port %d:%d\n",
+ __func__, pin->nid, port->id);
/*
* PCMs are not registered during device probe, so don't
@@ -1431,122 +1431,6 @@ static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdev)
}
-static int hdac_hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_info *uinfo)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
- struct hdac_hdmi_pcm *pcm;
- struct hdac_hdmi_port *port;
- struct hdac_hdmi_eld *eld;
-
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = 0;
-
- pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
- if (!pcm) {
- dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
- kcontrol->id.device);
- return 0;
- }
-
- if (list_empty(&pcm->port_list)) {
- dev_dbg(component->dev, "%s: empty port list, device %d\n",
- __func__, kcontrol->id.device);
- return 0;
- }
-
- mutex_lock(&hdmi->pin_mutex);
-
- list_for_each_entry(port, &pcm->port_list, head) {
- eld = &port->eld;
-
- if (eld->eld_valid) {
- uinfo->count = eld->eld_size;
- break;
- }
- }
-
- mutex_unlock(&hdmi->pin_mutex);
-
- return 0;
-}
-
-static int hdac_hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
- struct hdac_hdmi_pcm *pcm;
- struct hdac_hdmi_port *port;
- struct hdac_hdmi_eld *eld;
-
- memset(ucontrol->value.bytes.data, 0, sizeof(ucontrol->value.bytes.data));
-
- pcm = get_hdmi_pcm_from_id(hdmi, kcontrol->id.device);
- if (!pcm) {
- dev_dbg(component->dev, "%s: no pcm, device %d\n", __func__,
- kcontrol->id.device);
- return 0;
- }
-
- if (list_empty(&pcm->port_list)) {
- dev_dbg(component->dev, "%s: empty port list, device %d\n",
- __func__, kcontrol->id.device);
- return 0;
- }
-
- mutex_lock(&hdmi->pin_mutex);
-
- list_for_each_entry(port, &pcm->port_list, head) {
- eld = &port->eld;
-
- if (!eld->eld_valid)
- continue;
-
- if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
- eld->eld_size > ELD_MAX_SIZE) {
- mutex_unlock(&hdmi->pin_mutex);
-
- dev_err(component->dev, "%s: buffer too small, device %d eld_size %d\n",
- __func__, kcontrol->id.device, eld->eld_size);
- snd_BUG();
- return -EINVAL;
- }
-
- memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
- eld->eld_size);
- break;
- }
-
- mutex_unlock(&hdmi->pin_mutex);
-
- return 0;
-}
-
-static int hdac_hdmi_create_eld_ctl(struct snd_soc_component *component, struct hdac_hdmi_pcm *pcm)
-{
- struct snd_kcontrol *kctl;
- struct snd_kcontrol_new hdmi_eld_ctl = {
- .access = SNDRV_CTL_ELEM_ACCESS_READ |
- SNDRV_CTL_ELEM_ACCESS_VOLATILE,
- .iface = SNDRV_CTL_ELEM_IFACE_PCM,
- .name = "ELD",
- .info = hdac_hdmi_eld_ctl_info,
- .get = hdac_hdmi_eld_ctl_get,
- .device = pcm->pcm_id,
- };
-
- /* add ELD ctl with the device number corresponding to the PCM stream */
- kctl = snd_ctl_new1(&hdmi_eld_ctl, component);
- if (!kctl)
- return -ENOMEM;
-
- pcm->eld_ctl = kctl;
-
- return snd_ctl_add(component->card->snd_card, kctl);
-}
-
static const struct snd_soc_dai_ops hdmi_dai_ops = {
.startup = hdac_hdmi_pcm_open,
.shutdown = hdac_hdmi_pcm_close,
@@ -1648,8 +1532,8 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_device *hdev,
unsigned int caps;
unsigned int type;
- caps = get_wcaps(hdev, nid);
- type = get_wcaps_type(caps);
+ caps = snd_hdac_get_wcaps(hdev, nid);
+ type = snd_hdac_get_wcaps_type(caps);
if (!(caps & AC_WCAP_DIGITAL))
continue;
@@ -1754,186 +1638,6 @@ static struct drm_audio_component_audio_ops aops = {
.pin_eld_notify = hdac_hdmi_eld_notify_cb,
};
-static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card,
- int device)
-{
- struct snd_soc_pcm_runtime *rtd;
-
- for_each_card_rtds(card, rtd) {
- if (rtd->pcm && (rtd->pcm->device == device))
- return rtd->pcm;
- }
-
- return NULL;
-}
-
-/* create jack pin kcontrols */
-static int create_fill_jack_kcontrols(struct snd_soc_card *card,
- struct hdac_device *hdev)
-{
- struct hdac_hdmi_pin *pin;
- struct snd_kcontrol_new *kc;
- char *name;
- int i = 0, j;
- struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
- struct snd_soc_component *component = hdmi->component;
-
- kc = devm_kcalloc(component->dev, hdmi->num_ports,
- sizeof(*kc), GFP_KERNEL);
-
- if (!kc)
- return -ENOMEM;
-
- list_for_each_entry(pin, &hdmi->pin_list, head) {
- for (j = 0; j < pin->num_ports; j++) {
- name = devm_kasprintf(component->dev, GFP_KERNEL,
- "hif%d-%d Jack",
- pin->nid, pin->ports[j].id);
- if (!name)
- return -ENOMEM;
-
- kc[i].name = devm_kasprintf(component->dev, GFP_KERNEL,
- "%s Switch", name);
- if (!kc[i].name)
- return -ENOMEM;
-
- kc[i].private_value = (unsigned long)name;
- kc[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
- kc[i].access = 0;
- kc[i].info = snd_soc_dapm_info_pin_switch;
- kc[i].put = snd_soc_dapm_put_pin_switch;
- kc[i].get = snd_soc_dapm_get_pin_switch;
- i++;
- }
- }
-
- return snd_soc_add_card_controls(card, kc, i);
-}
-
-int hdac_hdmi_jack_port_init(struct snd_soc_component *component,
- struct snd_soc_dapm_context *dapm)
-{
- struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
- struct hdac_device *hdev = hdmi->hdev;
- struct hdac_hdmi_pin *pin;
- struct snd_soc_dapm_widget *widgets;
- struct snd_soc_dapm_route *route;
- char w_name[NAME_SIZE];
- int i = 0, j, ret;
-
- widgets = devm_kcalloc(dapm->dev, hdmi->num_ports,
- sizeof(*widgets), GFP_KERNEL);
-
- if (!widgets)
- return -ENOMEM;
-
- route = devm_kcalloc(dapm->dev, hdmi->num_ports,
- sizeof(*route), GFP_KERNEL);
- if (!route)
- return -ENOMEM;
-
- /* create Jack DAPM widget */
- list_for_each_entry(pin, &hdmi->pin_list, head) {
- for (j = 0; j < pin->num_ports; j++) {
- snprintf(w_name, sizeof(w_name), "hif%d-%d Jack",
- pin->nid, pin->ports[j].id);
-
- ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
- snd_soc_dapm_spk, NULL,
- w_name, NULL, NULL, 0, NULL, 0);
- if (ret < 0)
- return ret;
-
- pin->ports[j].jack_pin = widgets[i].name;
- pin->ports[j].dapm = dapm;
-
- /* add to route from Jack widget to output */
- hdac_hdmi_fill_route(&route[i], pin->ports[j].jack_pin,
- NULL, pin->ports[j].output_pin, NULL);
-
- i++;
- }
- }
-
- /* Add Route from Jack widget to the output widget */
- ret = snd_soc_dapm_new_controls(dapm, widgets, hdmi->num_ports);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dapm_add_routes(dapm, route, hdmi->num_ports);
- if (ret < 0)
- return ret;
-
- ret = snd_soc_dapm_new_widgets(dapm->card);
- if (ret < 0)
- return ret;
-
- /* Add Jack Pin switch Kcontrol */
- ret = create_fill_jack_kcontrols(dapm->card, hdev);
-
- if (ret < 0)
- return ret;
-
- /* default set the Jack Pin switch to OFF */
- list_for_each_entry(pin, &hdmi->pin_list, head) {
- for (j = 0; j < pin->num_ports; j++)
- snd_soc_dapm_disable_pin(pin->ports[j].dapm,
- pin->ports[j].jack_pin);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(hdac_hdmi_jack_port_init);
-
-int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
- struct snd_soc_jack *jack)
-{
- struct snd_soc_component *component = dai->component;
- struct hdac_hdmi_priv *hdmi = snd_soc_component_get_drvdata(component);
- struct hdac_device *hdev = hdmi->hdev;
- struct hdac_hdmi_pcm *pcm;
- struct snd_pcm *snd_pcm;
- int err;
-
- /*
- * this is a new PCM device, create new pcm and
- * add to the pcm list
- */
- pcm = devm_kzalloc(&hdev->dev, sizeof(*pcm), GFP_KERNEL);
- if (!pcm)
- return -ENOMEM;
- pcm->pcm_id = device;
- pcm->cvt = hdmi->dai_map[dai->id].cvt;
- pcm->jack_event = 0;
- pcm->jack = jack;
- mutex_init(&pcm->lock);
- INIT_LIST_HEAD(&pcm->port_list);
- snd_pcm = hdac_hdmi_get_pcm_from_id(dai->component->card, device);
- if (snd_pcm) {
- err = snd_hdac_add_chmap_ctls(snd_pcm, device, &hdmi->chmap);
- if (err < 0) {
- dev_err(&hdev->dev,
- "chmap control add failed with err: %d for pcm: %d\n",
- err, device);
- return err;
- }
- }
-
- /* add control for ELD Bytes */
- err = hdac_hdmi_create_eld_ctl(component, pcm);
- if (err < 0) {
- dev_err(&hdev->dev,
- "eld control add failed with err: %d for pcm: %d\n",
- err, device);
- return err;
- }
-
- list_add_tail(&pcm->head, &hdmi->pcm_list);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
-
static void hdac_hdmi_present_sense_all_pins(struct hdac_device *hdev,
struct hdac_hdmi_priv *hdmi, bool detect_pin_caps)
{
diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h
deleted file mode 100644
index 493fa3b4ef75..000000000000
--- a/sound/soc/codecs/hdac_hdmi.h
+++ /dev/null
@@ -1,10 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef __HDAC_HDMI_H__
-#define __HDAC_HDMI_H__
-
-int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device,
- struct snd_soc_jack *jack);
-
-int hdac_hdmi_jack_port_init(struct snd_soc_component *component,
- struct snd_soc_dapm_context *dapm);
-#endif /* __HDAC_HDMI_H__ */
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index bc01ff65bd6f..e1933f733af1 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -943,7 +943,7 @@ static void hdmi_codec_jack_report(struct hdmi_codec_priv *hcp,
{
if (jack_status != hcp->jack_status) {
if (hcp->jack)
- snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_LINEOUT);
+ snd_soc_jack_report(hcp->jack, jack_status, SND_JACK_AVOUT);
hcp->jack_status = jack_status;
}
}
@@ -964,7 +964,7 @@ static void plugged_cb(struct device *dev, bool plugged)
else
snd_show_eld(dev, &hcp->eld_parsed);
}
- hdmi_codec_jack_report(hcp, SND_JACK_LINEOUT);
+ hdmi_codec_jack_report(hcp, SND_JACK_AVOUT);
} else {
hdmi_codec_jack_report(hcp, 0);
memset(hcp->eld, 0, sizeof(hcp->eld));
@@ -984,7 +984,7 @@ static int hdmi_codec_set_jack(struct snd_soc_component *component,
* Report the initial jack status which may have been provided
* by the parent hdmi driver while the hpd hook was registered.
*/
- snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_LINEOUT);
+ snd_soc_jack_report(jack, hcp->jack_status, SND_JACK_AVOUT);
return 0;
}
@@ -1026,6 +1026,7 @@ static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
.startup = hdmi_codec_startup,
.shutdown = hdmi_codec_shutdown,
.hw_params = hdmi_codec_hw_params,
+ .prepare = hdmi_codec_prepare,
.mute_stream = hdmi_codec_mute,
.pcm_new = hdmi_codec_pcm_new,
};
diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c
index cb7a68c799f8..55e90604bbaa 100644
--- a/sound/soc/codecs/idt821034.c
+++ b/sound/soc/codecs/idt821034.c
@@ -957,7 +957,8 @@ static const struct snd_soc_component_driver idt821034_component_driver = {
#define IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(_offset) (((_offset) / 5) % 4)
#define IDT821034_GPIO_OFFSET_TO_SLIC_MASK(_offset) BIT((_offset) % 5)
-static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+static int idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset,
+ int val)
{
u8 ch = IDT821034_GPIO_OFFSET_TO_SLIC_CHANNEL(offset);
u8 mask = IDT821034_GPIO_OFFSET_TO_SLIC_MASK(offset);
@@ -973,12 +974,14 @@ static void idt821034_chip_gpio_set(struct gpio_chip *c, unsigned int offset, in
else
slic_raw &= ~mask;
ret = idt821034_write_slic_raw(idt821034, ch, slic_raw);
- if (ret) {
+
+ mutex_unlock(&idt821034->mutex);
+
+ if (ret)
dev_err(&idt821034->spi->dev, "set gpio %d (%u, 0x%x) failed (%d)\n",
offset, ch, mask, ret);
- }
- mutex_unlock(&idt821034->mutex);
+ return ret;
}
static int idt821034_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
@@ -1054,7 +1057,9 @@ static int idt821034_chip_direction_output(struct gpio_chip *c, unsigned int off
u8 slic_conf;
int ret;
- idt821034_chip_gpio_set(c, offset, val);
+ ret = idt821034_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
mutex_lock(&idt821034->mutex);
@@ -1112,7 +1117,7 @@ static int idt821034_gpio_init(struct idt821034 *idt821034)
idt821034->gpio_chip.direction_input = idt821034_chip_direction_input;
idt821034->gpio_chip.direction_output = idt821034_chip_direction_output;
idt821034->gpio_chip.get = idt821034_chip_gpio_get;
- idt821034->gpio_chip.set = idt821034_chip_gpio_set;
+ idt821034->gpio_chip.set_rv = idt821034_chip_gpio_set;
idt821034->gpio_chip.can_sleep = true;
return devm_gpiochip_add_data(&idt821034->spi->dev, &idt821034->gpio_chip,
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index 45a6b83808b2..238dbdb46c18 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -619,7 +619,6 @@ static struct interp_sample_rate sr_val_tbl[] = {
};
enum {
- RX_MACRO_AIF_INVALID = 0,
RX_MACRO_AIF1_PB,
RX_MACRO_AIF2_PB,
RX_MACRO_AIF3_PB,
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index 27bae58f4072..40d79bee4584 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -208,7 +208,6 @@
#define MCLK_FREQ 19200000
enum {
- TX_MACRO_AIF_INVALID = 0,
TX_MACRO_AIF1_CAP,
TX_MACRO_AIF2_CAP,
TX_MACRO_AIF3_CAP,
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index 74ada6e77526..a49551f3fb29 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -165,7 +165,6 @@
static const DECLARE_TLV_DB_SCALE(digital_gain, -8400, 100, -8400);
enum {
- VA_MACRO_AIF_INVALID = 0,
VA_MACRO_AIF1_CAP,
VA_MACRO_AIF2_CAP,
VA_MACRO_AIF3_CAP,
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index c1fb71cfb5d0..da6adb3de21d 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -369,7 +369,6 @@ static struct interp_sample_rate int_mix_sample_rate_val[] = {
};
enum {
- WSA_MACRO_AIF_INVALID = 0,
WSA_MACRO_AIF1_PB,
WSA_MACRO_AIF_MIX1_PB,
WSA_MACRO_AIF_VI,
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
index 950105e5bffd..25af78ab30d5 100644
--- a/sound/soc/codecs/max98363.c
+++ b/sound/soc/codecs/max98363.c
@@ -14,7 +14,7 @@
#include "max98363.h"
-static struct reg_default max98363_reg[] = {
+static const struct reg_default max98363_reg[] = {
{MAX98363_R2021_ERR_MON_CTRL, 0x0},
{MAX98363_R2022_SPK_MON_THRESH, 0x0},
{MAX98363_R2023_SPK_MON_DURATION, 0x0},
@@ -188,7 +188,6 @@ static int max98363_io_init(struct sdw_slave *slave)
max98363->hw_init = true;
out:
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return ret;
diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c
index 56c4ba1f3782..f58b8c8625a7 100644
--- a/sound/soc/codecs/max98373-i2c.c
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -23,7 +23,7 @@ static const u32 max98373_i2c_cache_reg[] = {
MAX98373_R20B6_BDE_CUR_STATE_READBACK,
};
-static struct reg_default max98373_reg[] = {
+static const struct reg_default max98373_reg[] = {
{MAX98373_R2000_SW_RESET, 0x00},
{MAX98373_R2001_INT_RAW1, 0x00},
{MAX98373_R2002_INT_RAW2, 0x00},
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index 6088278e6503..88ff215f52b3 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -26,7 +26,7 @@ static const u32 max98373_sdw_cache_reg[] = {
MAX98373_R20B6_BDE_CUR_STATE_READBACK,
};
-static struct reg_default max98373_reg[] = {
+static const struct reg_default max98373_reg[] = {
{MAX98373_R0040_SCP_INIT_STAT_1, 0x00},
{MAX98373_R0041_SCP_INIT_MASK_1, 0x00},
{MAX98373_R0042_SCP_INIT_STAT_2, 0x00},
@@ -458,7 +458,6 @@ static int max98373_io_init(struct sdw_slave *slave)
max98373->first_hw_init = true;
max98373->hw_init = true;
- pm_runtime_mark_last_busy(dev);
pm_runtime_put_autosuspend(dev);
return 0;
diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c
index 99986090b4a6..076f15a9867e 100644
--- a/sound/soc/codecs/max98388.c
+++ b/sound/soc/codecs/max98388.c
@@ -18,7 +18,7 @@
#include <sound/tlv.h>
#include "max98388.h"
-static struct reg_default max98388_reg[] = {
+static const struct reg_default max98388_reg[] = {
{MAX98388_R2000_SW_RESET, 0x00},
{MAX98388_R2001_INT_RAW1, 0x00},
{MAX98388_R2002_INT_RAW2, 0x00},
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index 76296176f948..a8a282ff9fc5 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -23,7 +23,7 @@
#include "max98390.h"
-static struct reg_default max98390_reg_defaults[] = {
+static const struct reg_default max98390_reg_defaults[] = {
{MAX98390_INT_EN1, 0xf0},
{MAX98390_INT_EN2, 0x00},
{MAX98390_INT_EN3, 0x00},
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
index c1888cd83dbc..4b4e1fc98a6d 100644
--- a/sound/soc/codecs/max98396.c
+++ b/sound/soc/codecs/max98396.c
@@ -16,7 +16,7 @@ static const char * const max98396_core_supplies[MAX98396_NUM_CORE_SUPPLIES] = {
"dvddio",
};
-static struct reg_default max98396_reg[] = {
+static const struct reg_default max98396_reg[] = {
{MAX98396_R2000_SW_RESET, 0x00},
{MAX98396_R2001_INT_RAW1, 0x00},
{MAX98396_R2002_INT_RAW2, 0x00},
@@ -174,7 +174,7 @@ static struct reg_default max98396_reg[] = {
{MAX98396_R21FF_REVISION_ID, 0x00},
};
-static struct reg_default max98397_reg[] = {
+static const struct reg_default max98397_reg[] = {
{MAX98396_R2000_SW_RESET, 0x00},
{MAX98396_R2001_INT_RAW1, 0x00},
{MAX98396_R2002_INT_RAW2, 0x00},
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
index 6b6a7ece4cec..c94142768c81 100644
--- a/sound/soc/codecs/max98504.c
+++ b/sound/soc/codecs/max98504.c
@@ -35,7 +35,7 @@ struct max98504_priv {
unsigned int brownout_release_rate;
};
-static struct reg_default max98504_reg_defaults[] = {
+static const struct reg_default max98504_reg_defaults[] = {
{ 0x01, 0},
{ 0x02, 0},
{ 0x03, 0},
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
index adf5a898c6df..2bf8976c1828 100644
--- a/sound/soc/codecs/max98520.c
+++ b/sound/soc/codecs/max98520.c
@@ -16,7 +16,7 @@
#include <sound/tlv.h>
#include "max98520.h"
-static struct reg_default max98520_reg[] = {
+static const struct reg_default max98520_reg[] = {
{MAX98520_R2000_SW_RESET, 0x00},
{MAX98520_R2001_STATUS_1, 0x00},
{MAX98520_R2002_STATUS_2, 0x00},
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index 55cc18451a2d..0e9b8970997c 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -19,7 +19,7 @@
#include <sound/tlv.h>
#include "max98927.h"
-static struct reg_default max98927_reg[] = {
+static const struct reg_default max98927_reg[] = {
{MAX98927_R0001_INT_RAW1, 0x00},
{MAX98927_R0002_INT_RAW2, 0x00},
{MAX98927_R0003_INT_RAW3, 0x00},
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
index b2bd2f172ae7..75af12231d1d 100644
--- a/sound/soc/codecs/pcm6240.c
+++ b/sound/soc/codecs/pcm6240.c
@@ -1642,8 +1642,7 @@ static int pcmdevice_comp_probe(struct snd_soc_component *comp)
}
ret = pcmdev_profile_ctrl_add(pcm_dev);
out:
- if (fw_entry)
- release_firmware(fw_entry);
+ release_firmware(fw_entry);
mutex_unlock(&pcm_dev->codec_lock);
return ret;
diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c
index a989cfe058f0..b8905c03445e 100644
--- a/sound/soc/codecs/peb2466.c
+++ b/sound/soc/codecs/peb2466.c
@@ -1726,7 +1726,8 @@ end:
return ret;
}
-static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+static int peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset,
+ int val)
{
struct peb2466 *peb2466 = gpiochip_get_data(c);
unsigned int xr_reg;
@@ -1740,14 +1741,14 @@ static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int
*/
dev_warn(&peb2466->spi->dev, "cannot set gpio %d (read-only)\n",
offset);
- return;
+ return -EINVAL;
}
ret = peb2466_chip_gpio_offset_to_data_regmask(offset, &xr_reg, &mask);
if (ret) {
dev_err(&peb2466->spi->dev, "cannot set gpio %d (%d)\n",
offset, ret);
- return;
+ return ret;
}
ret = peb2466_chip_gpio_update_bits(peb2466, xr_reg, mask, val ? mask : 0);
@@ -1755,6 +1756,8 @@ static void peb2466_chip_gpio_set(struct gpio_chip *c, unsigned int offset, int
dev_err(&peb2466->spi->dev, "set gpio %d (0x%x, 0x%x) failed (%d)\n",
offset, xr_reg, mask, ret);
}
+
+ return ret;
}
static int peb2466_chip_gpio_get(struct gpio_chip *c, unsigned int offset)
@@ -1879,7 +1882,9 @@ static int peb2466_chip_direction_output(struct gpio_chip *c, unsigned int offse
return -EINVAL;
}
- peb2466_chip_gpio_set(c, offset, val);
+ ret = peb2466_chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
if (offset < 16) {
/* SOx_{0,1} */
@@ -1940,7 +1945,7 @@ static int peb2466_gpio_init(struct peb2466 *peb2466)
peb2466->gpio.gpio_chip.direction_input = peb2466_chip_direction_input;
peb2466->gpio.gpio_chip.direction_output = peb2466_chip_direction_output;
peb2466->gpio.gpio_chip.get = peb2466_chip_gpio_get;
- peb2466->gpio.gpio_chip.set = peb2466_chip_gpio_set;
+ peb2466->gpio.gpio_chip.set_rv = peb2466_chip_gpio_set;
peb2466->gpio.gpio_chip.can_sleep = true;
return devm_gpiochip_add_data(&peb2466->spi->dev, &peb2466->gpio.gpio_chip,
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c
index 88fc23a4999f..a9c000876be8 100644
--- a/sound/soc/codecs/rt1017-sdca-sdw.c
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -362,7 +362,6 @@ static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt1017->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "hw_init complete\n");
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index ea708068f0e8..b6c224832a43 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -291,7 +291,6 @@ _preset_ready_:
/* Mark Slave initialization complete */
rt1308->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index 960b6c4f5a66..01a977398864 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -302,7 +302,6 @@ static int rt1316_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt1316->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
index 4eb636e0c9ed..70db5450d6d2 100644
--- a/sound/soc/codecs/rt1318-sdw.c
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -434,7 +434,6 @@ static int rt1318_io_init(struct device *dev, struct sdw_slave *slave)
rt1318->first_hw_init = true;
rt1318->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index f51ba345a16e..b13d7a99bf63 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -204,7 +204,7 @@ static const struct reg_sequence rt1320_vc_blind_write[] = {
{ 0x3fc2bfc0, 0x03 },
{ 0x0000d486, 0x43 },
{ SDW_SDCA_CTL(FUNC_NUM_AMP, RT1320_SDCA_ENT_PDE23, RT1320_SDCA_CTL_REQ_POWER_STATE, 0), 0x00 },
- { 0x1000db00, 0x04 },
+ { 0x1000db00, 0x07 },
{ 0x1000db01, 0x00 },
{ 0x1000db02, 0x11 },
{ 0x1000db03, 0x00 },
@@ -225,6 +225,21 @@ static const struct reg_sequence rt1320_vc_blind_write[] = {
{ 0x1000db12, 0x00 },
{ 0x1000db13, 0x00 },
{ 0x1000db14, 0x45 },
+ { 0x1000db15, 0x0d },
+ { 0x1000db16, 0x01 },
+ { 0x1000db17, 0x00 },
+ { 0x1000db18, 0x00 },
+ { 0x1000db19, 0xbf },
+ { 0x1000db1a, 0x13 },
+ { 0x1000db1b, 0x09 },
+ { 0x1000db1c, 0x00 },
+ { 0x1000db1d, 0x00 },
+ { 0x1000db1e, 0x00 },
+ { 0x1000db1f, 0x12 },
+ { 0x1000db20, 0x09 },
+ { 0x1000db21, 0x00 },
+ { 0x1000db22, 0x00 },
+ { 0x1000db23, 0x00 },
{ 0x0000d540, 0x01 },
{ 0x0000c081, 0xfc },
{ 0x0000f01e, 0x80 },
@@ -748,7 +763,6 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
rt1320->first_hw_init = true;
rt1320->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 21a18012b4c0..f50e771db24b 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -193,7 +193,7 @@ static bool rt5640_volatile_register(struct device *dev, unsigned int reg)
case RT5640_PRIV_DATA:
case RT5640_PGM_REG_ARR1:
case RT5640_PGM_REG_ARR3:
- case RT5640_DUMMY2:
+ case RT5640_GCTL2:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
case RT5640_VENDOR_ID2:
@@ -325,8 +325,8 @@ static bool rt5640_readable_register(struct device *dev, unsigned int reg)
case RT5640_HP_CALIB2:
case RT5640_SV_ZCD1:
case RT5640_SV_ZCD2:
- case RT5640_DUMMY1:
- case RT5640_DUMMY2:
+ case RT5640_GCTL1:
+ case RT5640_GCTL2:
case RT5640_DUMMY3:
case RT5640_VENDOR_ID:
case RT5640_VENDOR_ID1:
@@ -423,7 +423,7 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
SOC_DOUBLE_TLV("ADC Capture Volume", RT5640_ADC_DIG_VOL,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
127, 0, adc_vol_tlv),
- SOC_DOUBLE("Mono ADC Capture Switch", RT5640_DUMMY1,
+ SOC_DOUBLE("Mono ADC Capture Switch", RT5640_GCTL1,
RT5640_M_MONO_ADC_L_SFT, RT5640_M_MONO_ADC_R_SFT, 1, 1),
SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5640_ADC_DATA,
RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
@@ -1969,7 +1969,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_FV1 | RT5640_PWR_FV2,
RT5640_PWR_FV1 | RT5640_PWR_FV2);
- snd_soc_component_update_bits(component, RT5640_DUMMY1,
+ snd_soc_component_update_bits(component, RT5640_GCTL1,
0x1, 0x1);
snd_soc_component_update_bits(component, RT5640_MICBIAS,
0x0030, 0x0030);
@@ -1979,7 +1979,7 @@ static int rt5640_set_bias_level(struct snd_soc_component *component,
case SND_SOC_BIAS_OFF:
snd_soc_component_write(component, RT5640_DEPOP_M1, 0x0004);
snd_soc_component_write(component, RT5640_DEPOP_M2, 0x1100);
- snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x1, 0);
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 0x1, 0);
snd_soc_component_write(component, RT5640_PWR_DIG1, 0x0000);
snd_soc_component_write(component, RT5640_PWR_DIG2, 0x0000);
snd_soc_component_write(component, RT5640_PWR_VOL, 0x0000);
@@ -2328,12 +2328,12 @@ static void rt5640_jack_work(struct work_struct *work)
jack_type |= SND_JACK_MICROPHONE;
/* headphone jack */
- val = snd_soc_component_read(component, RT5640_DUMMY2);
+ val = snd_soc_component_read(component, RT5640_GCTL2);
hda_hp_plugged = !(val & (0x1 << 11));
dev_dbg(component->dev, "headphone jack status %d\n",
hda_hp_plugged);
- snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ snd_soc_component_update_bits(component, RT5640_GCTL2,
(0x1 << 10), !hda_hp_plugged << 10);
if (hda_hp_plugged)
@@ -2504,7 +2504,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
- snd_soc_component_write(component, RT5640_DUMMY1, 0x3f41);
+ snd_soc_component_write(component, RT5640_GCTL1, 0x3f41);
rt5640_set_ovcd_params(component);
@@ -2519,7 +2519,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
snd_soc_component_write(component, RT5640_IRQ_CTRL1,
RT5640_IRQ_JD_NOR);
else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
- snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ snd_soc_component_update_bits(component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK | RT5640_JD2_MASK,
RT5640_IRQ_JD2_NOR | RT5640_JD2_EN);
} else {
@@ -2527,7 +2527,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
snd_soc_component_write(component, RT5640_IRQ_CTRL1,
RT5640_IRQ_JD_NOR | RT5640_JD_P_INV);
else if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
- snd_soc_component_update_bits(component, RT5640_DUMMY2,
+ snd_soc_component_update_bits(component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK | RT5640_JD2_P_MASK |
RT5640_JD2_MASK,
RT5640_IRQ_JD2_NOR | RT5640_JD2_P_INV |
@@ -2596,7 +2596,7 @@ static void rt5640_enable_hda_jack_detect(
snd_soc_component_write(component, RT5640_IRQ_CTRL1, RT5640_IRQ_JD_NOR);
/* Select JD2 for Headphone */
- snd_soc_component_update_bits(component, RT5640_DUMMY2, 0x1100, 0x1100);
+ snd_soc_component_update_bits(component, RT5640_GCTL2, 0x1100, 0x1100);
/* Selecting GPIO01 as an interrupt */
snd_soc_component_update_bits(component, RT5640_GPIO_CTRL1,
@@ -2606,7 +2606,7 @@ static void rt5640_enable_hda_jack_detect(
snd_soc_component_update_bits(component, RT5640_GPIO_CTRL3,
RT5640_GP1_PF_MASK, RT5640_GP1_PF_OUT);
- snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x400, 0x0);
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 0x400, 0x0);
snd_soc_component_update_bits(component, RT5640_PWR_ANLG1,
RT5640_PWR_VREF2 | RT5640_PWR_MB | RT5640_PWR_BG,
@@ -2668,7 +2668,7 @@ static int rt5640_probe(struct snd_soc_component *component)
snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF);
- snd_soc_component_update_bits(component, RT5640_DUMMY1, 0x0301, 0x0301);
+ snd_soc_component_update_bits(component, RT5640_GCTL1, 0x0301, 0x0301);
snd_soc_component_update_bits(component, RT5640_MICBIAS, 0x0030, 0x0030);
snd_soc_component_update_bits(component, RT5640_DSP_PATH2, 0xfc00, 0x0c00);
@@ -2719,7 +2719,7 @@ static int rt5640_probe(struct snd_soc_component *component)
RT5640_IN_DF2, RT5640_IN_DF2);
if (device_property_read_bool(component->dev, "realtek,lout-differential"))
- snd_soc_component_update_bits(component, RT5640_DUMMY1,
+ snd_soc_component_update_bits(component, RT5640_GCTL1,
RT5640_EN_LOUT_DF, RT5640_EN_LOUT_DF);
if (device_property_read_u32(component->dev, "realtek,dmic1-data-pin",
@@ -2829,12 +2829,12 @@ static int rt5640_resume(struct snd_soc_component *component)
if (rt5640->jack) {
if (rt5640->jd_src == RT5640_JD_SRC_HDA_HEADER) {
snd_soc_component_update_bits(component,
- RT5640_DUMMY2, 0x1100, 0x1100);
+ RT5640_GCTL2, 0x1100, 0x1100);
} else {
if (rt5640->jd_inverted) {
if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
snd_soc_component_update_bits(
- component, RT5640_DUMMY2,
+ component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK |
RT5640_JD2_MASK,
RT5640_IRQ_JD2_NOR |
@@ -2843,7 +2843,7 @@ static int rt5640_resume(struct snd_soc_component *component)
} else {
if (rt5640->jd_src == RT5640_JD_SRC_JD2_IN4N)
snd_soc_component_update_bits(
- component, RT5640_DUMMY2,
+ component, RT5640_GCTL2,
RT5640_IRQ_JD2_MASK |
RT5640_JD2_P_MASK |
RT5640_JD2_MASK,
@@ -3014,6 +3014,11 @@ static int rt5640_i2c_probe(struct i2c_client *i2c)
regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
if (val != RT5640_DEVICE_ID) {
+ usleep_range(60000, 100000);
+ regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
+ }
+
+ if (val != RT5640_DEVICE_ID) {
dev_err(&i2c->dev,
"Device with ID register %#x is not rt5640/39\n", val);
return -ENODEV;
@@ -3026,7 +3031,7 @@ static int rt5640_i2c_probe(struct i2c_client *i2c)
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
- regmap_update_bits(rt5640->regmap, RT5640_DUMMY1,
+ regmap_update_bits(rt5640->regmap, RT5640_GCTL1,
RT5640_MCLK_DET, RT5640_MCLK_DET);
rt5640->hp_mute = true;
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 94b9a502f7f9..8a12cee76bdc 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -139,8 +139,8 @@
#define RT5640_SV_ZCD1 0xd9
#define RT5640_SV_ZCD2 0xda
/* Dummy Register */
-#define RT5640_DUMMY1 0xfa
-#define RT5640_DUMMY2 0xfb
+#define RT5640_GCTL1 0xfa
+#define RT5640_GCTL2 0xfb
#define RT5640_DUMMY3 0xfc
@@ -1986,7 +1986,7 @@
#define RT5640_M_MONO_ADC_R_SFT 12
#define RT5640_MCLK_DET (0x1 << 11)
-/* General Control 1 (0xfb) */
+/* General Control 2 (0xfb) */
#define RT5640_IRQ_JD2_MASK (0x1 << 12)
#define RT5640_IRQ_JD2_SFT 12
#define RT5640_IRQ_JD2_BP (0x0 << 12)
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index dba78efadc85..29a403526cd9 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -82,6 +82,7 @@ static const struct reg_sequence rt5650_init_list[] = {
{0xf6, 0x0100},
{RT5645_PWR_ANLG1, 0x02},
{RT5645_IL_CMD3, 0x6728},
+ {RT5645_PR_BASE + 0x3a, 0x0000},
};
static const struct reg_default rt5645_reg[] = {
@@ -3439,7 +3440,8 @@ static irqreturn_t rt5645_irq(int irq, void *data)
static void rt5645_btn_check_callback(struct timer_list *t)
{
- struct rt5645_priv *rt5645 = from_timer(rt5645, t, btn_check_timer);
+ struct rt5645_priv *rt5645 = timer_container_of(rt5645, t,
+ btn_check_timer);
queue_delayed_work(system_power_efficient_wq,
&rt5645->jack_detect_work, msecs_to_jiffies(5));
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index 82b92e83be4c..44c3a3b92f98 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -1315,14 +1315,17 @@ static int rt5660_i2c_probe(struct i2c_client *i2c)
regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
RT5660_GP1_PIN_MASK, RT5660_GP1_PIN_DMIC1_SCL);
- if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2)
+ if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2) {
regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
RT5660_SEL_DMIC_DATA_MASK,
RT5660_SEL_DMIC_DATA_GPIO2);
- else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P)
+ regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
+ RT5660_GP2_PIN_MASK, RT5660_GP2_PIN_DMIC1_SDA);
+ } else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P) {
regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
RT5660_SEL_DMIC_DATA_MASK,
RT5660_SEL_DMIC_DATA_IN1P);
+ }
}
return devm_snd_soc_register_component(&i2c->dev,
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index bcb6d7c6f301..b16b2c66e754 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -1022,102 +1022,6 @@ static int rt5665_mono_vol_put(struct snd_kcontrol *kcontrol,
return ret;
}
-/**
- * rt5665_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @component: SoC audio component device.
- * @filter_mask: mask of filters.
- * @clk_src: clock source
- *
- * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5665 can
- * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
- * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
- * ASRC function will track i2s clock and generate a corresponding system clock
- * for codec. This function provides an API to select the clock source for a
- * set of filters specified by the mask. And the codec driver will turn on ASRC
- * for these filters if ASRC is selected as their clock source.
- */
-int rt5665_sel_asrc_clk_src(struct snd_soc_component *component,
- unsigned int filter_mask, unsigned int clk_src)
-{
- unsigned int asrc2_mask = 0;
- unsigned int asrc2_value = 0;
- unsigned int asrc3_mask = 0;
- unsigned int asrc3_value = 0;
-
- switch (clk_src) {
- case RT5665_CLK_SEL_SYS:
- case RT5665_CLK_SEL_I2S1_ASRC:
- case RT5665_CLK_SEL_I2S2_ASRC:
- case RT5665_CLK_SEL_I2S3_ASRC:
- case RT5665_CLK_SEL_SYS2:
- case RT5665_CLK_SEL_SYS3:
- case RT5665_CLK_SEL_SYS4:
- break;
-
- default:
- return -EINVAL;
- }
-
- if (filter_mask & RT5665_DA_STEREO1_FILTER) {
- asrc2_mask |= RT5665_DA_STO1_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_STO1_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_STO1_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_DA_STEREO2_FILTER) {
- asrc2_mask |= RT5665_DA_STO2_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_STO2_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_STO2_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_DA_MONO_L_FILTER) {
- asrc2_mask |= RT5665_DA_MONOL_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_MONOL_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_MONOL_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_DA_MONO_R_FILTER) {
- asrc2_mask |= RT5665_DA_MONOR_CLK_SEL_MASK;
- asrc2_value = (asrc2_value & ~RT5665_DA_MONOR_CLK_SEL_MASK)
- | (clk_src << RT5665_DA_MONOR_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_STEREO1_FILTER) {
- asrc3_mask |= RT5665_AD_STO1_CLK_SEL_MASK;
- asrc3_value = (asrc2_value & ~RT5665_AD_STO1_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_STO1_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_STEREO2_FILTER) {
- asrc3_mask |= RT5665_AD_STO2_CLK_SEL_MASK;
- asrc3_value = (asrc2_value & ~RT5665_AD_STO2_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_STO2_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_MONO_L_FILTER) {
- asrc3_mask |= RT5665_AD_MONOL_CLK_SEL_MASK;
- asrc3_value = (asrc3_value & ~RT5665_AD_MONOL_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_MONOL_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5665_AD_MONO_R_FILTER) {
- asrc3_mask |= RT5665_AD_MONOR_CLK_SEL_MASK;
- asrc3_value = (asrc3_value & ~RT5665_AD_MONOR_CLK_SEL_MASK)
- | (clk_src << RT5665_AD_MONOR_CLK_SEL_SFT);
- }
-
- if (asrc2_mask)
- snd_soc_component_update_bits(component, RT5665_ASRC_2,
- asrc2_mask, asrc2_value);
-
- if (asrc3_mask)
- snd_soc_component_update_bits(component, RT5665_ASRC_3,
- asrc3_mask, asrc3_value);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(rt5665_sel_asrc_clk_src);
-
static int rt5665_button_detect(struct snd_soc_component *component)
{
int btn_type, val;
diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h
index 12ab28e5f10d..089e4078d37a 100644
--- a/sound/soc/codecs/rt5665.h
+++ b/sound/soc/codecs/rt5665.h
@@ -1999,7 +1999,4 @@ enum {
RT5665_CLK_SEL_SYS4,
};
-int rt5665_sel_asrc_clk_src(struct snd_soc_component *component,
- unsigned int filter_mask, unsigned int clk_src);
-
#endif /* __RT5665_H__ */
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index f626453f332b..8442dd09cfaf 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -799,49 +799,6 @@ static void rt5668_reset(struct regmap *regmap)
regmap_write(regmap, RT5668_RESET, 0);
regmap_write(regmap, RT5668_I2C_MODE, 1);
}
-/**
- * rt5668_sel_asrc_clk_src - select ASRC clock source for a set of filters
- * @component: SoC audio component device.
- * @filter_mask: mask of filters.
- * @clk_src: clock source
- *
- * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5668 can
- * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
- * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
- * ASRC function will track i2s clock and generate a corresponding system clock
- * for codec. This function provides an API to select the clock source for a
- * set of filters specified by the mask. And the component driver will turn on
- * ASRC for these filters if ASRC is selected as their clock source.
- */
-int rt5668_sel_asrc_clk_src(struct snd_soc_component *component,
- unsigned int filter_mask, unsigned int clk_src)
-{
-
- switch (clk_src) {
- case RT5668_CLK_SEL_SYS:
- case RT5668_CLK_SEL_I2S1_ASRC:
- case RT5668_CLK_SEL_I2S2_ASRC:
- break;
-
- default:
- return -EINVAL;
- }
-
- if (filter_mask & RT5668_DA_STEREO1_FILTER) {
- snd_soc_component_update_bits(component, RT5668_PLL_TRACK_2,
- RT5668_FILTER_CLK_SEL_MASK,
- clk_src << RT5668_FILTER_CLK_SEL_SFT);
- }
-
- if (filter_mask & RT5668_AD_STEREO1_FILTER) {
- snd_soc_component_update_bits(component, RT5668_PLL_TRACK_3,
- RT5668_FILTER_CLK_SEL_MASK,
- clk_src << RT5668_FILTER_CLK_SEL_SFT);
- }
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(rt5668_sel_asrc_clk_src);
static int rt5668_button_detect(struct snd_soc_component *component)
{
diff --git a/sound/soc/codecs/rt5668.h b/sound/soc/codecs/rt5668.h
index 6b851ddcc58a..b34a61d2109c 100644
--- a/sound/soc/codecs/rt5668.h
+++ b/sound/soc/codecs/rt5668.h
@@ -1309,7 +1309,4 @@ enum {
RT5668_CLK_SEL_I2S2_ASRC,
};
-int rt5668_sel_asrc_clk_src(struct snd_soc_component *component,
- unsigned int filter_mask, unsigned int clk_src);
-
#endif /* __RT5668_H__ */
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index abe0a5a95770..885edcf0a3a5 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -365,8 +365,8 @@ static void rt5677_spi_copy_work(struct work_struct *work)
new_bytes -= copy_bytes;
}
- delay = bytes_to_frames(runtime, period_bytes) / (runtime->rate / 1000);
- schedule_delayed_work(&rt5677_dsp->copy_work, msecs_to_jiffies(delay));
+ delay = bytes_to_frames(runtime, period_bytes) / runtime->rate;
+ schedule_delayed_work(&rt5677_dsp->copy_work, secs_to_jiffies(delay));
done:
mutex_unlock(&rt5677_dsp->dma_lock);
}
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 6e4774148566..69a0fb8d7f77 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -4725,13 +4725,14 @@ static int rt5677_update_gpio_bits(struct rt5677_priv *rt5677, unsigned offset,
}
#ifdef CONFIG_GPIOLIB
-static void rt5677_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int rt5677_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct rt5677_priv *rt5677 = gpiochip_get_data(chip);
int level = value ? RT5677_GPIOx_OUT_HI : RT5677_GPIOx_OUT_LO;
int m = RT5677_GPIOx_OUT_MASK;
- rt5677_update_gpio_bits(rt5677, offset, m, level);
+ return rt5677_update_gpio_bits(rt5677, offset, m, level);
}
static int rt5677_gpio_direction_out(struct gpio_chip *chip,
@@ -4834,7 +4835,7 @@ static const struct gpio_chip rt5677_template_chip = {
.label = RT5677_DRV_NAME,
.owner = THIS_MODULE,
.direction_output = rt5677_gpio_direction_out,
- .set = rt5677_gpio_set,
+ .set_rv = rt5677_gpio_set,
.direction_input = rt5677_gpio_direction_in,
.get = rt5677_gpio_get,
.to_irq = rt5677_to_irq,
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index aa229894129b..055bea0a4a3b 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -474,7 +474,6 @@ reinit:
rt5682->first_hw_init = true;
err_nodev:
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete: %d\n", __func__, ret);
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 7c88370e2dee..a0abd2ce0c1e 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -2675,8 +2675,8 @@ static unsigned long rt5682_wclk_recalc_rate(struct clk_hw *hw,
return rt5682->lrck[RT5682_AIF1];
}
-static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int rt5682_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
@@ -2689,13 +2689,13 @@ static long rt5682_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
* Only accept to set wclk rate to 44.1k or 48kHz.
* It will force to 48kHz if not both.
*/
- if (rate != CLK_48 && rate != CLK_44) {
+ if (req->rate != CLK_48 && req->rate != CLK_44) {
dev_warn(rt5682->i2c_dev, "%s: clk %s only support %d or %d Hz output\n",
__func__, clk_name, CLK_44, CLK_48);
- rate = CLK_48;
+ req->rate = CLK_48;
}
- return rate;
+ return 0;
}
static int rt5682_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -2795,15 +2795,15 @@ static unsigned long rt5682_bclk_get_factor(unsigned long rate,
return 256;
}
-static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int rt5682_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct rt5682_priv *rt5682 =
container_of(hw, struct rt5682_priv,
dai_clks_hw[RT5682_DAI_BCLK_IDX]);
unsigned long factor;
- if (!*parent_rate || !rt5682_clk_check(rt5682))
+ if (!req->best_parent_rate || !rt5682_clk_check(rt5682))
return -EINVAL;
/*
@@ -2813,9 +2813,11 @@ static long rt5682_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
* and find the appropriate multiplier of BCLK to
* get the rounded down BCLK value.
*/
- factor = rt5682_bclk_get_factor(rate, *parent_rate);
+ factor = rt5682_bclk_get_factor(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
- return *parent_rate * factor;
+ return 0;
}
static int rt5682_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -2849,12 +2851,12 @@ static const struct clk_ops rt5682_dai_clk_ops[RT5682_DAI_NUM_CLKS] = {
.prepare = rt5682_wclk_prepare,
.unprepare = rt5682_wclk_unprepare,
.recalc_rate = rt5682_wclk_recalc_rate,
- .round_rate = rt5682_wclk_round_rate,
+ .determine_rate = rt5682_wclk_determine_rate,
.set_rate = rt5682_wclk_set_rate,
},
[RT5682_DAI_BCLK_IDX] = {
.recalc_rate = rt5682_bclk_recalc_rate,
- .round_rate = rt5682_bclk_round_rate,
+ .determine_rate = rt5682_bclk_determine_rate,
.set_rate = rt5682_bclk_set_rate,
},
};
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index 73c4b3c31f8c..80b921695e7d 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -2610,8 +2610,8 @@ static unsigned long rt5682s_wclk_recalc_rate(struct clk_hw *hw,
return rt5682s->lrck[RT5682S_AIF1];
}
-static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int rt5682s_wclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct rt5682s_priv *rt5682s =
container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_WCLK_IDX]);
@@ -2624,13 +2624,13 @@ static long rt5682s_wclk_round_rate(struct clk_hw *hw, unsigned long rate,
* Only accept to set wclk rate to 44.1k or 48kHz.
* It will force to 48kHz if not both.
*/
- if (rate != CLK_48 && rate != CLK_44) {
+ if (req->rate != CLK_48 && req->rate != CLK_44) {
dev_warn(component->dev, "%s: clk %s only support %d or %d Hz output\n",
__func__, clk_name, CLK_44, CLK_48);
- rate = CLK_48;
+ req->rate = CLK_48;
}
- return rate;
+ return 0;
}
static int rt5682s_wclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -2719,14 +2719,14 @@ static unsigned long rt5682s_bclk_get_factor(unsigned long rate,
return 256;
}
-static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int rt5682s_bclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct rt5682s_priv *rt5682s =
container_of(hw, struct rt5682s_priv, dai_clks_hw[RT5682S_DAI_BCLK_IDX]);
unsigned long factor;
- if (!*parent_rate || !rt5682s_clk_check(rt5682s))
+ if (!req->best_parent_rate || !rt5682s_clk_check(rt5682s))
return -EINVAL;
/*
@@ -2736,9 +2736,11 @@ static long rt5682s_bclk_round_rate(struct clk_hw *hw, unsigned long rate,
* and find the appropriate multiplier of BCLK to
* get the rounded down BCLK value.
*/
- factor = rt5682s_bclk_get_factor(rate, *parent_rate);
+ factor = rt5682s_bclk_get_factor(req->rate, req->best_parent_rate);
+
+ req->rate = req->best_parent_rate * factor;
- return *parent_rate * factor;
+ return 0;
}
static int rt5682s_bclk_set_rate(struct clk_hw *hw, unsigned long rate,
@@ -2769,12 +2771,12 @@ static const struct clk_ops rt5682s_dai_clk_ops[RT5682S_DAI_NUM_CLKS] = {
.prepare = rt5682s_wclk_prepare,
.unprepare = rt5682s_wclk_unprepare,
.recalc_rate = rt5682s_wclk_recalc_rate,
- .round_rate = rt5682s_wclk_round_rate,
+ .determine_rate = rt5682s_wclk_determine_rate,
.set_rate = rt5682s_wclk_set_rate,
},
[RT5682S_DAI_BCLK_IDX] = {
.recalc_rate = rt5682s_bclk_recalc_rate,
- .round_rate = rt5682s_bclk_round_rate,
+ .determine_rate = rt5682s_bclk_determine_rate,
.set_rate = rt5682s_bclk_set_rate,
},
};
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
index 434b926f96c8..816117c13aea 100644
--- a/sound/soc/codecs/rt700.c
+++ b/sound/soc/codecs/rt700.c
@@ -338,7 +338,6 @@ static int rt700_set_jack_detect(struct snd_soc_component *component,
rt700_jack_init(rt700);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
@@ -862,7 +861,7 @@ static int rt700_set_bias_level(struct snd_soc_component *component,
default:
break;
}
- dapm->bias_level = level;
+
return 0;
}
@@ -1230,7 +1229,6 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt700->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
index dd6ccf17afd4..16c351779243 100644
--- a/sound/soc/codecs/rt711-sdca.c
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -545,7 +545,6 @@ static int rt711_sdca_set_jack_detect(struct snd_soc_component *component,
rt711_sdca_jack_init(rt711);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
@@ -1662,7 +1661,6 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt711->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
index 5446f9506a16..af3a49aee618 100644
--- a/sound/soc/codecs/rt711.c
+++ b/sound/soc/codecs/rt711.c
@@ -480,7 +480,6 @@ static int rt711_set_jack_detect(struct snd_soc_component *component,
rt711_jack_init(rt711);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
@@ -1331,7 +1330,6 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt711->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index db011da63bd9..42f8f7b8bed0 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -236,7 +236,6 @@ static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt712->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
@@ -263,12 +262,8 @@ static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
if (!adc_vol_flag) /* boost gain */
ctl = regvalue / 0x0a00;
- else { /* ADC gain */
- if (adc_vol_flag)
- ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
- else
- ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset);
- }
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
ucontrol->value.integer.value[i] = ctl;
}
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
index 19d99b9d4ab2..5b298db5f0f6 100644
--- a/sound/soc/codecs/rt712-sdca.c
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -479,7 +479,6 @@ static int rt712_sdca_set_jack_detect(struct snd_soc_component *component,
rt712_sdca_jack_init(rt712);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
@@ -1065,12 +1064,8 @@ static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
if (!adc_vol_flag) /* boost gain */
ctl = regvalue / 0x0a00;
- else { /* ADC gain */
- if (adc_vol_flag)
- ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
- else
- ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset);
- }
+ else /* ADC gain */
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
ucontrol->value.integer.value[i] = ctl;
}
@@ -1929,7 +1924,6 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
suspend:
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
return 0;
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
index 7fb02654c16b..db7d43349d7d 100644
--- a/sound/soc/codecs/rt715-sdca.c
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -1065,7 +1065,6 @@ int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt715->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
return 0;
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
index 2cf461852091..7e1628a5c9d1 100644
--- a/sound/soc/codecs/rt715.c
+++ b/sound/soc/codecs/rt715.c
@@ -775,7 +775,7 @@ static int rt715_set_bias_level(struct snd_soc_component *component,
default:
break;
}
- dapm->bias_level = level;
+
return 0;
}
@@ -1129,7 +1129,6 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt715->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
return 0;
diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c
index 1c9f32e405cf..f6f7c2ffde1c 100644
--- a/sound/soc/codecs/rt721-sdca.c
+++ b/sound/soc/codecs/rt721-sdca.c
@@ -327,7 +327,6 @@ static int rt721_sdca_set_jack_detect(struct snd_soc_component *component,
rt721_sdca_jack_init(rt721);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
@@ -430,6 +429,7 @@ static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0;
unsigned int adc_vol_flag = 0;
const unsigned int interval_offset = 0xc0;
+ const unsigned int tendA = 0x200;
const unsigned int tendB = 0xa00;
if (strstr(ucontrol->id.name, "FU1E Capture Volume") ||
@@ -439,9 +439,16 @@ static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
regmap_read(rt721->mbq_regmap, mc->reg, &read_l);
regmap_read(rt721->mbq_regmap, mc->rreg, &read_r);
- if (mc->shift == 8) /* boost gain */
+ if (mc->shift == 8) {
+ /* boost gain */
ctl_l = read_l / tendB;
- else {
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_l == 0x8000 || read_l == 0xfe00)
+ ctl_l = 0;
+ else
+ ctl_l = read_l / tendA + 1;
+ } else {
if (adc_vol_flag)
ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset);
else
@@ -449,9 +456,16 @@ static int rt721_sdca_set_gain_get(struct snd_kcontrol *kcontrol,
}
if (read_l != read_r) {
- if (mc->shift == 8) /* boost gain */
+ if (mc->shift == 8) {
+ /* boost gain */
ctl_r = read_r / tendB;
- else { /* ADC/DAC gain */
+ } else if (mc->shift == 1) {
+ /* FU33 boost gain */
+ if (read_r == 0x8000 || read_r == 0xfe00)
+ ctl_r = 0;
+ else
+ ctl_r = read_r / tendA + 1;
+ } else { /* ADC/DAC gain */
if (adc_vol_flag)
ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset);
else
@@ -1533,7 +1547,6 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt721->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
index 11e2e8f68a98..70700bdb80a1 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.c
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -24,6 +24,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
case 0x2f50:
case 0x2f54:
case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE,
0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
@@ -42,8 +43,12 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
RT722_SDCA_CTL_FU_MUTE, CH_R):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01,
RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11,
@@ -56,6 +61,9 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
RT722_SDCA_CTL_VENDOR_DEF, 0):
case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F,
RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
@@ -70,6 +78,9 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
RT722_SDCA_CTL_VENDOR_DEF, CH_08):
case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
RT722_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
+ RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
@@ -136,7 +147,7 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
}
}
-static struct regmap_sdw_mbq_cfg rt722_mbq_config = {
+static const struct regmap_sdw_mbq_cfg rt722_mbq_config = {
.mbq_size = rt722_sdca_mbq_size,
};
@@ -150,11 +161,18 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case 0x2f01:
case 0x2f54:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE,
0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER,
0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01,
RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
case 0x2000000:
case 0x200000d:
@@ -166,6 +184,8 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
case 0x2000084:
case 0x2000086:
case 0x3110000:
+ case 0x5800003:
+ case 0x5810000:
return true;
default:
return false;
diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h
index 80b014456940..c5dd472a2c00 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.h
+++ b/sound/soc/codecs/rt722-sdca-sdw.h
@@ -34,6 +34,7 @@ static const struct reg_default rt722_sdca_reg_defaults[] = {
{ 0x200003c, 0xc214 },
{ 0x2000046, 0x8004 },
{ 0x5810000, 0x702d },
+ { 0x6100000, 0x0201 },
{ 0x6100006, 0x0005 },
{ 0x6100010, 0x2630 },
{ 0x6100011, 0x152f },
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
index f093ce841b3f..333611490ae3 100644
--- a/sound/soc/codecs/rt722-sdca.c
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -339,7 +339,6 @@ static int rt722_sdca_set_jack_detect(struct snd_soc_component *component,
rt722_sdca_jack_init(rt722);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
@@ -842,6 +841,7 @@ static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMU:
rt722->fu1e_dapm_mute = false;
rt722_sdca_set_fu1e_capture_ctl(rt722);
+ usleep_range(150000, 160000);
break;
case SND_SOC_DAPM_PRE_PMD:
rt722->fu1e_dapm_mute = true;
@@ -871,6 +871,28 @@ static int rt722_sdca_fu36_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static void rt722_pde_transition_delay(struct rt722_sdca_priv *rt722, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int delay = 1000, val;
+
+ pm_runtime_mark_last_busy(&rt722->slave->dev);
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (delay) {
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(func, entity, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ break;
+
+ usleep_range(1000, 1500);
+ delay--;
+ }
+ if (!delay) {
+ dev_warn(&rt722->slave->dev, "%s PDE to %s is NOT ready", __func__, ps?"PS3":"PS0");
+ }
+}
+
static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -884,11 +906,13 @@ static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w,
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps0);
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, ps3);
break;
}
return 0;
@@ -907,11 +931,13 @@ static int rt722_sdca_pde23_event(struct snd_soc_dapm_widget *w,
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps0);
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, ps3);
break;
}
return 0;
@@ -930,11 +956,13 @@ static int rt722_sdca_pde11_event(struct snd_soc_dapm_widget *w,
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps0);
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, ps3);
break;
}
return 0;
@@ -953,11 +981,13 @@ static int rt722_sdca_pde12_event(struct snd_soc_dapm_widget *w,
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps0);
break;
case SND_SOC_DAPM_PRE_PMD:
regmap_write(rt722->regmap,
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12,
RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3);
+ rt722_pde_transition_delay(rt722, FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, ps3);
break;
}
return 0;
@@ -1278,6 +1308,8 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave
rt722->slave = slave;
rt722->regmap = regmap;
+ regcache_cache_only(rt722->regmap, true);
+
mutex_init(&rt722->calibrate_mutex);
mutex_init(&rt722->disable_irq_lock);
@@ -1302,140 +1334,183 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave
static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722)
{
- /* Set AD07 power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29);
- /* Set AD10 power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_ADC10_PDE_FLOAT_CTL, 0x2a00);
- /* Set DMIC1/DMIC2 power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a);
- /* Set DMIC2 IT entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_DMIC_ENT_FLOAT_CTL, 0x2626);
- /* Set AD10 FU entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_ADC_ENT_FLOAT_CTL, 0x1e00);
- /* Set DMIC2 FU entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
- /* Set AD10 FU channel floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304);
- /* Set DMIC2 FU channel floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
- /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
- RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000);
- /* Enable vf707_r12_05/vf707_r13_05 */
- regmap_write(rt722->regmap,
- SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
- RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01);
- /* Fine tune PDE2A latency */
- regmap_write(rt722->regmap, 0x2f5c, 0x25);
+ unsigned int mic_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &mic_func_status);
+ dev_dbg(dev, "%s mic func_status=0x%x\n", __func__, mic_func_status);
+
+ if ((mic_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Set AD07 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29);
+ /* Set AD10 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC10_PDE_FLOAT_CTL, 0x2a00);
+ /* Set DMIC1/DMIC2 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a);
+ /* Set DMIC2 IT entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_ENT_FLOAT_CTL, 0x2626);
+ /* Set AD10 FU entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC_ENT_FLOAT_CTL, 0x1e00);
+ /* Set DMIC2 FU entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
+ /* Set AD10 FU channel floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304);
+ /* Set DMIC2 FU channel floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
+ /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL,
+ RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000);
+ /* Enable vf707_r12_05/vf707_r13_05 */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26,
+ RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ /* Fine tune PDE2A latency */
+ regmap_write(rt722->regmap, 0x2f5c, 0x25);
+ /* PHYtiming TDZ/TZD control */
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
}
static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722)
{
- /* Set DVQ=01 */
- rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
- 0xc215);
- /* Reset dc_cal_top */
- rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
- 0x702c);
- /* W1C Trigger Calibration */
- rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
- 0xf02d);
- /* Set DAC02/ClassD power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL,
- 0x2323);
- /* Set EAPD high */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL,
- 0x0002);
- /* Enable vf707_r14 */
- regmap_write(rt722->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
- RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04);
+ unsigned int amp_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &amp_func_status);
+ dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
+
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Set DVQ=01 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
+ 0xc215);
+ /* Reset dc_cal_top */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
+ 0x702c);
+ /* W1C Trigger Calibration */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL,
+ 0xf02d);
+ /* Set DAC02/ClassD power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL,
+ 0x2323);
+ /* Set EAPD high */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL,
+ 0x0002);
+ /* Enable vf707_r14 */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23,
+ RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
}
static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722)
{
int loop_check, chk_cnt = 100, ret;
unsigned int calib_status = 0;
+ unsigned int jack_func_status;
+ struct device *dev = &rt722->slave->dev;
+
+ regmap_read(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0), &jack_func_status);
+ dev_dbg(dev, "%s jack func_status=0x%x\n", __func__, jack_func_status);
+
+ if ((jack_func_status & FUNCTION_NEEDS_INITIALIZATION) || (!rt722->first_hw_init)) {
+ /* Config analog bias */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3,
+ 0xa081);
+ /* GE related settings */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2,
+ 0xa009);
+ /* Button A, B, C, D bypass mode */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4,
+ 0xcf00);
+ /* HID1 slot enable */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5,
+ 0x000f);
+ /* Report ID for HID1 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0,
+ 0x1100);
+ /* OSC/OOC for slot 2, 3 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7,
+ 0x0c12);
+ /* Set JD de-bounce clock control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1,
+ 0x7002);
+ /* Set DVQ=01 */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
+ 0xc215);
+ /* FSM switch to calibration manual mode */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL,
+ 0x4100);
+ /* W1C Trigger DC calibration (HP) */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3,
+ 0x008d);
+ /* check HP calibration FSM status */
+ for (loop_check = 0; loop_check < chk_cnt; loop_check++) {
+ usleep_range(10000, 11000);
+ ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI,
+ RT722_DAC_DC_CALI_CTL3, &calib_status);
+ if (ret < 0)
+ dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret);
+ if ((calib_status & 0x0040) == 0x0)
+ break;
+ }
- /* Config analog bias */
- rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_ANALOG_BIAS_CTL3,
- 0xa081);
- /* GE related settings */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_GE_RELATED_CTL2,
- 0xa009);
- /* Button A, B, C, D bypass mode */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4,
- 0xcf00);
- /* HID1 slot enable */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5,
- 0x000f);
- /* Report ID for HID1 */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0,
- 0x1100);
- /* OSC/OOC for slot 2, 3 */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7,
- 0x0c12);
- /* Set JD de-bounce clock control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1,
- 0x7002);
- /* Set DVQ=01 */
- rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6,
- 0xc215);
- /* FSM switch to calibration manual mode */
- rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL,
- 0x4100);
- /* W1C Trigger DC calibration (HP) */
- rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3,
- 0x008d);
- /* check HP calibration FSM status */
- for (loop_check = 0; loop_check < chk_cnt; loop_check++) {
- usleep_range(10000, 11000);
- ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI,
- RT722_DAC_DC_CALI_CTL3, &calib_status);
- if (ret < 0)
- dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret);
- if ((calib_status & 0x0040) == 0x0)
- break;
+ if (loop_check == chk_cnt)
+ dev_dbg(&rt722->slave->dev, "%s, calibration time-out!\n", __func__);
+
+ /* Set ADC09 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL,
+ 0x2a12);
+ /* Set MIC2 and LINE1 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL,
+ 0x3429);
+ /* Set ET41h and LINE2 power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL,
+ 0x4112);
+ /* Set DAC03 and HP power entity floating control */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL,
+ 0x4040);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1,
+ 0x4141);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1,
+ 0x0101);
+ /* Fine tune PDE40 latency */
+ regmap_write(rt722->regmap, 0x2f58, 0x07);
+ regmap_write(rt722->regmap, 0x2f03, 0x06);
+ /* MIC VRefo */
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200);
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_VREFO_GAT, 0x4000, 0x4000);
+ /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */
+ rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4,
+ 0x0010);
+
+ /* clear flag */
+ regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT0, RT722_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
}
-
- if (loop_check == chk_cnt)
- dev_dbg(&rt722->slave->dev, "%s, calibration time-out!\n", __func__);
-
- /* Set ADC09 power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL,
- 0x2a12);
- /* Set MIC2 and LINE1 power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL,
- 0x3429);
- /* Set ET41h and LINE2 power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL,
- 0x4112);
- /* Set DAC03 and HP power entity floating control */
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL,
- 0x4040);
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ENT_FLOAT_CTRL_1,
- 0x4141);
- rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_FLOAT_CTRL_1,
- 0x0101);
- /* Fine tune PDE40 latency */
- regmap_write(rt722->regmap, 0x2f58, 0x07);
- regmap_write(rt722->regmap, 0x2f03, 0x06);
- /* MIC VRefo */
- rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
- RT722_COMBO_JACK_AUTO_CTL1, 0x0200, 0x0200);
- rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
- RT722_VREFO_GAT, 0x4000, 0x4000);
- /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */
- rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4,
- 0x0010);
}
int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
@@ -1447,8 +1522,8 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
if (rt722->hw_init)
return 0;
+ regcache_cache_only(rt722->regmap, false);
if (rt722->first_hw_init) {
- regcache_cache_only(rt722->regmap, false);
regcache_cache_bypass(rt722->regmap, true);
} else {
/*
@@ -1483,7 +1558,6 @@ int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave)
/* Mark Slave initialization complete */
rt722->hw_init = true;
- pm_runtime_mark_last_busy(&slave->dev);
pm_runtime_put_autosuspend(&slave->dev);
dev_dbg(&slave->dev, "%s hw_init complete\n", __func__);
diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h
index 04c3b4232ef3..3c383705dd3c 100644
--- a/sound/soc/codecs/rt722-sdca.h
+++ b/sound/soc/codecs/rt722-sdca.h
@@ -183,6 +183,7 @@ struct rt722_sdca_dmic_kctrl_priv {
#define RT722_SDCA_ENT_PLATFORM_FU44 0x44
#define RT722_SDCA_ENT_XU03 0x03
#define RT722_SDCA_ENT_XU0D 0x0d
+#define RT722_SDCA_ENT0 0x00
/* RT722 SDCA control */
#define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
@@ -197,6 +198,8 @@ struct rt722_sdca_dmic_kctrl_priv {
#define RT722_SDCA_CTL_REQ_POWER_STATE 0x01
#define RT722_SDCA_CTL_VENDOR_DEF 0x30
#define RT722_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT722_SDCA_CTL_FUNC_STATUS 0x10
+#define RT722_SDCA_CTL_ACTUAL_POWER_STATE 0x10
/* RT722 SDCA channel */
#define CH_L 0x01
@@ -215,6 +218,9 @@ struct rt722_sdca_dmic_kctrl_priv {
#define RT722_SDCA_RATE_96000HZ 0x0b
#define RT722_SDCA_RATE_192000HZ 0x0d
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
enum {
RT722_AIF1, /* For headset mic and headphone */
RT722_AIF2, /* For speaker */
diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c
new file mode 100644
index 000000000000..b162824526d6
--- /dev/null
+++ b/sound/soc/codecs/rt9123.c
@@ -0,0 +1,500 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123.c -- RT9123 (SW I2C Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define RT9123_REG_AMPCTRL 0x01
+#define RT9123_REG_I2SOPT 0x02
+#define RT9123_REG_TDMRX 0x03
+#define RT9123_REG_SILVOLEN 0x04
+#define RT9123_REG_VOLGAIN 0x12
+#define RT9123_REG_ANAFLAG 0x36
+#define RT9123_REG_COMBOID 0xF7
+
+#define RT9123_MASK_SWRST BIT(15)
+#define RT9123_MASK_SWMUTE BIT(14)
+#define RT9123_MASK_AMPON BIT(12)
+#define RT9123_MASK_AUDBIT GENMASK(14, 12)
+#define RT9123_MASK_AUDFMT GENMASK(11, 8)
+#define RT9123_MASK_TDMRXLOC GENMASK(4, 0)
+#define RT9123_MASK_VENID GENMASK(15, 4)
+
+#define RT9123_FIXED_VENID 0x340
+
+struct rt9123_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rt9123_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct device *dev = comp->dev;
+ unsigned int enable;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ enable = 1;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ enable = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /* AMPON bit is located in volatile RG, use pm_runtime to guarantee the RG access */
+ snd_soc_component_write_field(comp, RT9123_REG_AMPCTRL, RT9123_MASK_AMPON, enable);
+
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 0, TLV_DB_SCALE_ITEM(-1200, 0, 0),
+ 1, 9, TLV_DB_SCALE_ITEM(0, 150, 0),
+ 10, 10, TLV_DB_SCALE_ITEM(1400, 0, 0));
+static const char * const pwmfreq_text[] = { "300KHz", "325KHz", "350KHz", "375KHz" };
+static const struct soc_enum rt9123_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_AMPCTRL, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rt9123_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RT9123_REG_I2SOPT, 4, ARRAY_SIZE(i2sch_text), i2sch_text);
+
+static int rt9123_kcontrol_name_comp(struct snd_kcontrol *kcontrol, const char *s)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ const char *kctlname = kcontrol->id.name;
+
+ if (comp && comp->name_prefix)
+ kctlname += strlen(comp->name_prefix) + 1;
+
+ return strcmp(kctlname, s);
+}
+
+static int rt9123_xhandler_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG read
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to get control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int rt9123_xhandler_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol);
+ struct device *dev = comp->dev;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * Since the RG bitfield for 'Speaker Volume' and 'PWM Frequency Select' are located in
+ * volatile RG address, special handling here with pm runtime API to guarantee RG write
+ * operation.
+ */
+ if (rt9123_kcontrol_name_comp(kcontrol, "Speaker Volume") == 0)
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ else
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+
+ if (ret < 0)
+ dev_err(dev, "Failed to put control (%d)\n", ret);
+
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static const struct snd_kcontrol_new rt9123_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RT9123_REG_VOLGAIN, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_EXT_TLV("Speaker Volume", RT9123_REG_AMPCTRL, 0, 10, 0, rt9123_xhandler_get,
+ rt9123_xhandler_put, ana_tlv),
+ SOC_ENUM_EXT("PWM Frequency Select", rt9123_pwm_freq_enum, rt9123_xhandler_get,
+ rt9123_xhandler_put),
+ SOC_ENUM("I2S CH Select", rt9123_i2sch_select_enum),
+ SOC_SINGLE("Silence Detect Switch", RT9123_REG_SILVOLEN, 14, 1, 0),
+};
+
+static const struct snd_soc_component_driver rt9123_comp_driver = {
+ .controls = rt9123_controls,
+ .num_controls = ARRAY_SIZE(rt9123_controls),
+ .dapm_widgets = rt9123_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123_dapm_widgets),
+ .dapm_routes = rt9123_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rt9123_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+
+ rt9123->dai_fmt = fmt;
+ return 0;
+}
+
+static int rt9123_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int rx_loc;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slot_width <= 0 || slots % 2 || slot_width % 8 ||
+ slots * slot_width > 256) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || ffs(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure rx channel data location */
+ rx_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write_field(comp, RT9123_REG_TDMRX, RT9123_MASK_TDMRXLOC, rx_loc);
+
+ rt9123->tdm_slots = slots;
+ rt9123->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rt9123_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rt9123_priv *rt9123 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rt9123->dai_fmt);
+ if (rt9123->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A && fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rt9123->tdm_slots ? 4 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rt9123->tdm_slots ? 12 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ audbit = 2;
+ break;
+ case 32:
+ audbit = 3;
+ break;
+ case 8:
+ audbit = 4;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ slot_width = params_physical_width(param);
+ if (rt9123->tdm_slots && slot_width > rt9123->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDFMT, audfmt);
+ snd_soc_component_write_field(comp, RT9123_REG_I2SOPT, RT9123_MASK_AUDBIT, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt9123_dai_ops = {
+ .set_fmt = rt9123_dai_set_format,
+ .set_tdm_slot = rt9123_dai_set_tdm_slot,
+ .hw_params = rt9123_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rt9123_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rt9123_dai_ops,
+};
+
+static bool rt9123_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01 ... 0x05:
+ case 0x12 ... 0x13:
+ case 0x20 ... 0x21:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt9123_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x01:
+ case 0x20:
+ case 0x36:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt9123_regmap_config = {
+ .name = "rt9123",
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .readable_reg = rt9123_readable_reg,
+ .writeable_reg = rt9123_writeable_reg,
+ .volatile_reg = rt9123_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = RT9123_REG_ANAFLAG + 1,
+};
+
+static int rt9123_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rt9123_priv *rt9123;
+ struct regmap *regmap;
+ __be16 value;
+ u16 venid;
+ int ret;
+
+ rt9123 = devm_kzalloc(dev, sizeof(*rt9123), GFP_KERNEL);
+ if (!rt9123)
+ return -ENOMEM;
+
+ rt9123->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rt9123->enable))
+ return PTR_ERR(rt9123->enable);
+ else if (rt9123->enable)
+ usleep_range(250, 350);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_i2c_block_data(i2c, RT9123_REG_COMBOID, sizeof(value), (u8 *)&value);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read vendor-id\n");
+
+ venid = be16_to_cpu(value);
+ if ((venid & RT9123_MASK_VENID) != RT9123_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect vendor-id 0x%04x\n", venid);
+
+ /* Trigger RG reset before regmap init cache */
+ value = cpu_to_be16(RT9123_MASK_SWRST);
+ ret = i2c_smbus_write_i2c_block_data(i2c, RT9123_REG_AMPCTRL, sizeof(value), (u8 *)&value);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init_i2c(i2c, &rt9123_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ i2c_set_clientdata(i2c, rt9123);
+
+ pm_runtime_set_autosuspend_delay(dev, 500);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rt9123_comp_driver, &rt9123_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rt9123_runtime_suspend(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rt9123->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rt9123->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rt9123_runtime_resume(struct device *dev)
+{
+ struct rt9123_priv *rt9123 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rt9123->enable) {
+ gpiod_set_value(rt9123->enable, 1);
+ usleep_range(250, 350);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rt9123_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rt9123_runtime_suspend, rt9123_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123_device_id[] = {
+ { .compatible = "richtek,rt9123" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123_acpi_match[] = {
+ { "RT9123", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123_acpi_match);
+#endif
+
+static struct i2c_driver rt9123_i2c_driver = {
+ .driver = {
+ .name = "rt9123",
+ .of_match_table = of_match_ptr(rt9123_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123_acpi_match),
+ .pm = pm_ptr(&rt9123_dev_pm_ops),
+ },
+ .probe = rt9123_i2c_probe,
+};
+module_i2c_driver(rt9123_i2c_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt9123p.c b/sound/soc/codecs/rt9123p.c
new file mode 100644
index 000000000000..d509659e735b
--- /dev/null
+++ b/sound/soc/codecs/rt9123p.c
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt9123p.c -- RT9123 (HW Mode) ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/acpi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+struct rt9123p_priv {
+ struct gpio_desc *enable;
+ unsigned int enable_delay;
+ int enable_switch;
+};
+
+static int rt9123p_daiops_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *comp = dai->component;
+ struct rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (!rt9123p->enable)
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ mdelay(rt9123p->enable_delay);
+ if (rt9123p->enable_switch) {
+ gpiod_set_value(rt9123p->enable, 1);
+ dev_dbg(comp->dev, "set enable to 1");
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ gpiod_set_value(rt9123p->enable, 0);
+ dev_dbg(comp->dev, "set enable to 0");
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt9123p_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ struct rt9123p_priv *rt9123p = snd_soc_component_get_drvdata(comp);
+
+ if (event & SND_SOC_DAPM_POST_PMU)
+ rt9123p->enable_switch = 1;
+ else if (event & SND_SOC_DAPM_POST_PMD)
+ rt9123p->enable_switch = 0;
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt9123p_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rt9123p_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rt9123p_dapm_routes[] = {
+ {"Amp Drv", NULL, "HiFi Playback"},
+ {"SPK", NULL, "Amp Drv"},
+};
+
+static const struct snd_soc_component_driver rt9123p_comp_driver = {
+ .dapm_widgets = rt9123p_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt9123p_dapm_widgets),
+ .dapm_routes = rt9123p_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt9123p_dapm_routes),
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops rt9123p_dai_ops = {
+ .trigger = rt9123p_daiops_trigger,
+};
+
+static struct snd_soc_dai_driver rt9123p_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |
+ SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+ SNDRV_PCM_RATE_96000,
+ .rate_min = 8000,
+ .rate_max = 96000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rt9123p_dai_ops,
+};
+
+static int rt9123p_platform_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rt9123p_priv *rt9123p;
+ int ret;
+
+ rt9123p = devm_kzalloc(dev, sizeof(*rt9123p), GFP_KERNEL);
+ if (!rt9123p)
+ return -ENOMEM;
+
+ rt9123p->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(rt9123p->enable))
+ return PTR_ERR(rt9123p->enable);
+
+ ret = device_property_read_u32(dev, "enable-delay-ms", &rt9123p->enable_delay);
+ if (ret) {
+ rt9123p->enable_delay = 0;
+ dev_dbg(dev, "no optional property 'enable-delay-ms' found, default: no delay\n");
+ }
+
+ platform_set_drvdata(pdev, rt9123p);
+
+ return devm_snd_soc_register_component(dev, &rt9123p_comp_driver, &rt9123p_dai_driver, 1);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id rt9123p_device_id[] = {
+ { .compatible = "richtek,rt9123p" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rt9123p_device_id);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id rt9123p_acpi_match[] = {
+ { "RT9123P", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, rt9123p_acpi_match);
+#endif
+
+static struct platform_driver rt9123p_platform_driver = {
+ .driver = {
+ .name = "rt9123p",
+ .of_match_table = of_match_ptr(rt9123p_device_id),
+ .acpi_match_table = ACPI_PTR(rt9123p_acpi_match),
+ },
+ .probe = rt9123p_platform_probe,
+};
+module_platform_driver(rt9123p_platform_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC rt9123p Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c
new file mode 100644
index 000000000000..186904b31434
--- /dev/null
+++ b/sound/soc/codecs/rtq9124.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rtq9124.c -- RTQ9124 ALSA SoC Codec driver
+//
+// Author: ChiYuan Huang <cy_huang@richtek.com>
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/byteorder/generic.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#define RTQ9124_REG_SDI_SEL 0x00
+#define RTQ9124_REG_SDO_SEL 0x01
+#define RTQ9124_REG_I2S_OPT 0x02
+#define RTQ9124_REG_AMP_OPT 0x03
+#define RTQ9124_REG_STATE_CTRL 0x04
+#define RTQ9124_REG_PWM_PHASE 0x05
+#define RTQ9124_REG_SIL_CTRL 0x06
+#define RTQ9124_REG_PWM_SS_OPT 0x07
+#define RTQ9124_REG_ERR_INT_0 0x10
+#define RTQ9124_REG_ERR_MASK6 0x26
+#define RTQ9124_REG_TDM_TX_CH0 0x32
+#define RTQ9124_REG_TDM_RX_CH0 0x34
+#define RTQ9124_REG_VOL_OPT 0x38
+#define RTQ9124_REG_DCR_TH 0x4B
+#define RTQ9124_REG_ERR_TH 0x4C
+#define RTQ9124_REG_PROT_EN 0x5B
+#define RTQ9124_REG_PRJ_CODE 0xF9
+
+#define RTQ9124_MASK_CS_DATA_INV BIT(9)
+#define RTQ9124_MASK_VDDIO_SDO_SEL BIT(8)
+#define RTQ9124_MASK_AUD_BITS GENMASK(5, 4)
+#define RTQ9124_MASK_AUD_FMT GENMASK(3, 0)
+#define RTQ9124_MASK_CH_STATE GENMASK(1, 0)
+#define RTQ9124_MASK_SF_RESET BIT(15)
+
+#define RTQ9124_FIXED_VENID 0x9124
+
+struct rtq9124_priv {
+ struct gpio_desc *enable;
+ unsigned int dai_fmt;
+ int tdm_slots;
+ int tdm_slot_width;
+};
+
+static int rtq9124_enable_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_component *comp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int i, chan_state;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Change state to normal */
+ chan_state = 0;
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ /* Change state to HiZ */
+ chan_state = 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Before amp turn on, clear old events first */
+ for (i = 0; !chan_state && i < 8; i++)
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_INT_0 + i, 0xffff);
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_CH_STATE,
+ chan_state);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rtq9124_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_OUT_DRV_E("Amp Drv", SND_SOC_NOPM, 0, 0, NULL, 0, rtq9124_enable_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route rtq9124_dapm_routes[] = {
+ { "Amp Drv", NULL, "HiFi Playback" },
+ { "SPK", NULL, "Amp Drv" },
+};
+
+static const DECLARE_TLV_DB_SCALE(dig_tlv, -10375, 25, 0);
+static const DECLARE_TLV_DB_RANGE(ana_tlv,
+ 0, 3, TLV_DB_SCALE_ITEM(-600, 600, 0),
+ 4, 6, TLV_DB_SCALE_ITEM(1400, 200, 0));
+static const char * const i2sch_text[] = { "(L+R)/2", "LCH", "RCH", "(L+R)/2" };
+static const struct soc_enum rtq9124_i2sch_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDI_SEL, 0, ARRAY_SIZE(i2sch_text), i2sch_text);
+static const char * const sdo_vsel_text[] = { "1.8V", "3.3V" };
+static const struct soc_enum rtq9124_sdo_vselect_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_SDO_SEL, 8, ARRAY_SIZE(sdo_vsel_text), sdo_vsel_text);
+static const char * const pwmfreq_text[] = { "8*fs", "10*fs", "40*fs", "44*fs", "48*fs" };
+static const struct soc_enum rtq9124_pwm_freq_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_AMP_OPT, 4, ARRAY_SIZE(pwmfreq_text), pwmfreq_text);
+static const char * const out_angle_text[] = { "0", "45", "90", "135", "180", "225", "270", "315" };
+static const struct soc_enum rtq9124_out_angle_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_PWM_PHASE, 0, ARRAY_SIZE(out_angle_text), out_angle_text);
+static const char * const sdo_select_text[] = {
+ "None", "I2S DataI", "Interface", "DSP", "DF", "ISense", "ACLoad Cos", "ACLoad Sin",
+ "DCR",
+};
+static const struct soc_enum rtq9124_sdo_select_enum =
+ SOC_ENUM_DOUBLE(RTQ9124_REG_SDO_SEL, 4, 0, ARRAY_SIZE(sdo_select_text), sdo_select_text);
+static const char * const ulqm_dcvt_text[] = { "Disable", "DC", "VT", "DC+VT" };
+static const struct soc_enum rtq9124_ulqm_dcvt_select_enum =
+ SOC_ENUM_SINGLE(RTQ9124_REG_STATE_CTRL, 10, ARRAY_SIZE(ulqm_dcvt_text), ulqm_dcvt_text);
+
+static const struct snd_kcontrol_new rtq9124_controls[] = {
+ SOC_SINGLE_TLV("Master Volume", RTQ9124_REG_VOL_OPT, 2, 511, 1, dig_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", RTQ9124_REG_AMP_OPT, 0, 6, 0, ana_tlv),
+ SOC_ENUM("I2S CH Select", rtq9124_i2sch_select_enum),
+ SOC_ENUM("SDO VDDIO Select", rtq9124_sdo_vselect_enum),
+ SOC_ENUM("PWM Frequency Select", rtq9124_pwm_freq_enum),
+ SOC_ENUM("PWM Output Phase Select", rtq9124_out_angle_enum),
+ SOC_ENUM("SDO Select", rtq9124_sdo_select_enum),
+ SOC_ENUM("ULQM DCVT Select", rtq9124_ulqm_dcvt_select_enum),
+ SOC_SINGLE("Silence Detect Enable Switch", RTQ9124_REG_SIL_CTRL, 7, 1, 0),
+ SOC_SINGLE("Spread Spectrum Enable Switch", RTQ9124_REG_PWM_SS_OPT, 7, 1, 0),
+};
+
+static int rtq9124_comp_probe(struct snd_soc_component *comp)
+{
+ /* CS Data INV */
+ snd_soc_component_write_field(comp, RTQ9124_REG_SDO_SEL, RTQ9124_MASK_CS_DATA_INV, 1);
+
+ /* RTLD */
+ snd_soc_component_write(comp, RTQ9124_REG_DCR_TH, 0x5e30);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_TH, 0x3ff);
+ snd_soc_component_write(comp, RTQ9124_REG_PROT_EN, 0x3fc);
+ snd_soc_component_write(comp, RTQ9124_REG_ERR_MASK6, 0);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver rtq9124_comp_driver = {
+ .probe = rtq9124_comp_probe,
+ .controls = rtq9124_controls,
+ .num_controls = ARRAY_SIZE(rtq9124_controls),
+ .dapm_widgets = rtq9124_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rtq9124_dapm_widgets),
+ .dapm_routes = rtq9124_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rtq9124_dapm_routes),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int rtq9124_dai_set_format(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+
+ rtq9124->dai_fmt = fmt;
+ return 0;
+}
+
+static int rtq9124_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ struct device *dev = dai->dev;
+ unsigned int byte_loc, i;
+
+ dev_dbg(dev, "(slots, slot_width) = (%d, %d), (txmask, rxmask) = 0x%x, 0x%x\n", slots,
+ slot_width, tx_mask, rx_mask);
+
+ if (slots <= 0 || slots > 16 || slot_width <= 0 || slots % 2 || slot_width % 8) {
+ dev_err(dev, "Invalid slot parameter (%d, %d)\n", slots, slot_width);
+ return -EINVAL;
+ }
+
+ if (tx_mask && (hweight_long(tx_mask) > 2 || fls(tx_mask) > slots)) {
+ dev_err(dev, "Invalid tx_mask 0x%08x, slots = %d\n", tx_mask, slots);
+ return -EINVAL;
+ }
+
+ if (!rx_mask || hweight_long(rx_mask) > 1 || fls(rx_mask) > slots) {
+ dev_err(dev, "Invalid rx_mask 0x%08x, slots = %d\n", rx_mask, slots);
+ return -EINVAL;
+ }
+
+ /* Configure tx channel data location */
+ for (i = 0; tx_mask; i++, tx_mask ^= BIT(ffs(tx_mask) - 1)) {
+ byte_loc = (ffs(tx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_TX_CH0 + i, byte_loc);
+ }
+
+ /* Configure rx channel data location */
+ byte_loc = (ffs(rx_mask) - 1) * slot_width / 8;
+ snd_soc_component_write(comp, RTQ9124_REG_TDM_RX_CH0, byte_loc);
+
+ rtq9124->tdm_slots = slots;
+ rtq9124->tdm_slot_width = slot_width;
+
+ return 0;
+}
+
+static int rtq9124_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *param, struct snd_soc_dai *dai)
+{
+ struct rtq9124_priv *rtq9124 = snd_soc_dai_get_drvdata(dai);
+ struct snd_soc_component *comp = dai->component;
+ unsigned int fmtval, width, slot_width, bitrate;
+ struct device *dev = dai->dev;
+ unsigned int audfmt, audbit;
+
+ fmtval = FIELD_GET(SND_SOC_DAIFMT_FORMAT_MASK, rtq9124->dai_fmt);
+ if (rtq9124->tdm_slots && fmtval != SND_SOC_DAIFMT_DSP_A &&
+ fmtval != SND_SOC_DAIFMT_DSP_B) {
+ dev_err(dev, "TDM only can support DSP_A or DSP_B format\n");
+ return -EINVAL;
+ }
+
+ switch (fmtval) {
+ case SND_SOC_DAIFMT_I2S:
+ audfmt = 0;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ audfmt = 1;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ audfmt = 2;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ audfmt = rtq9124->tdm_slots ? 7 : 3;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ audfmt = rtq9124->tdm_slots ? 15 : 11;
+ break;
+ default:
+ dev_err(dev, "Unsupported format %d\n", fmtval);
+ return -EINVAL;
+ }
+
+ switch (width = params_width(param)) {
+ case 16:
+ audbit = 0;
+ break;
+ case 20:
+ audbit = 1;
+ break;
+ case 24:
+ case 32:
+ audbit = 3;
+ break;
+ default:
+ dev_err(dev, "Unsupported width %d\n", width);
+ return -EINVAL;
+ }
+
+ if (rtq9124->tdm_slots) {
+ slot_width = params_physical_width(param);
+ if (slot_width > rtq9124->tdm_slot_width) {
+ dev_err(dev, "Slot width is larger than TDM slot width\n");
+ return -EINVAL;
+ }
+
+ bitrate = rtq9124->tdm_slots * rtq9124->tdm_slot_width * params_rate(param);
+ if (bitrate > 24576000) {
+ dev_err(dev, "Bitrate exceed the internal PLL 24.576MHz (%d)\n", bitrate);
+ return -EINVAL;
+ }
+ }
+
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_FMT, audfmt);
+ snd_soc_component_write_field(comp, RTQ9124_REG_I2S_OPT, RTQ9124_MASK_AUD_BITS, audbit);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rtq9124_dai_ops = {
+ .set_fmt = rtq9124_dai_set_format,
+ .set_tdm_slot = rtq9124_dai_set_tdm_slot,
+ .hw_params = rtq9124_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver rtq9124_dai_driver = {
+ .name = "HiFi",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .formats = SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S24 |
+ SNDRV_PCM_FMTBIT_S32,
+ .rates = SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_24000 |
+ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &rtq9124_dai_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+};
+
+static bool rtq9124_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30 ... 0x3D:
+ case 0x40 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x00 ... 0x09:
+ case 0x0C ... 0x0E:
+ case 0x10 ... 0x17:
+ case 0x20 ... 0x27:
+ case 0x30:
+ case 0x32 ... 0x3D:
+ case 0x40 ... 0x4E:
+ case 0x50 ... 0x68:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq9124_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x0A ... 0x0B:
+ case 0x0F ... 0x17:
+ case 0x31:
+ case 0x4F:
+ case 0x51:
+ case 0x53 ... 0x57:
+ case 0x80 ... 0xBC:
+ case 0xC0 ... 0xDE:
+ case 0xE0 ... 0xE7:
+ case 0xF0 ... 0xFD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static inline u8 rtq9124_get_reg_len(unsigned int reg)
+{
+ return (reg >= 0x40 && reg <= 0x47) ? 4 : 2;
+}
+
+static int rtq9124_regmap_read(void *context, const void *reg_buf, size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)reg_buf;
+ u8 size = rtq9124_get_reg_len(reg);
+ u32 *val = val_buf;
+ int ret;
+
+ ret = i2c_smbus_read_i2c_block_data(i2c, reg, size, val_buf);
+ if (ret < 0)
+ return ret;
+ else if (ret != size)
+ return -EIO;
+
+ *val = size == 4 ? be32_to_cpup(val_buf) : be16_to_cpup(val_buf);
+
+ return 0;
+}
+
+static int rtq9124_regmap_write(void *context, const void *data, size_t count)
+{
+ struct i2c_client *i2c = context;
+ u8 reg = *(u8 *)data, *vbuf;
+ u8 size = rtq9124_get_reg_len(reg);
+ __be16 val16 = cpu_to_be16p(data + 1);
+ __be32 val32 = cpu_to_be32p(data + 1);
+
+ vbuf = size == 4 ? (u8 *)&val32 : (u8 *)&val16;
+ return i2c_smbus_write_i2c_block_data(i2c, reg, size, vbuf);
+}
+
+static const struct regmap_config rtq9124_regmap_config = {
+ .name = "rtq9124",
+ .reg_bits = 8,
+ .val_bits = 32,
+ .read = rtq9124_regmap_read,
+ .write = rtq9124_regmap_write,
+ .readable_reg = rtq9124_readable_reg,
+ .writeable_reg = rtq9124_writeable_reg,
+ .volatile_reg = rtq9124_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+ .num_reg_defaults_raw = 0xFD + 1,
+ .use_single_read = 1,
+ .use_single_write = 1,
+};
+
+static const struct reg_sequence rtq9124_init_regs[] = {
+ { 0xfb, 0x0065 },
+ { 0x93, 0x2000 },
+ { 0xfb, 0x0000 },
+};
+
+static int rtq9124_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct rtq9124_priv *rtq9124;
+ struct regmap *regmap;
+ int ret;
+
+ rtq9124 = devm_kzalloc(dev, sizeof(*rtq9124), GFP_KERNEL);
+ if (!rtq9124)
+ return -ENOMEM;
+
+ rtq9124->enable = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
+ if (IS_ERR(rtq9124->enable))
+ return PTR_ERR(rtq9124->enable);
+ else if (rtq9124->enable)
+ usleep_range(6000, 7000);
+ else
+ dev_dbg(dev, "No 'enable' GPIO specified, treat it as default on\n");
+
+ /* Check vendor id information */
+ ret = i2c_smbus_read_word_swapped(i2c, RTQ9124_REG_PRJ_CODE);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to read project code\n");
+ else if (ret != RTQ9124_FIXED_VENID)
+ return dev_err_probe(dev, -ENODEV, "Incorrect project-code 0x%04x\n", ret);
+
+ /* Trigger RG reset before regmap init */
+ ret = i2c_smbus_write_word_swapped(i2c, RTQ9124_REG_STATE_CTRL, RTQ9124_MASK_SF_RESET);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to trigger RG reset\n");
+
+ /* Need to wait 10ms for the reset to complete */
+ usleep_range(10000, 11000);
+
+ regmap = devm_regmap_init(dev, NULL, i2c, &rtq9124_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap), "Failed to init regmap\n");
+
+ ret = regmap_register_patch(regmap, rtq9124_init_regs, ARRAY_SIZE(rtq9124_init_regs));
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register regmap patch\n");
+
+ i2c_set_clientdata(i2c, rtq9124);
+
+ pm_runtime_set_autosuspend_delay(dev, 1000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm runtime\n");
+
+ return devm_snd_soc_register_component(dev, &rtq9124_comp_driver, &rtq9124_dai_driver, 1);
+}
+
+#ifdef CONFIG_PM
+static int rtq9124_runtime_suspend(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+
+ if (rtq9124->enable) {
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ gpiod_set_value(rtq9124->enable, 0);
+ }
+
+ return 0;
+}
+
+static int rtq9124_runtime_resume(struct device *dev)
+{
+ struct rtq9124_priv *rtq9124 = dev_get_drvdata(dev);
+ struct regmap *regmap = dev_get_regmap(dev, NULL);
+ int ret;
+
+ if (rtq9124->enable) {
+ gpiod_set_value(rtq9124->enable, 1);
+ usleep_range(6000, 7000);
+
+ regcache_cache_only(regmap, false);
+ ret = regcache_sync(regmap);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops rtq9124_dev_pm_ops = {
+ SET_RUNTIME_PM_OPS(rtq9124_runtime_suspend, rtq9124_runtime_resume, NULL)
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id rtq9124_device_id[] = {
+ { .compatible = "richtek,rtq9124" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq9124_device_id);
+#endif
+
+static struct i2c_driver rtq9124_driver = {
+ .driver = {
+ .name = "rtq9124",
+ .of_match_table = of_match_ptr(rtq9124_device_id),
+ .pm = pm_ptr(&rtq9124_dev_pm_ops),
+ },
+ .probe = rtq9124_probe,
+};
+module_i2c_driver(rtq9124_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("ASoC RTQ9124 Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c
index 498189ab691c..b3d401ada176 100644
--- a/sound/soc/codecs/sma1307.c
+++ b/sound/soc/codecs/sma1307.c
@@ -8,7 +8,6 @@
#include <linux/firmware.h>
#include <linux/i2c.h>
-#include <linux/of_gpio.h>
#include <linux/regmap.h>
#include <sound/pcm_params.h>
#include <sound/tlv.h>
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index bd8848ea1ec2..24d4b643917d 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -983,8 +983,7 @@ static int sta32x_probe(struct snd_soc_component *component)
err_regulator_bulk_disable:
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
err_clk_disable_unprepare:
- if (sta32x->xti_clk)
- clk_disable_unprepare(sta32x->xti_clk);
+ clk_disable_unprepare(sta32x->xti_clk);
return ret;
}
@@ -995,8 +994,7 @@ static void sta32x_remove(struct snd_soc_component *component)
sta32x_watchdog_stop(sta32x);
regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
- if (sta32x->xti_clk)
- clk_disable_unprepare(sta32x->xti_clk);
+ clk_disable_unprepare(sta32x->xti_clk);
}
static const struct snd_soc_component_driver sta32x_component = {
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index b56dd279d90a..43449d7c2584 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -724,7 +724,6 @@ static int tas2552_probe(struct i2c_client *client)
pm_runtime_set_autosuspend_delay(&client->dev, 1000);
pm_runtime_use_autosuspend(&client->dev);
pm_runtime_enable(&client->dev);
- pm_runtime_mark_last_busy(&client->dev);
pm_runtime_put_sync_autosuspend(&client->dev);
dev_set_drvdata(&client->dev, data);
diff --git a/sound/soc/codecs/tas2764-quirks.h b/sound/soc/codecs/tas2764-quirks.h
new file mode 100644
index 000000000000..7a62b3ba5b40
--- /dev/null
+++ b/sound/soc/codecs/tas2764-quirks.h
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef __TAS2764_QUIRKS__
+#define __TAS2764_QUIRKS__
+
+#include <linux/regmap.h>
+
+#include "tas2764.h"
+
+/* Bitmask of enabled Apple quirks */
+#define ENABLED_APPLE_QUIRKS 0x3f
+
+/*
+ * Disable noise gate and flip down reserved bit in NS_CFG0
+ */
+#define TAS2764_NOISE_GATE_DISABLE BIT(0)
+
+static const struct reg_sequence tas2764_noise_gate_dis_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x35), 0xb0)
+};
+
+/*
+ * CONV_VBAT_PVDD_MODE=1
+ */
+#define TAS2764_CONV_VBAT_PVDD_MODE BIT(1)
+
+static const struct reg_sequence tas2764_conv_vbat_pvdd_mode_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x6b), 0x41)
+};
+
+/*
+ * Reset of DAC modulator when DSP is OFF
+ */
+#define TAS2764_DMOD_RST BIT(2)
+
+static const struct reg_sequence tas2764_dmod_rst_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x0, 0x76), 0x0)
+};
+
+/*
+ * Unknown 0x133/0x137 writes (maybe TDM related)
+ */
+#define TAS2764_UNK_SEQ0 BIT(3)
+
+static const struct reg_sequence tas2764_unk_seq0[] = {
+ REG_SEQ0(TAS2764_REG(0x1, 0x33), 0x80),
+ REG_SEQ0(TAS2764_REG(0x1, 0x37), 0x3a),
+};
+
+/*
+ * Unknown 0x614 - 0x61f writes
+ */
+#define TAS2764_APPLE_UNK_SEQ1 BIT(4)
+
+static const struct reg_sequence tas2764_unk_seq1[] = {
+ REG_SEQ0(TAS2764_REG(0x6, 0x14), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x15), 0x13),
+ REG_SEQ0(TAS2764_REG(0x6, 0x16), 0x52),
+ REG_SEQ0(TAS2764_REG(0x6, 0x17), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x18), 0xe4),
+ REG_SEQ0(TAS2764_REG(0x6, 0x19), 0xc),
+ REG_SEQ0(TAS2764_REG(0x6, 0x16), 0xaa),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1b), 0x0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1c), 0x12),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1d), 0xa0),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1e), 0xd8),
+ REG_SEQ0(TAS2764_REG(0x6, 0x1f), 0x0),
+};
+
+/*
+ * Unknown writes in the 0xfd page (with secondary paging inside)
+ */
+#define TAS2764_APPLE_UNK_SEQ2 BIT(5)
+
+static const struct reg_sequence tas2764_unk_seq2[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x6c), 0x2),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x6d), 0xf),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
+};
+
+/*
+ * Disable 'Thermal Threshold 1'
+ */
+#define TAS2764_THERMAL_TH1_DISABLE BIT(6)
+
+static const struct reg_sequence tas2764_thermal_th1_dis_seq[] = {
+ REG_SEQ0(TAS2764_REG(0x1, 0x47), 0x2),
+};
+
+/*
+ * Imitate Apple's shutdown dance
+ */
+#define TAS2764_SHUTDOWN_DANCE BIT(7)
+
+static const struct reg_sequence tas2764_shutdown_dance_init_seq[] = {
+ /*
+ * SDZ_MODE=01 (immediate)
+ *
+ * We want the shutdown to happen under the influence of
+ * the magic writes in the 0xfdXX region, so make sure
+ * the shutdown is immediate and there's no grace period
+ * followed by the codec part.
+ */
+ REG_SEQ0(TAS2764_REG(0x0, 0x7), 0x60),
+};
+
+static const struct reg_sequence tas2764_pre_shutdown_seq[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd), /* switch hidden page */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x4), /* do write (unknown semantics) */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0), /* switch hidden page back */
+};
+
+static const struct reg_sequence tas2764_post_shutdown_seq[] = {
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0xd),
+ REG_SEQ0(TAS2764_REG(0xfd, 0x64), 0x0), /* revert write from pre sequence */
+ REG_SEQ0(TAS2764_REG(0xfd, 0x0d), 0x0),
+};
+
+static int tas2764_do_quirky_pwr_ctrl_change(struct tas2764_priv *tas2764,
+ unsigned int target)
+{
+ unsigned int curr;
+ int ret;
+
+ curr = snd_soc_component_read_field(tas2764->component,
+ TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK);
+
+ if (target == curr)
+ return 0;
+
+ /* Handle power state transition to shutdown */
+ if (target == TAS2764_PWR_CTRL_SHUTDOWN &&
+ (curr == TAS2764_PWR_CTRL_MUTE || curr == TAS2764_PWR_CTRL_ACTIVE)) {
+ ret = regmap_multi_reg_write(tas2764->regmap, tas2764_pre_shutdown_seq,
+ ARRAY_SIZE(tas2764_pre_shutdown_seq));
+ if (!ret)
+ ret = snd_soc_component_update_bits(tas2764->component,
+ TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK,
+ TAS2764_PWR_CTRL_SHUTDOWN);
+ if (!ret)
+ ret = regmap_multi_reg_write(tas2764->regmap,
+ tas2764_post_shutdown_seq,
+ ARRAY_SIZE(tas2764_post_shutdown_seq));
+ }
+
+ ret = snd_soc_component_update_bits(tas2764->component, TAS2764_PWR_CTRL,
+ TAS2764_PWR_CTRL_MASK, target);
+
+ return ret;
+}
+
+/*
+ * Via devicetree (TODO):
+ * - switch from spread spectrum to class-D switching
+ * - disable edge control
+ * - set BOP settings (the BOP config bits *and* BOP_SRC)
+ */
+
+/*
+ * Other setup TODOs:
+ * - DVC ramp rate
+ */
+
+static const struct tas2764_quirk_init_sequence {
+ const struct reg_sequence *seq;
+ int len;
+} tas2764_quirk_init_sequences[] = {
+ { tas2764_noise_gate_dis_seq, ARRAY_SIZE(tas2764_noise_gate_dis_seq) },
+ { tas2764_dmod_rst_seq, ARRAY_SIZE(tas2764_dmod_rst_seq) },
+ { tas2764_conv_vbat_pvdd_mode_seq, ARRAY_SIZE(tas2764_conv_vbat_pvdd_mode_seq) },
+ { tas2764_unk_seq0, ARRAY_SIZE(tas2764_unk_seq0) },
+ { tas2764_unk_seq1, ARRAY_SIZE(tas2764_unk_seq1) },
+ { tas2764_unk_seq2, ARRAY_SIZE(tas2764_unk_seq2) },
+ { tas2764_thermal_th1_dis_seq, ARRAY_SIZE(tas2764_thermal_th1_dis_seq) },
+ { tas2764_shutdown_dance_init_seq, ARRAY_SIZE(tas2764_shutdown_dance_init_seq) },
+};
+
+#endif /* __TAS2764_QUIRKS__ */
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index 08aa7ee34256..36e25e48b354 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -8,6 +8,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/hwmon.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/gpio/consumer.h>
@@ -45,6 +46,8 @@ struct tas2764_priv {
bool unmuted;
};
+#include "tas2764-quirks.h"
+
static const char *tas2764_int_ltch0_msgs[8] = {
"fault: over temperature", /* INT_LTCH0 & BIT(0) */
"fault: over current",
@@ -122,6 +125,9 @@ static int tas2764_update_pwr_ctrl(struct tas2764_priv *tas2764)
else
val = TAS2764_PWR_CTRL_SHUTDOWN;
+ if (ENABLED_APPLE_QUIRKS & TAS2764_SHUTDOWN_DANCE)
+ return tas2764_do_quirky_pwr_ctrl_change(tas2764, val);
+
ret = snd_soc_component_update_bits(component, TAS2764_PWR_CTRL,
TAS2764_PWR_CTRL_MASK, val);
if (ret < 0)
@@ -546,6 +552,106 @@ static uint8_t sn012776_bop_presets[] = {
0x06, 0x3e, 0x37, 0x30, 0xff, 0xe6
};
+static const struct regmap_config tas2764_i2c_regmap;
+
+static int tas2764_apply_init_quirks(struct tas2764_priv *tas2764)
+{
+ int ret, i;
+
+ for (i = 0; i < ARRAY_SIZE(tas2764_quirk_init_sequences); i++) {
+ const struct tas2764_quirk_init_sequence *init_seq =
+ &tas2764_quirk_init_sequences[i];
+
+ if (!init_seq->seq)
+ continue;
+
+ if (!(BIT(i) & ENABLED_APPLE_QUIRKS))
+ continue;
+
+ ret = regmap_multi_reg_write(tas2764->regmap, init_seq->seq,
+ init_seq->len);
+
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tas2764_read_die_temp(struct tas2764_priv *tas2764, long *result)
+{
+ int ret, reg;
+
+ ret = regmap_read(tas2764->regmap, TAS2764_TEMP, &reg);
+ if (ret)
+ return ret;
+ /*
+ * As per datasheet, subtract 93 from raw value to get degrees
+ * Celsius. hwmon wants millidegrees.
+ *
+ * NOTE: The chip will initialise the TAS2764_TEMP register to
+ * 2.6 *C to avoid triggering temperature protection. Since the
+ * ADC is powered down during software shutdown, this value will
+ * persist until the chip is fully powered up (e.g. the PCM it's
+ * attached to is opened). The ADC will power down again when
+ * the chip is put back into software shutdown, with the last
+ * value sampled persisting in the ADC's register.
+ */
+ *result = (reg - 93) * 1000;
+ return 0;
+}
+
+static umode_t tas2764_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tas2764_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct tas2764_priv *tas2764 = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = tas2764_read_die_temp(tas2764, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const tas2764_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops tas2764_hwmon_ops = {
+ .is_visible = tas2764_hwmon_is_visible,
+ .read = tas2764_hwmon_read,
+};
+
+static const struct hwmon_chip_info tas2764_hwmon_chip_info = {
+ .ops = &tas2764_hwmon_ops,
+ .info = tas2764_hwmon_info,
+};
+
static int tas2764_codec_probe(struct snd_soc_component *component)
{
struct tas2764_priv *tas2764 = snd_soc_component_get_drvdata(component);
@@ -559,9 +665,10 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
}
tas2764_reset(tas2764);
+ regmap_reinit_cache(tas2764->regmap, &tas2764_i2c_regmap);
if (tas2764->irq) {
- ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0xff);
+ ret = snd_soc_component_write(tas2764->component, TAS2764_INT_MASK0, 0x00);
if (ret < 0)
return ret;
@@ -614,6 +721,13 @@ static int tas2764_codec_probe(struct snd_soc_component *component)
if (ret < 0)
return ret;
}
+
+ /* Apply all enabled Apple quirks */
+ ret = tas2764_apply_init_quirks(tas2764);
+
+ if (ret < 0)
+ return ret;
+
break;
default:
break;
@@ -682,7 +796,7 @@ static const struct reg_default tas2764_reg_defaults[] = {
static const struct regmap_range_cfg tas2764_regmap_ranges[] = {
{
.range_min = 0,
- .range_max = 1 * 128,
+ .range_max = 0xffff,
.selector_reg = TAS2764_PAGE,
.selector_mask = 0xff,
.selector_shift = 0,
@@ -698,6 +812,9 @@ static bool tas2764_volatile_register(struct device *dev, unsigned int reg)
case TAS2764_INT_LTCH0 ... TAS2764_INT_LTCH4:
case TAS2764_INT_CLK_CFG:
return true;
+ case TAS2764_REG(0xf0, 0x0) ... TAS2764_REG(0xff, 0x0):
+ /* TI's undocumented registers for the application of quirks */
+ return true;
default:
return false;
}
@@ -712,7 +829,7 @@ static const struct regmap_config tas2764_i2c_regmap = {
.cache_type = REGCACHE_RBTREE,
.ranges = tas2764_regmap_ranges,
.num_ranges = ARRAY_SIZE(tas2764_regmap_ranges),
- .max_register = 1 * 128,
+ .max_register = 0xffff,
};
static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
@@ -759,7 +876,7 @@ static int tas2764_i2c_probe(struct i2c_client *client)
if (!tas2764)
return -ENOMEM;
- tas2764->devid = (enum tas2764_devid)of_device_get_match_data(&client->dev);
+ tas2764->devid = (kernel_ulong_t)of_device_get_match_data(&client->dev);
tas2764->dev = &client->dev;
tas2764->irq = client->irq;
@@ -783,6 +900,20 @@ static int tas2764_i2c_probe(struct i2c_client *client)
}
}
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2764",
+ tas2764,
+ &tas2764_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ return dev_err_probe(&client->dev, PTR_ERR(hwmon),
+ "Failed to register temp sensor\n");
+ }
+ }
+
+
return devm_snd_soc_register_component(tas2764->dev,
&soc_component_driver_tas2764,
tas2764_dai_driver,
diff --git a/sound/soc/codecs/tas2764.h b/sound/soc/codecs/tas2764.h
index 3251dc0106e0..538290ed3d92 100644
--- a/sound/soc/codecs/tas2764.h
+++ b/sound/soc/codecs/tas2764.h
@@ -117,6 +117,9 @@
#define TAS2764_INT_LTCH3 TAS2764_REG(0x0, 0x50)
#define TAS2764_INT_LTCH4 TAS2764_REG(0x0, 0x51)
+/* Readout Registers */
+#define TAS2764_TEMP TAS2764_REG(0x0, 0x56)
+
/* Clock/IRQ Settings */
#define TAS2764_INT_CLK_CFG TAS2764_REG(0x0, 0x5c)
#define TAS2764_INT_CLK_CFG_IRQZ_CLR BIT(2)
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index 7f219df8be70..6f878b01716f 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -12,6 +12,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/delay.h>
+#include <linux/hwmon.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/gpio/consumer.h>
@@ -156,11 +157,37 @@ static const struct snd_kcontrol_new isense_switch =
static const struct snd_kcontrol_new vsense_switch =
SOC_DAPM_SINGLE("Switch", TAS2770_PWR_CTRL, 2, 1, 1);
+static int sense_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 tas2770_priv *tas2770 = snd_soc_component_get_drvdata(component);
+
+ /*
+ * Powering up ISENSE/VSENSE requires a trip through the shutdown state.
+ * Do that here to ensure that our changes are applied properly, otherwise
+ * we might end up with non-functional IVSENSE if playback started earlier,
+ * which would break software speaker protection.
+ */
+ switch (event) {
+ case SND_SOC_DAPM_PRE_REG:
+ return snd_soc_component_update_bits(component, TAS2770_PWR_CTRL,
+ TAS2770_PWR_CTRL_MASK,
+ TAS2770_PWR_CTRL_SHUTDOWN);
+ case SND_SOC_DAPM_POST_REG:
+ return tas2770_update_pwr_ctrl(tas2770);
+ default:
+ return 0;
+ }
+}
+
static const struct snd_soc_dapm_widget tas2770_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("ASI1", "ASI1 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("ASI1 Sel", SND_SOC_NOPM, 0, 0, &tas2770_asi1_mux),
- SND_SOC_DAPM_SWITCH("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch),
- SND_SOC_DAPM_SWITCH("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch),
+ SND_SOC_DAPM_SWITCH_E("ISENSE", TAS2770_PWR_CTRL, 3, 1, &isense_switch,
+ sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),
+ SND_SOC_DAPM_SWITCH_E("VSENSE", TAS2770_PWR_CTRL, 2, 1, &vsense_switch,
+ sense_event, SND_SOC_DAPM_PRE_REG | SND_SOC_DAPM_POST_REG),
SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, tas2770_dac_event,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_OUTPUT("OUT"),
@@ -214,6 +241,19 @@ static int tas2770_set_ivsense_transmit(struct tas2770_priv *tas2770,
return 0;
}
+static int tas2770_set_pdm_transmit(struct tas2770_priv *tas2770, int slot)
+{
+ struct snd_soc_component *component = tas2770->component;
+ int ret;
+
+ ret = snd_soc_component_update_bits(component, TAS2770_TDM_CFG_REG7,
+ TAS2770_TDM_CFG_REG7_PDM_MASK |
+ TAS2770_TDM_CFG_REG7_50_MASK,
+ TAS2770_TDM_CFG_REG7_PDM_ENABLE |
+ slot);
+ return ret;
+}
+
static int tas2770_set_bitwidth(struct tas2770_priv *tas2770, int bitwidth)
{
int ret;
@@ -491,6 +531,88 @@ static struct snd_soc_dai_driver tas2770_dai_driver[] = {
},
};
+static int tas2770_read_die_temp(struct tas2770_priv *tas2770, long *result)
+{
+ int ret = 0;
+ int reading, msb, lsb;
+
+ ret = regmap_read(tas2770->regmap, TAS2770_TEMP_MSB, &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(tas2770->regmap, TAS2770_TEMP_LSB, &lsb);
+ if (ret)
+ return ret;
+
+ reading = (msb << 4) | (lsb >> 4);
+
+ /*
+ * As per datasheet: divide register by 16 and subtract 93 to get
+ * degrees Celsius. hwmon requires millidegrees. Let's avoid rounding
+ * errors by subtracting 93 * 16 then multiplying by 1000 / 16.
+ *
+ * NOTE: The ADC registers are initialised to 0 on reset. This means
+ * that the temperature will read -93 *C until the chip is brought out
+ * of software shutdown (e.g. the PCM it's attached to is opened). The
+ * ADC is also shut down in software shutdown/low-power mode, so the
+ * value read back from its registers will be the last value sampled
+ * before entering software shutdown.
+ */
+ *result = (reading - (93 * 16)) * (1000 / 16);
+ return 0;
+}
+
+static umode_t tas2770_hwmon_is_visible(const void *data,
+ enum hwmon_sensor_types type, u32 attr,
+ int channel)
+{
+ if (type != hwmon_temp)
+ return 0;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ return 0444;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int tas2770_hwmon_read(struct device *dev,
+ enum hwmon_sensor_types type,
+ u32 attr, int channel, long *val)
+{
+ struct tas2770_priv *tas2770 = dev_get_drvdata(dev);
+ int ret;
+
+ switch (attr) {
+ case hwmon_temp_input:
+ ret = tas2770_read_die_temp(tas2770, val);
+ break;
+ default:
+ ret = -EOPNOTSUPP;
+ break;
+ }
+
+ return ret;
+}
+
+static const struct hwmon_channel_info *const tas2770_hwmon_info[] = {
+ HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
+ NULL
+};
+
+static const struct hwmon_ops tas2770_hwmon_ops = {
+ .is_visible = tas2770_hwmon_is_visible,
+ .read = tas2770_hwmon_read,
+};
+
+static const struct hwmon_chip_info tas2770_hwmon_chip_info = {
+ .ops = &tas2770_hwmon_ops,
+ .info = tas2770_hwmon_info,
+};
+
static const struct regmap_config tas2770_i2c_regmap;
static int tas2770_codec_probe(struct snd_soc_component *component)
@@ -517,6 +639,13 @@ static int tas2770_codec_probe(struct snd_soc_component *component)
return ret;
}
+ if (tas2770->pdm_slot != -1) {
+ ret = tas2770_set_pdm_transmit(tas2770, tas2770->pdm_slot);
+
+ if (ret < 0)
+ return ret;
+ }
+
return 0;
}
@@ -656,6 +785,11 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)
tas2770->v_sense_slot = -1;
}
+ rc = fwnode_property_read_u32(dev->fwnode, "ti,pdm-slot-no",
+ &tas2770->pdm_slot);
+ if (rc)
+ tas2770->pdm_slot = -1;
+
tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
if (IS_ERR(tas2770->sdz_gpio)) {
if (PTR_ERR(tas2770->sdz_gpio) == -EPROBE_DEFER)
@@ -707,6 +841,19 @@ static int tas2770_i2c_probe(struct i2c_client *client)
}
}
+ if (IS_REACHABLE(CONFIG_HWMON)) {
+ struct device *hwmon;
+
+ hwmon = devm_hwmon_device_register_with_info(&client->dev, "tas2770",
+ tas2770,
+ &tas2770_hwmon_chip_info,
+ NULL);
+ if (IS_ERR(hwmon)) {
+ return dev_err_probe(&client->dev, PTR_ERR(hwmon),
+ "Failed to register temp sensor\n");
+ }
+ }
+
result = tas2770_register_codec(tas2770);
if (result)
dev_err(tas2770->dev, "Register codec failed.\n");
diff --git a/sound/soc/codecs/tas2770.h b/sound/soc/codecs/tas2770.h
index f75f40781ab1..3fd2e7003c50 100644
--- a/sound/soc/codecs/tas2770.h
+++ b/sound/soc/codecs/tas2770.h
@@ -77,6 +77,11 @@
#define TAS2770_TDM_CFG_REG6_ISNS_MASK BIT(6)
#define TAS2770_TDM_CFG_REG6_ISNS_ENABLE BIT(6)
#define TAS2770_TDM_CFG_REG6_50_MASK GENMASK(5, 0)
+ /* TDM Configuration Reg10 */
+#define TAS2770_TDM_CFG_REG7 TAS2770_REG(0X0, 0x11)
+#define TAS2770_TDM_CFG_REG7_PDM_MASK BIT(6)
+#define TAS2770_TDM_CFG_REG7_PDM_ENABLE BIT(6)
+#define TAS2770_TDM_CFG_REG7_50_MASK GENMASK(5, 0)
/* Brown Out Prevention Reg0 */
#define TAS2770_BO_PRV_REG0 TAS2770_REG(0X0, 0x1B)
/* Interrupt MASK Reg0 */
@@ -138,6 +143,7 @@ struct tas2770_priv {
struct device *dev;
int v_sense_slot;
int i_sense_slot;
+ int pdm_slot;
bool dac_powered;
bool unmuted;
};
diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c
new file mode 100644
index 000000000000..c078bb0a8437
--- /dev/null
+++ b/sound/soc/codecs/tas2781-comlib-i2c.c
@@ -0,0 +1,369 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers based on I2C
+//
+// Copyright 2025 Texas Instruments, Inc.
+//
+// Author: Shenghao Ding <shenghao-ding@ti.com>
+
+#include <linux/crc8.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
+
+static const struct regmap_range_cfg tasdevice_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 256 * 128,
+ .selector_reg = TASDEVICE_PAGE_SELECT,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 128,
+ },
+};
+
+static const struct regmap_config tasdevice_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ .ranges = tasdevice_ranges,
+ .num_ranges = ARRAY_SIZE(tasdevice_ranges),
+ .max_register = 256 * 128,
+};
+
+static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
+ unsigned short chn, int book)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
+ struct regmap *map = tas_priv->regmap;
+
+ if (client->addr != tasdev->dev_addr) {
+ client->addr = tasdev->dev_addr;
+ /* All tas2781s share the same regmap, clear the page
+ * inside regmap once switching to another tas2781.
+ * Register 0 at any pages and any books inside tas2781
+ * is the same one for page-switching.
+ */
+ ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d channel:%d\n",
+ __func__, ret, chn);
+ goto out;
+ }
+ }
+
+ if (tasdev->cur_book != book) {
+ ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n",
+ __func__, ret);
+ goto out;
+ }
+ tasdev->cur_book = book;
+ }
+ } else {
+ ret = -EINVAL;
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ }
+
+out:
+ return ret;
+}
+
+int tasdev_chn_switch(struct tasdevice_priv *tas_priv,
+ unsigned short chn)
+{
+ struct i2c_client *client = (struct i2c_client *)tas_priv->client;
+ struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
+ struct regmap *map = tas_priv->regmap;
+ int ret;
+
+ if (client->addr != tasdev->dev_addr) {
+ client->addr = tasdev->dev_addr;
+ /* All devices share the same regmap, clear the page
+ * inside regmap once switching to another device.
+ * Register 0 at any pages and any books inside tas2781
+ * is the same one for page-switching.
+ */
+ ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
+ if (ret < 0) {
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ return ret;
+ }
+ return 1;
+ }
+ return 0;
+}
+EXPORT_SYMBOL_GPL(tasdev_chn_switch);
+
+int tasdevice_dev_update_bits(
+ struct tasdevice_priv *tas_priv, unsigned short chn,
+ unsigned int reg, unsigned int mask, unsigned int value)
+{
+ int ret = 0;
+
+ if (chn < tas_priv->ndev) {
+ struct regmap *map = tas_priv->regmap;
+
+ ret = tas_priv->change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
+
+ ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
+ mask, value);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ } else {
+ dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
+ chn);
+ ret = -EINVAL;
+ }
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits);
+
+struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
+{
+ struct tasdevice_priv *tas_priv;
+
+ tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL);
+ if (!tas_priv)
+ return NULL;
+ tas_priv->dev = &i2c->dev;
+ tas_priv->client = (void *)i2c;
+
+ return tas_priv;
+}
+EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
+
+int tasdevice_init(struct tasdevice_priv *tas_priv)
+{
+ int ret = 0;
+ int i;
+
+ tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client,
+ &tasdevice_regmap);
+ if (IS_ERR(tas_priv->regmap)) {
+ ret = PTR_ERR(tas_priv->regmap);
+ dev_err(tas_priv->dev, "Failed to allocate register map: %d\n",
+ ret);
+ goto out;
+ }
+
+ tas_priv->cur_prog = -1;
+ tas_priv->cur_conf = -1;
+ tas_priv->isspi = false;
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ tas_priv->tasdevice[i].cur_book = -1;
+ tas_priv->tasdevice[i].cur_prog = -1;
+ tas_priv->tasdevice[i].cur_conf = -1;
+ }
+
+ tas_priv->update_bits = tasdevice_dev_update_bits;
+ tas_priv->change_chn_book = tasdevice_change_chn_book;
+ tas_priv->dev_read = tasdevice_dev_read;
+ tas_priv->dev_bulk_read = tasdevice_dev_bulk_read;
+
+ mutex_init(&tas_priv->codec_lock);
+
+out:
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tasdevice_init);
+
+static int tasdevice_clamp(int val, int max, unsigned int invert)
+{
+ if (val > max)
+ val = max;
+ if (invert)
+ val = max - val;
+ if (val < 0)
+ val = 0;
+ return val;
+}
+
+int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask;
+ int max = mc->max;
+ int err_cnt = 0;
+ int val, i, ret;
+
+ mask = (1 << fls(max)) - 1;
+ mask <<= mc->shift;
+ val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+ for (i = 0; i < tas_priv->ndev; i++) {
+ ret = tasdevice_dev_update_bits(tas_priv, i,
+ mc->reg, mask, (unsigned int)(val << mc->shift));
+ if (!ret)
+ continue;
+ err_cnt++;
+ dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i);
+ }
+
+ /* All the devices set error, return 0 */
+ return (err_cnt == tas_priv->ndev) ? 0 : 1;
+}
+EXPORT_SYMBOL_GPL(tasdevice_amp_putvol);
+
+int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ unsigned char mask = 0;
+ int max = mc->max;
+ int ret = 0;
+ int val;
+
+ /* Read the primary device */
+ ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
+ goto out;
+ }
+
+ mask = (1 << fls(max)) - 1;
+ mask <<= mc->shift;
+ val = (val & mask) >> mc->shift;
+ val = tasdevice_clamp(val, max, invert);
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_amp_getvol);
+
+int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int ret, val;
+
+ /* Read the primary device as the whole */
+ ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
+ if (ret) {
+ dev_err(tas_priv->dev, "%s, get digital vol error\n",
+ __func__);
+ goto out;
+ }
+
+ val = tasdevice_clamp(val, max, invert);
+ ucontrol->value.integer.value[0] = val;
+
+out:
+ return ret;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_digital_getvol);
+
+int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
+ struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
+{
+ unsigned int invert = mc->invert;
+ int max = mc->max;
+ int err_cnt = 0;
+ int ret;
+ int val, i;
+
+ val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
+
+ for (i = 0; i < tas_priv->ndev; i++) {
+ ret = tasdevice_dev_write(tas_priv, i, mc->reg,
+ (unsigned int)val);
+ if (!ret)
+ continue;
+ err_cnt++;
+ dev_err(tas_priv->dev,
+ "set digital vol err in dev %d\n", i);
+ }
+
+ /* All the devices set error, return 0 */
+ return (err_cnt == tas_priv->ndev) ? 0 : 1;
+
+}
+EXPORT_SYMBOL_GPL(tasdevice_digital_putvol);
+
+void tasdevice_reset(struct tasdevice_priv *tas_dev)
+{
+ int ret, i;
+
+ if (tas_dev->reset) {
+ gpiod_set_value_cansleep(tas_dev->reset, 0);
+ usleep_range(500, 1000);
+ gpiod_set_value_cansleep(tas_dev->reset, 1);
+ } else {
+ for (i = 0; i < tas_dev->ndev; i++) {
+ ret = tasdevice_dev_write(tas_dev, i,
+ TASDEVICE_REG_SWRESET,
+ TASDEVICE_REG_SWRESET_RESET);
+ if (ret < 0)
+ dev_err(tas_dev->dev,
+ "dev %d swreset fail, %d\n",
+ i, ret);
+ }
+ }
+ usleep_range(1000, 1050);
+}
+EXPORT_SYMBOL_GPL(tasdevice_reset);
+
+int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
+ struct module *module,
+ void (*cont)(const struct firmware *fw, void *context))
+{
+ int ret = 0;
+
+ /* Codec Lock Hold to ensure that codec_probe and firmware parsing and
+ * loading do not simultaneously execute.
+ */
+ mutex_lock(&tas_priv->codec_lock);
+
+ if (tas_priv->name_prefix)
+ scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin",
+ tas_priv->name_prefix, tas_priv->dev_name,
+ tas_priv->ndev);
+ else
+ scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin",
+ tas_priv->dev_name, tas_priv->ndev);
+ crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
+ tas_priv->codec = codec;
+ ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
+ tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
+ cont);
+ if (ret)
+ dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
+ ret);
+
+ /* Codec Lock Release*/
+ mutex_unlock(&tas_priv->codec_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(tascodec_init);
+
+MODULE_DESCRIPTION("TAS2781 common library for I2C");
+MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
index 1e0b3aa95749..4cec9f8a00af 100644
--- a/sound/soc/codecs/tas2781-comlib.c
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -2,14 +2,14 @@
//
// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
//
-// Copyright 2023 - 2024 Texas Instruments, Inc.
+// Copyright 2023 - 2025 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
#include <linux/crc8.h>
+#include <linux/dev_printk.h>
#include <linux/firmware.h>
#include <linux/gpio/consumer.h>
-#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -17,67 +17,24 @@
#include <linux/of_irq.h>
#include <linux/regmap.h>
#include <linux/slab.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
#include <sound/tas2781.h>
-#define TASDEVICE_CRC8_POLYNOMIAL 0x4d
-
-static const struct regmap_range_cfg tasdevice_ranges[] = {
- {
- .range_min = 0,
- .range_max = 256 * 128,
- .selector_reg = TASDEVICE_PAGE_SELECT,
- .selector_mask = 0xff,
- .selector_shift = 0,
- .window_start = 0,
- .window_len = 128,
- },
-};
-
-static const struct regmap_config tasdevice_regmap = {
- .reg_bits = 8,
- .val_bits = 8,
- .cache_type = REGCACHE_NONE,
- .ranges = tasdevice_ranges,
- .num_ranges = ARRAY_SIZE(tasdevice_ranges),
- .max_register = 256 * 128,
-};
-
-static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
- unsigned short chn, int book)
+int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned int *val)
{
- struct i2c_client *client = (struct i2c_client *)tas_priv->client;
int ret = 0;
if (chn < tas_priv->ndev) {
- struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
struct regmap *map = tas_priv->regmap;
- if (client->addr != tasdev->dev_addr) {
- client->addr = tasdev->dev_addr;
- /* All tas2781s share the same regmap, clear the page
- * inside regmap once switching to another tas2781.
- * Register 0 at any pages and any books inside tas2781
- * is the same one for page-switching.
- */
- ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
- if (ret < 0) {
- dev_err(tas_priv->dev, "%s, E=%d channel:%d\n",
- __func__, ret, chn);
- goto out;
- }
- }
+ ret = tas_priv->change_chn_book(tas_priv, chn,
+ TASDEVICE_BOOK_ID(reg));
+ if (ret < 0)
+ goto out;
- if (tasdev->cur_book != book) {
- ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book);
- if (ret < 0) {
- dev_err(tas_priv->dev, "%s, E=%d\n",
- __func__, ret);
- goto out;
- }
- tasdev->cur_book = book;
- }
+ ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
+ if (ret < 0)
+ dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
} else {
ret = -EINVAL;
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
@@ -87,59 +44,33 @@ static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv,
out:
return ret;
}
+EXPORT_SYMBOL_GPL(tasdevice_dev_read);
-int tasdev_chn_switch(struct tasdevice_priv *tas_priv,
- unsigned short chn)
-{
- struct i2c_client *client = (struct i2c_client *)tas_priv->client;
- struct tasdevice *tasdev = &tas_priv->tasdevice[chn];
- struct regmap *map = tas_priv->regmap;
- int ret;
-
- if (client->addr != tasdev->dev_addr) {
- client->addr = tasdev->dev_addr;
- /* All devices share the same regmap, clear the page
- * inside regmap once switching to another device.
- * Register 0 at any pages and any books inside tas2781
- * is the same one for page-switching.
- */
- ret = regmap_write(map, TASDEVICE_PAGE_SELECT, 0);
- if (ret < 0) {
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
- return ret;
- }
- return 1;
- }
- return 0;
-}
-EXPORT_SYMBOL_GPL(tasdev_chn_switch);
-
-int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
- unsigned short chn, unsigned int reg, unsigned int *val)
+int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
+ unsigned short chn, unsigned int reg, unsigned char *data,
+ unsigned int len)
{
int ret = 0;
if (chn < tas_priv->ndev) {
struct regmap *map = tas_priv->regmap;
- ret = tasdevice_change_chn_book(tas_priv, chn,
+ ret = tas_priv->change_chn_book(tas_priv, chn,
TASDEVICE_BOOK_ID(reg));
if (ret < 0)
goto out;
- ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
+ ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
if (ret < 0)
dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
- } else {
- ret = -EINVAL;
+ } else
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
chn);
- }
out:
return ret;
}
-EXPORT_SYMBOL_GPL(tasdevice_dev_read);
+EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
unsigned short chn, unsigned int reg, unsigned int value)
@@ -149,7 +80,7 @@ int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
if (chn < tas_priv->ndev) {
struct regmap *map = tas_priv->regmap;
- ret = tasdevice_change_chn_book(tas_priv, chn,
+ ret = tas_priv->change_chn_book(tas_priv, chn,
TASDEVICE_BOOK_ID(reg));
if (ret < 0)
goto out;
@@ -179,7 +110,7 @@ int tasdevice_dev_bulk_write(
if (chn < tas_priv->ndev) {
struct regmap *map = tas_priv->regmap;
- ret = tasdevice_change_chn_book(tas_priv, chn,
+ ret = tas_priv->change_chn_book(tas_priv, chn,
TASDEVICE_BOOK_ID(reg));
if (ret < 0)
goto out;
@@ -199,161 +130,6 @@ out:
}
EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write);
-int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
- unsigned short chn, unsigned int reg, unsigned char *data,
- unsigned int len)
-{
- int ret = 0;
-
- if (chn < tas_priv->ndev) {
- struct regmap *map = tas_priv->regmap;
-
- ret = tasdevice_change_chn_book(tas_priv, chn,
- TASDEVICE_BOOK_ID(reg));
- if (ret < 0)
- goto out;
-
- ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
- if (ret < 0)
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
- } else
- dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
- chn);
-
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read);
-
-int tasdevice_dev_update_bits(
- struct tasdevice_priv *tas_priv, unsigned short chn,
- unsigned int reg, unsigned int mask, unsigned int value)
-{
- int ret = 0;
-
- if (chn < tas_priv->ndev) {
- struct regmap *map = tas_priv->regmap;
-
- ret = tasdevice_change_chn_book(tas_priv, chn,
- TASDEVICE_BOOK_ID(reg));
- if (ret < 0)
- goto out;
-
- ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
- mask, value);
- if (ret < 0)
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
- } else {
- dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
- chn);
- ret = -EINVAL;
- }
-
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits);
-
-struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c)
-{
- struct tasdevice_priv *tas_priv;
-
- tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL);
- if (!tas_priv)
- return NULL;
- tas_priv->dev = &i2c->dev;
- tas_priv->client = (void *)i2c;
-
- return tas_priv;
-}
-EXPORT_SYMBOL_GPL(tasdevice_kzalloc);
-
-void tasdevice_reset(struct tasdevice_priv *tas_dev)
-{
- int ret, i;
-
- if (tas_dev->reset) {
- gpiod_set_value_cansleep(tas_dev->reset, 0);
- usleep_range(500, 1000);
- gpiod_set_value_cansleep(tas_dev->reset, 1);
- } else {
- for (i = 0; i < tas_dev->ndev; i++) {
- ret = tasdevice_dev_write(tas_dev, i,
- TASDEVICE_REG_SWRESET,
- TASDEVICE_REG_SWRESET_RESET);
- if (ret < 0)
- dev_err(tas_dev->dev,
- "dev %d swreset fail, %d\n",
- i, ret);
- }
- }
- usleep_range(1000, 1050);
-}
-EXPORT_SYMBOL_GPL(tasdevice_reset);
-
-int tascodec_init(struct tasdevice_priv *tas_priv, void *codec,
- struct module *module,
- void (*cont)(const struct firmware *fw, void *context))
-{
- int ret = 0;
-
- /* Codec Lock Hold to ensure that codec_probe and firmware parsing and
- * loading do not simultaneously execute.
- */
- mutex_lock(&tas_priv->codec_lock);
-
- if (tas_priv->name_prefix)
- scnprintf(tas_priv->rca_binaryname, 64, "%s-%sRCA%d.bin",
- tas_priv->name_prefix, tas_priv->dev_name,
- tas_priv->ndev);
- else
- scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin",
- tas_priv->dev_name, tas_priv->ndev);
- crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL);
- tas_priv->codec = codec;
- ret = request_firmware_nowait(module, FW_ACTION_UEVENT,
- tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv,
- cont);
- if (ret)
- dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n",
- ret);
-
- /* Codec Lock Release*/
- mutex_unlock(&tas_priv->codec_lock);
- return ret;
-}
-EXPORT_SYMBOL_GPL(tascodec_init);
-
-int tasdevice_init(struct tasdevice_priv *tas_priv)
-{
- int ret = 0;
- int i;
-
- tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client,
- &tasdevice_regmap);
- if (IS_ERR(tas_priv->regmap)) {
- ret = PTR_ERR(tas_priv->regmap);
- dev_err(tas_priv->dev, "Failed to allocate register map: %d\n",
- ret);
- goto out;
- }
-
- tas_priv->cur_prog = -1;
- tas_priv->cur_conf = -1;
-
- for (i = 0; i < tas_priv->ndev; i++) {
- tas_priv->tasdevice[i].cur_book = -1;
- tas_priv->tasdevice[i].cur_prog = -1;
- tas_priv->tasdevice[i].cur_conf = -1;
- }
-
- mutex_init(&tas_priv->codec_lock);
-
-out:
- return ret;
-}
-EXPORT_SYMBOL_GPL(tasdevice_init);
-
static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog)
{
struct tasdevice_data *tas_dt;
@@ -440,137 +216,6 @@ void tasdevice_remove(struct tasdevice_priv *tas_priv)
}
EXPORT_SYMBOL_GPL(tasdevice_remove);
-int tasdevice_save_calibration(struct tasdevice_priv *tas_priv)
-{
- if (tas_priv->save_calibration)
- return tas_priv->save_calibration(tas_priv);
- return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(tasdevice_save_calibration);
-
-void tasdevice_apply_calibration(struct tasdevice_priv *tas_priv)
-{
- if (tas_priv->apply_calibration && tas_priv->cali_data.total_sz)
- tas_priv->apply_calibration(tas_priv);
-}
-EXPORT_SYMBOL_GPL(tasdevice_apply_calibration);
-
-static int tasdevice_clamp(int val, int max, unsigned int invert)
-{
- if (val > max)
- val = max;
- if (invert)
- val = max - val;
- if (val < 0)
- val = 0;
- return val;
-}
-
-int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv,
- struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
-{
- unsigned int invert = mc->invert;
- unsigned char mask;
- int max = mc->max;
- int err_cnt = 0;
- int val, i, ret;
-
- mask = (1 << fls(max)) - 1;
- mask <<= mc->shift;
- val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
- for (i = 0; i < tas_priv->ndev; i++) {
- ret = tasdevice_dev_update_bits(tas_priv, i,
- mc->reg, mask, (unsigned int)(val << mc->shift));
- if (!ret)
- continue;
- err_cnt++;
- dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i);
- }
-
- /* All the devices set error, return 0 */
- return (err_cnt == tas_priv->ndev) ? 0 : 1;
-}
-EXPORT_SYMBOL_GPL(tasdevice_amp_putvol);
-
-int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv,
- struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
-{
- unsigned int invert = mc->invert;
- unsigned char mask = 0;
- int max = mc->max;
- int ret = 0;
- int val;
-
- /* Read the primary device */
- ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
- if (ret) {
- dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__);
- goto out;
- }
-
- mask = (1 << fls(max)) - 1;
- mask <<= mc->shift;
- val = (val & mask) >> mc->shift;
- val = tasdevice_clamp(val, max, invert);
- ucontrol->value.integer.value[0] = val;
-
-out:
- return ret;
-
-}
-EXPORT_SYMBOL_GPL(tasdevice_amp_getvol);
-
-int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv,
- struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
-{
- unsigned int invert = mc->invert;
- int max = mc->max;
- int err_cnt = 0;
- int ret;
- int val, i;
-
- val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert);
-
- for (i = 0; i < tas_priv->ndev; i++) {
- ret = tasdevice_dev_write(tas_priv, i, mc->reg,
- (unsigned int)val);
- if (!ret)
- continue;
- err_cnt++;
- dev_err(tas_priv->dev,
- "set digital vol err in dev %d\n", i);
- }
-
- /* All the devices set error, return 0 */
- return (err_cnt == tas_priv->ndev) ? 0 : 1;
-
-}
-EXPORT_SYMBOL_GPL(tasdevice_digital_putvol);
-
-int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv,
- struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc)
-{
- unsigned int invert = mc->invert;
- int max = mc->max;
- int ret, val;
-
- /* Read the primary device as the whole */
- ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val);
- if (ret) {
- dev_err(tas_priv->dev, "%s, get digital vol error\n",
- __func__);
- goto out;
- }
-
- val = tasdevice_clamp(val, max, invert);
- ucontrol->value.integer.value[0] = val;
-
-out:
- return ret;
-
-}
-EXPORT_SYMBOL_GPL(tasdevice_digital_getvol);
-
MODULE_DESCRIPTION("TAS2781 common library");
MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 13a197468193..c9c1e608ddb7 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -50,6 +50,11 @@
#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG
#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG
+#define TASDEVICE_CMD_SING_W 0x1
+#define TASDEVICE_CMD_BURST 0x2
+#define TASDEVICE_CMD_DELAY 0x3
+#define TASDEVICE_CMD_FIELD_W 0x4
+
#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5
#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64
#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10
@@ -803,8 +808,13 @@ static int tasdevice_process_block(void *context, unsigned char *data,
chn = idx - 1;
chnend = idx;
} else {
- chn = 0;
- chnend = tas_priv->ndev;
+ if (tas_priv->isspi) {
+ chn = tas_priv->index;
+ chnend = chn + 1;
+ } else {
+ chn = 0;
+ chnend = tas_priv->ndev;
+ }
}
for (; chn < chnend; chn++) {
@@ -896,7 +906,7 @@ static int tasdevice_process_block(void *context, unsigned char *data,
is_err = true;
break;
}
- rc = tasdevice_dev_update_bits(tas_priv, chn,
+ rc = tas_priv->update_bits(tas_priv, chn,
TASDEVICE_REG(data[subblk_offset + 2],
data[subblk_offset + 3],
data[subblk_offset + 4]),
@@ -1461,7 +1471,7 @@ static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice,
goto end;
}
- ret = tasdevice_dev_bulk_read(tasdevice, chn,
+ ret = tasdevice->dev_bulk_read(tasdevice, chn,
TASDEVICE_REG(book, page, crc_data.offset),
nBuf1, crc_data.len);
if (ret < 0)
@@ -1511,7 +1521,7 @@ static int do_singlereg_checksum(struct tasdevice_priv *tasdevice,
in = check_yram(&crc_data, book, page, reg, 1);
if (!in)
goto end;
- ret = tasdevice_dev_read(tasdevice, chl,
+ ret = tasdevice->dev_read(tasdevice, chl,
TASDEVICE_REG(book, page, reg), &nData1);
if (ret < 0)
goto end;
@@ -1615,7 +1625,7 @@ static int tasdev_block_chksum(struct tasdevice_priv *tas_priv,
unsigned int nr_value;
int ret;
- ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG,
+ ret = tas_priv->dev_read(tas_priv, chn, TASDEVICE_CHECKSUM_REG,
&nr_value);
if (ret < 0) {
dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn);
@@ -2074,8 +2084,7 @@ int tas2781_load_calibration(void *context, char *file_name,
}
out:
- if (fw_entry)
- release_firmware(fw_entry);
+ release_firmware(fw_entry);
return ret;
}
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index b950914b7d48..9f4d965a1335 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -28,6 +28,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tas2781.h>
+#include <sound/tas2781-comlib-i2c.h>
#include <sound/tlv.h>
#include <sound/tas2563-tlv.h>
#include <sound/tas2781-tlv.h>
@@ -1422,10 +1423,150 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
nctrls < i ? nctrls : i);
}
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+/*
+ * This debugfs node is a bridge to the acoustic tuning application
+ * tool which can tune the chips' acoustic effect.
+ *
+ * package structure for PPC3 communications:
+ * Pkg len (1 byte)
+ * Pkg id (1 byte, 'r' or 'w')
+ * Dev id (1 byte, i2c address)
+ * Book id (1 byte)
+ * Page id (1 byte)
+ * Reg id (1 byte)
+ * switch (pkg id) {
+ * case 'w':
+ * 1 byte, length of data to read
+ * case 'r':
+ * data payload (1~128 bytes)
+ * }
+ */
+static ssize_t acoustic_ctl_read(struct file *file, char __user *to,
+ size_t count, loff_t *ppos)
+{
+ struct snd_soc_component *comp = file->private_data;
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ struct acoustic_data *p = &tas_priv->acou_data;
+ int ret = -1;
+
+ if (p->id == 'r' && p->len == count && count <= sizeof(*p))
+ ret = simple_read_from_buffer(to, count, ppos, p, p->len);
+ else
+ dev_err(tas_priv->dev, "Not ready for get.\n");
+ return ret;
+}
+
+static ssize_t acoustic_ctl_write(struct file *file,
+ const char __user *from, size_t count, loff_t *ppos)
+{
+ struct snd_soc_component *comp = file->private_data;
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ struct acoustic_data *p = &priv->acou_data;
+ unsigned int max_pkg_len = sizeof(*p);
+ unsigned char *src;
+ int j, len, reg, val;
+ unsigned short chn;
+ int ret = -1;
+
+ if (count > sizeof(*p)) {
+ dev_err(priv->dev, "count(%u) is larger than max(%u).\n",
+ (unsigned int)count, max_pkg_len);
+ return ret;
+ }
+
+ src = memdup_user(from, count);
+ if (IS_ERR(src))
+ return PTR_ERR(src);
+
+ if (src[0] > max_pkg_len && src[0] != count) {
+ dev_err(priv->dev, "pkg(%u), max(%u), count(%u) dismatch.\n",
+ src[0], max_pkg_len, (unsigned int)count);
+ ret = 0;
+ goto exit;
+ }
+
+ switch (src[1]) {
+ case 'r':
+ /* length of data to read */
+ len = src[6];
+ break;
+ case 'w':
+ /* Skip 6 bytes for package type and register address */
+ len = src[0] - 6;
+ break;
+ default:
+ dev_err(priv->dev, "%s Wrong code %02x.\n", __func__, src[1]);
+ ret = 0;
+ goto exit;
+ }
+
+ if (len < 1) {
+ dev_err(priv->dev, "pkg fmt invalid %02x.\n", len);
+ ret = 0;
+ goto exit;
+ }
+
+ for (j = 0; j < priv->ndev; j++)
+ if (src[2] == priv->tasdevice[j].dev_addr) {
+ chn = j;
+ break;
+ }
+ if (j >= priv->ndev) {
+ dev_err(priv->dev, "no such device 0x%02x.\n", src[2]);
+ ret = 0;
+ goto exit;
+ }
+
+ reg = TASDEVICE_REG(src[3], src[4], src[5]);
+
+ guard(mutex)(&priv->codec_lock);
+
+ if (src[1] == 'w') {
+ if (len > 1)
+ ret = tasdevice_dev_bulk_write(priv, chn, reg,
+ &src[6], len);
+ else
+ ret = tasdevice_dev_write(priv, chn, reg, src[6]);
+ } else {
+ struct acoustic_data *p = &priv->acou_data;
+
+ memcpy(p, src, 6);
+ if (len > 1) {
+ ret = tasdevice_dev_bulk_read(priv, chn, reg,
+ p->data, len);
+ } else {
+ ret = tasdevice_dev_read(priv, chn, reg, &val);
+ p->data[0] = val;
+ }
+ p->len = len + 6;
+ }
+
+ if (ret)
+ dev_err(priv->dev, "i2c communication error.\n");
+ else
+ ret = count;
+exit:
+ kfree(src);
+ return ret;
+}
+
+static const struct file_operations acoustic_ctl_fops = {
+ .open = simple_open,
+ .read = acoustic_ctl_read,
+ .write = acoustic_ctl_write,
+};
+#endif
+
static void tasdevice_fw_ready(const struct firmware *fmw,
void *context)
{
struct tasdevice_priv *tas_priv = context;
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+ struct snd_soc_component *comp = tas_priv->codec;
+ struct dentry *debugfs_root = comp->debugfs_root;
+ char *acoustic_debugfs_node;
+#endif
int ret = 0;
int i;
@@ -1499,14 +1640,24 @@ static void tasdevice_fw_ready(const struct firmware *fmw,
tasdevice_prmg_load(tas_priv, 0);
tas_priv->cur_prog = 0;
+
+#ifdef CONFIG_SND_SOC_TAS2781_ACOUST_I2C
+ if (tas_priv->name_prefix)
+ acoustic_debugfs_node = devm_kasprintf(tas_priv->dev,
+ GFP_KERNEL, "%s_acoustic_ctl", tas_priv->name_prefix);
+ else
+ acoustic_debugfs_node = devm_kstrdup(tas_priv->dev,
+ "acoustic_ctl", GFP_KERNEL);
+ debugfs_create_file(acoustic_debugfs_node, 0644, debugfs_root,
+ comp, &acoustic_ctl_fops);
+#endif
out:
if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) {
/* If DSP FW fail, DSP kcontrol won't be created. */
tasdevice_dsp_remove(tas_priv);
}
mutex_unlock(&tas_priv->codec_lock);
- if (fmw)
- release_firmware(fmw);
+ release_firmware(fmw);
}
static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index 6c6e7ae07d80..41d73f470f8b 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -718,6 +718,69 @@ static const struct regmap_config tas5721_regmap_config = {
.volatile_table = &tas571x_volatile_regs,
};
+static const struct snd_kcontrol_new tas5733_controls[] = {
+ /* MVOL LSB is ignored - see comments in tas571x_i2c_probe() */
+ SOC_SINGLE_TLV("Master Volume",
+ TAS571X_MVOL_REG, 1, 0x1ff, 1,
+ tas5717_volume_tlv),
+ SOC_DOUBLE_R_TLV("Speaker Volume",
+ TAS571X_CH1_VOL_REG, TAS571X_CH2_VOL_REG,
+ 1, 0x1ff, 1, tas5717_volume_tlv),
+ SOC_DOUBLE("Speaker Switch",
+ TAS571X_SOFT_MUTE_REG,
+ TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
+ 1, 1),
+
+ SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
+ TAS5717_CH1_LEFT_CH_MIX_REG,
+ TAS5717_CH1_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
+ TAS5717_CH2_LEFT_CH_MIX_REG,
+ TAS5717_CH2_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ /*
+ * The biquads are named according to the register names.
+ * Please note that TI's TAS57xx Graphical Development Environment
+ * tool names them different.
+ */
+ BIQUAD_COEFS("CH1 - Biquad 0", TAS5733_CH1_BQ0_REG),
+ BIQUAD_COEFS("CH1 - Biquad 1", TAS5733_CH1_BQ1_REG),
+ BIQUAD_COEFS("CH1 - Biquad 2", TAS5733_CH1_BQ2_REG),
+ BIQUAD_COEFS("CH1 - Biquad 3", TAS5733_CH1_BQ3_REG),
+ BIQUAD_COEFS("CH1 - Biquad 4", TAS5733_CH1_BQ4_REG),
+ BIQUAD_COEFS("CH1 - Biquad 5", TAS5733_CH1_BQ5_REG),
+ BIQUAD_COEFS("CH1 - Biquad 6", TAS5733_CH1_BQ6_REG),
+ BIQUAD_COEFS("CH1 - Biquad 7", TAS5733_CH1_BQ7_REG),
+ BIQUAD_COEFS("CH1 - Biquad 8", TAS5733_CH1_BQ8_REG),
+ BIQUAD_COEFS("CH1 - Biquad 9", TAS5733_CH1_BQ9_REG),
+ BIQUAD_COEFS("CH1 - Biquad 10", TAS5733_CH1_BQ10_REG),
+
+ BIQUAD_COEFS("CH2 - Biquad 0", TAS5733_CH2_BQ0_REG),
+ BIQUAD_COEFS("CH2 - Biquad 1", TAS5733_CH2_BQ1_REG),
+ BIQUAD_COEFS("CH2 - Biquad 2", TAS5733_CH2_BQ2_REG),
+ BIQUAD_COEFS("CH2 - Biquad 3", TAS5733_CH2_BQ3_REG),
+ BIQUAD_COEFS("CH2 - Biquad 4", TAS5733_CH2_BQ4_REG),
+ BIQUAD_COEFS("CH2 - Biquad 5", TAS5733_CH2_BQ5_REG),
+ BIQUAD_COEFS("CH2 - Biquad 6", TAS5733_CH2_BQ6_REG),
+ BIQUAD_COEFS("CH2 - Biquad 7", TAS5733_CH2_BQ7_REG),
+ BIQUAD_COEFS("CH2 - Biquad 8", TAS5733_CH2_BQ8_REG),
+ BIQUAD_COEFS("CH2 - Biquad 9", TAS5733_CH2_BQ9_REG),
+ BIQUAD_COEFS("CH2 - Biquad 10", TAS5733_CH2_BQ10_REG),
+
+ BIQUAD_COEFS("CH1 - Cross Biquad 0", TAS5733_CH1_CBQ0_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 1", TAS5733_CH1_CBQ1_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 2", TAS5733_CH1_CBQ2_REG),
+ BIQUAD_COEFS("CH1 - Cross Biquad 3", TAS5733_CH1_CBQ3_REG),
+
+ BIQUAD_COEFS("CH2 - Cross Biquad 0", TAS5733_CH2_CBQ0_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 1", TAS5733_CH2_CBQ1_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 2", TAS5733_CH2_CBQ2_REG),
+ BIQUAD_COEFS("CH2 - Cross Biquad 3", TAS5733_CH2_CBQ3_REG),
+};
+
static const char *const tas5733_supply_names[] = {
"AVDD",
"DVDD",
@@ -770,12 +833,62 @@ static const struct regmap_config tas5733_regmap_config = {
static const struct tas571x_chip tas5733_chip = {
.supply_names = tas5733_supply_names,
.num_supply_names = ARRAY_SIZE(tas5733_supply_names),
- .controls = tas5717_controls,
- .num_controls = ARRAY_SIZE(tas5717_controls),
+ .controls = tas5733_controls,
+ .num_controls = ARRAY_SIZE(tas5733_controls),
.regmap_config = &tas5733_regmap_config,
.vol_reg_size = 2,
};
+static const struct reg_default tas5753_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x41},
+ {TAS571X_ERR_STATUS_REG, 0x00},
+ {TAS571X_SYS_CTRL_1_REG, 0xa0},
+ {TAS571X_SDI_REG, 0x05},
+ {TAS571X_SYS_CTRL_2_REG, 0x40},
+ {TAS571X_SOFT_MUTE_REG, 0x00},
+ {TAS571X_MVOL_REG, 0x03ff},
+ {TAS571X_CH1_VOL_REG, 0x00c0},
+ {TAS571X_CH2_VOL_REG, 0x00c0},
+ {TAS571X_CH3_VOL_REG, 0x00c0},
+ {TAS571X_VOL_CFG_REG, 0xf0},
+ {TAS571X_MODULATION_LIMIT_REG, 0x01},
+ {TAS571X_IC_DELAY_CH1_REG, 0xac},
+ {TAS571X_IC_DELAY_CH2_REG, 0x54},
+ {TAS571X_IC_DELAY_CH3_REG, 0xac},
+ {TAS571X_IC_DELAY_CH4_REG, 0x54},
+ {TAS571X_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x57},
+ {TAS571X_INPUT_MUX_REG, 0x00017772},
+ {TAS571X_PWM_MUX_REG, 0x01021345},
+ {TAS5717_CH1_RIGHT_CH_MIX_REG, 0x00},
+ {TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000},
+ {TAS5717_CH2_LEFT_CH_MIX_REG, 0x00},
+ {TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000},
+};
+
+static const struct regmap_config tas5753_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5753_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5753_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
+};
+
+static const struct tas571x_chip tas5753_chip = {
+ .supply_names = tas5721_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5721_supply_names),
+ .controls = tas5733_controls,
+ .num_controls = ARRAY_SIZE(tas5733_controls),
+ .regmap_config = &tas5753_regmap_config,
+ .vol_reg_size = 2,
+};
+
static const struct tas571x_chip tas5721_chip = {
.supply_names = tas5721_supply_names,
.num_supply_names = ARRAY_SIZE(tas5721_supply_names),
@@ -944,6 +1057,7 @@ static const struct of_device_id tas571x_of_match[] __maybe_unused = {
{ .compatible = "ti,tas5719", .data = &tas5717_chip, },
{ .compatible = "ti,tas5721", .data = &tas5721_chip, },
{ .compatible = "ti,tas5733", .data = &tas5733_chip, },
+ { .compatible = "ti,tas5753", .data = &tas5753_chip, },
{ }
};
MODULE_DEVICE_TABLE(of, tas571x_of_match);
@@ -955,6 +1069,7 @@ static const struct i2c_device_id tas571x_i2c_id[] = {
{ "tas5719", (kernel_ulong_t) &tas5717_chip },
{ "tas5721", (kernel_ulong_t) &tas5721_chip },
{ "tas5733", (kernel_ulong_t) &tas5733_chip },
+ { "tas5753", (kernel_ulong_t) &tas5753_chip },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
index 5340d3bec31d..2b3eff4023b9 100644
--- a/sound/soc/codecs/tas571x.h
+++ b/sound/soc/codecs/tas571x.h
@@ -104,4 +104,38 @@
#define TAS5717_CH2_LEFT_CH_MIX_REG 0x76
#define TAS5717_CH2_RIGHT_CH_MIX_REG 0x77
+#define TAS5733_CH1_BQ0_REG 0x26
+#define TAS5733_CH1_BQ1_REG 0x27
+#define TAS5733_CH1_BQ2_REG 0x28
+#define TAS5733_CH1_BQ3_REG 0x29
+#define TAS5733_CH1_BQ4_REG 0x2a
+#define TAS5733_CH1_BQ5_REG 0x2b
+#define TAS5733_CH1_BQ6_REG 0x2c
+#define TAS5733_CH1_BQ7_REG 0x2d
+#define TAS5733_CH1_BQ8_REG 0x2e
+#define TAS5733_CH1_BQ9_REG 0x2f
+
+#define TAS5733_CH2_BQ0_REG 0x30
+#define TAS5733_CH2_BQ1_REG 0x31
+#define TAS5733_CH2_BQ2_REG 0x32
+#define TAS5733_CH2_BQ3_REG 0x33
+#define TAS5733_CH2_BQ4_REG 0x34
+#define TAS5733_CH2_BQ5_REG 0x35
+#define TAS5733_CH2_BQ6_REG 0x36
+#define TAS5733_CH2_BQ7_REG 0x37
+#define TAS5733_CH2_BQ8_REG 0x38
+#define TAS5733_CH2_BQ9_REG 0x39
+
+#define TAS5733_CH1_BQ10_REG 0x58
+#define TAS5733_CH1_CBQ0_REG 0x59
+#define TAS5733_CH1_CBQ1_REG 0x5a
+#define TAS5733_CH1_CBQ2_REG 0x5b
+#define TAS5733_CH1_CBQ3_REG 0x5c
+
+#define TAS5733_CH2_BQ10_REG 0x5d
+#define TAS5733_CH2_CBQ0_REG 0x5e
+#define TAS5733_CH2_CBQ1_REG 0x5f
+#define TAS5733_CH2_CBQ2_REG 0x60
+#define TAS5733_CH2_CBQ3_REG 0x61
+
#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c
index 191e067ed1c9..1035ba17dc5d 100644
--- a/sound/soc/codecs/tlv320adc3xxx.c
+++ b/sound/soc/codecs/tlv320adc3xxx.c
@@ -1015,10 +1015,10 @@ static int adc3xxx_gpio_direction_out(struct gpio_chip *chip,
* so we set the output mode and output value in the same call. Hence
* .set in practice does the same thing as .direction_out .
*/
-static void adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
+static int adc3xxx_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
- (void) adc3xxx_gpio_direction_out(chip, offset, value);
+ return adc3xxx_gpio_direction_out(chip, offset, value);
}
/* Even though we only support GPIO output for now, some GPIO clients
@@ -1052,7 +1052,7 @@ static const struct gpio_chip adc3xxx_gpio_chip = {
.owner = THIS_MODULE,
.request = adc3xxx_gpio_request,
.direction_output = adc3xxx_gpio_direction_out,
- .set = adc3xxx_gpio_set,
+ .set_rv = adc3xxx_gpio_set,
.get = adc3xxx_gpio_get,
.can_sleep = 1,
};
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 54ea4bc58c27..7399080f8580 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -9,27 +9,26 @@
* Based on sound/soc/codecs/wm8974 and TI driver for kernel 2.6.27.
*/
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/cdev.h>
-#include <linux/slab.h>
#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/of_clk.h>
+#include <linux/pm.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
-#include <sound/tlv320aic32x4.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
+#include <sound/tlv320aic32x4.h>
#include "tlv320aic32x4.h"
@@ -38,7 +37,7 @@ struct aic32x4_priv {
u32 power_cfg;
u32 micpga_routing;
bool swapdacs;
- int rstn_gpio;
+ struct gpio_desc *rstn_gpio;
const char *mclk_name;
struct regulator *supply_ldo;
@@ -1236,7 +1235,14 @@ static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
- aic32x4->rstn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
+ /* Assert reset using GPIOD_OUT_HIGH, because reset is GPIO_ACTIVE_LOW */
+ aic32x4->rstn_gpio = devm_gpiod_get_optional(aic32x4->dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(aic32x4->rstn_gpio)) {
+ return dev_err_probe(aic32x4->dev, PTR_ERR(aic32x4->rstn_gpio),
+ "Failed to get reset gpio\n");
+ } else {
+ gpiod_set_consumer_name(aic32x4->rstn_gpio, "tlv320aic32x4_rstn");
+ }
if (of_property_read_u32_array(np, "aic32x4-gpio-func",
aic32x4_setup->gpio_func, 5) >= 0)
@@ -1346,7 +1352,6 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap,
enum aic32x4_type type)
{
struct aic32x4_priv *aic32x4;
- struct aic32x4_pdata *pdata = dev->platform_data;
struct device_node *np = dev->of_node;
int ret;
@@ -1363,13 +1368,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap,
dev_set_drvdata(dev, aic32x4);
- if (pdata) {
- aic32x4->power_cfg = pdata->power_cfg;
- aic32x4->swapdacs = pdata->swapdacs;
- aic32x4->micpga_routing = pdata->micpga_routing;
- aic32x4->rstn_gpio = pdata->rstn_gpio;
- aic32x4->mclk_name = "mclk";
- } else if (np) {
+ if (np) {
ret = aic32x4_parse_dt(aic32x4, np);
if (ret) {
dev_err(dev, "Failed to parse DT node\n");
@@ -1379,26 +1378,20 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap,
aic32x4->power_cfg = 0;
aic32x4->swapdacs = false;
aic32x4->micpga_routing = 0;
- aic32x4->rstn_gpio = -1;
+ aic32x4->rstn_gpio = NULL;
aic32x4->mclk_name = "mclk";
}
- if (gpio_is_valid(aic32x4->rstn_gpio)) {
- ret = devm_gpio_request_one(dev, aic32x4->rstn_gpio,
- GPIOF_OUT_INIT_LOW, "tlv320aic32x4 rstn");
- if (ret != 0)
- return ret;
- }
-
ret = aic32x4_setup_regulators(dev, aic32x4);
if (ret) {
dev_err(dev, "Failed to setup regulators\n");
return ret;
}
- if (gpio_is_valid(aic32x4->rstn_gpio)) {
+ if (aic32x4->rstn_gpio) {
ndelay(10);
- gpio_set_value_cansleep(aic32x4->rstn_gpio, 1);
+ /* deassert reset */
+ gpiod_set_value_cansleep(aic32x4->rstn_gpio, 0);
mdelay(1);
}
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 56e795a00e22..f1649df19738 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1818,10 +1818,8 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(aic3x->supplies),
aic3x->supplies);
- if (ret) {
- dev_err(dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request supplies\n");
aic3x_configure_ocmv(dev, aic3x);
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index b5472fa1bdda..38cc000891ea 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -7,19 +7,17 @@
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*/
-#include <linux/module.h>
-#include <linux/errno.h>
#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
-#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <sound/tpa6130a2-plat.h>
#include <sound/soc.h>
#include <sound/tlv.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/regmap.h>
#include "tpa6130a2.h"
@@ -33,7 +31,7 @@ struct tpa6130a2_data {
struct device *dev;
struct regmap *regmap;
struct regulator *supply;
- int power_gpio;
+ struct gpio_desc *power_gpio;
enum tpa_model id;
};
@@ -49,8 +47,7 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
return ret;
}
/* Power on */
- if (data->power_gpio >= 0)
- gpio_set_value(data->power_gpio, 1);
+ gpiod_set_value(data->power_gpio, 1);
/* Sync registers */
regcache_cache_only(data->regmap, false);
@@ -59,8 +56,7 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
dev_err(data->dev,
"Failed to sync registers: %d\n", ret);
regcache_cache_only(data->regmap, true);
- if (data->power_gpio >= 0)
- gpio_set_value(data->power_gpio, 0);
+ gpiod_set_value(data->power_gpio, 0);
ret2 = regulator_disable(data->supply);
if (ret2 != 0)
dev_err(data->dev,
@@ -76,8 +72,7 @@ static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
regcache_cache_only(data->regmap, true);
/* Power off */
- if (data->power_gpio >= 0)
- gpio_set_value(data->power_gpio, 0);
+ gpiod_set_value(data->power_gpio, 0);
ret = regulator_disable(data->supply);
if (ret != 0) {
@@ -209,18 +204,10 @@ static const struct regmap_config tpa6130a2_regmap_config = {
.cache_type = REGCACHE_RBTREE,
};
-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 char *regulator;
unsigned int version;
@@ -238,10 +225,13 @@ static int tpa6130a2_probe(struct i2c_client *client)
if (IS_ERR(data->regmap))
return PTR_ERR(data->regmap);
- if (pdata) {
- data->power_gpio = pdata->power_gpio;
- } else if (np) {
- data->power_gpio = of_get_named_gpio(np, "power-gpio", 0);
+ if (np) {
+ data->power_gpio = devm_gpiod_get_optional(dev, "power", GPIOD_OUT_LOW);
+ if (IS_ERR(data->power_gpio)) {
+ return dev_err_probe(dev, PTR_ERR(data->power_gpio),
+ "Failed to request power GPIO\n");
+ }
+ gpiod_set_consumer_name(data->power_gpio, "tpa6130a2 enable");
} else {
dev_err(dev, "Platform data not set\n");
dump_stack();
@@ -252,17 +242,6 @@ static int tpa6130a2_probe(struct i2c_client *client)
data->id = (uintptr_t)i2c_get_match_data(client);
- if (data->power_gpio >= 0) {
- ret = devm_gpio_request(dev, data->power_gpio,
- "tpa6130a2 enable");
- if (ret < 0) {
- dev_err(dev, "Failed to request power GPIO (%d)\n",
- data->power_gpio);
- return ret;
- }
- gpio_direction_output(data->power_gpio, 0);
- }
-
switch (data->id) {
default:
dev_warn(dev, "Unknown TPA model (%d). Assuming 6130A2\n",
@@ -318,7 +297,6 @@ static struct i2c_driver tpa6130a2_i2c_driver = {
.of_match_table = of_match_ptr(tpa6130a2_of_match),
},
.probe = tpa6130a2_probe,
- .id_table = tpa6130a2_id,
};
module_i2c_driver(tpa6130a2_i2c_driver);
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 609886461805..92194579e15b 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -5,28 +5,25 @@
* Author: Steve Sakoman, <steve@sakoman.com>
*/
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/mfd/twl.h>
+#include <linux/mfd/twl4030-audio.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
+#include <linux/of.h>
#include <linux/pm.h>
-#include <linux/i2c.h>
#include <linux/platform_device.h>
-#include <linux/of.h>
-#include <linux/of_gpio.h>
-#include <linux/mfd/twl.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
#include <sound/core.h>
+#include <sound/initval.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
-#include <sound/initval.h>
#include <sound/tlv.h>
-/* Register descriptions are here */
-#include <linux/mfd/twl4030-audio.h>
-
/* TWL4030 PMBR1 Register */
#define TWL4030_PMBR1_REG 0x0D
/* TWL4030 PMBR1 Register GPIO6 mux bits */
@@ -39,7 +36,7 @@ struct twl4030_board_params {
unsigned int ramp_delay_value;
unsigned int offset_cncl_path;
unsigned int hs_extmute:1;
- int hs_extmute_gpio;
+ struct gpio_desc *hs_extmute_gpio;
};
/* codec private data */
@@ -213,8 +210,7 @@ twl4030_get_board_param_values(struct twl4030_board_params *board_params,
if (!of_property_read_u32(node, "ti,hs_extmute", &value))
board_params->hs_extmute = value;
- board_params->hs_extmute_gpio = of_get_named_gpio(node, "ti,hs_extmute_gpio", 0);
- if (gpio_is_valid(board_params->hs_extmute_gpio))
+ if (of_property_present(node, "ti,hs_extmute_gpio"))
board_params->hs_extmute = 1;
}
@@ -242,7 +238,7 @@ twl4030_get_board_params(struct snd_soc_component *component)
return board_params;
}
-static void twl4030_init_chip(struct snd_soc_component *component)
+static int twl4030_init_chip(struct snd_soc_component *component)
{
struct twl4030_board_params *board_params;
struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
@@ -252,24 +248,20 @@ static void twl4030_init_chip(struct snd_soc_component *component)
board_params = twl4030_get_board_params(component);
if (board_params && board_params->hs_extmute) {
- if (gpio_is_valid(board_params->hs_extmute_gpio)) {
- int ret;
-
- if (!board_params->hs_extmute_gpio)
- dev_warn(component->dev,
- "Extmute GPIO is 0 is this correct?\n");
-
- ret = gpio_request_one(board_params->hs_extmute_gpio,
- GPIOF_OUT_INIT_LOW,
- "hs_extmute");
- if (ret) {
- dev_err(component->dev,
- "Failed to get hs_extmute GPIO\n");
- board_params->hs_extmute_gpio = -1;
- }
+ board_params->hs_extmute_gpio = devm_gpiod_get_optional(component->dev,
+ "ti,hs_extmute",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(board_params->hs_extmute_gpio))
+ return dev_err_probe(component->dev, PTR_ERR(board_params->hs_extmute_gpio),
+ "Failed to get hs_extmute GPIO\n");
+
+ if (board_params->hs_extmute_gpio) {
+ gpiod_set_consumer_name(board_params->hs_extmute_gpio, "hs_extmute");
} else {
u8 pin_mux;
+ dev_info(component->dev, "use TWL4030 GPIO6\n");
+
/* Set TWL4030 GPIO6 as EXTMUTE signal */
twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
TWL4030_PMBR1_REG);
@@ -297,7 +289,7 @@ static void twl4030_init_chip(struct snd_soc_component *component)
/* Machine dependent setup */
if (!board_params)
- return;
+ return 0;
twl4030->board_params = board_params;
@@ -332,6 +324,8 @@ static void twl4030_init_chip(struct snd_soc_component *component)
TWL4030_CNCL_OFFSET_START));
twl4030_codec_enable(component, 0);
+
+ return 0;
}
static void twl4030_apll_enable(struct snd_soc_component *component, int enable)
@@ -714,8 +708,8 @@ static void headset_ramp(struct snd_soc_component *component, int ramp)
/* Enable external mute control, this dramatically reduces
* the pop-noise */
if (board_params && board_params->hs_extmute) {
- if (gpio_is_valid(board_params->hs_extmute_gpio)) {
- gpio_set_value(board_params->hs_extmute_gpio, 1);
+ if (board_params->hs_extmute_gpio) {
+ gpiod_set_value(board_params->hs_extmute_gpio, 1);
} else {
hs_pop |= TWL4030_EXTMUTE;
twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
@@ -750,8 +744,8 @@ static void headset_ramp(struct snd_soc_component *component, int ramp)
/* Disable external mute */
if (board_params && board_params->hs_extmute) {
- if (gpio_is_valid(board_params->hs_extmute_gpio)) {
- gpio_set_value(board_params->hs_extmute_gpio, 0);
+ if (board_params->hs_extmute_gpio) {
+ gpiod_set_value(board_params->hs_extmute_gpio, 0);
} else {
hs_pop &= ~TWL4030_EXTMUTE;
twl4030_write(component, TWL4030_REG_HS_POPN_SET, hs_pop);
@@ -2168,24 +2162,11 @@ static int twl4030_soc_probe(struct snd_soc_component *component)
/* Set the defaults, and power up the codec */
twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
- twl4030_init_chip(component);
-
- return 0;
-}
-
-static void twl4030_soc_remove(struct snd_soc_component *component)
-{
- struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
- struct twl4030_board_params *board_params = twl4030->board_params;
-
- if (board_params && board_params->hs_extmute &&
- gpio_is_valid(board_params->hs_extmute_gpio))
- gpio_free(board_params->hs_extmute_gpio);
+ return twl4030_init_chip(component);
}
static const struct snd_soc_component_driver soc_component_dev_twl4030 = {
.probe = twl4030_soc_probe,
- .remove = twl4030_soc_remove,
.read = twl4030_read,
.write = twl4030_write,
.set_bias_level = twl4030_set_bias_level,
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index d589a212b768..26ebcdadeb7d 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -825,7 +825,6 @@ static int wcd_mbhc_initialise(struct wcd_mbhc *mbhc)
mutex_unlock(&mbhc->lock);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return 0;
@@ -1260,7 +1259,7 @@ correct_plug_type:
if (pt_gnd_mic_swap_cnt == mbhc->swap_thr) {
/* US_EU gpio present, flip switch */
if (mbhc->cfg->swap_gnd_mic) {
- if (mbhc->cfg->swap_gnd_mic(component, true))
+ if (mbhc->cfg->swap_gnd_mic(component))
continue;
}
}
@@ -1319,7 +1318,6 @@ exit:
if (mbhc->mbhc_cb->hph_pull_down_ctrl)
mbhc->mbhc_cb->hph_pull_down_ctrl(component, true);
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
}
diff --git a/sound/soc/codecs/wcd-mbhc-v2.h b/sound/soc/codecs/wcd-mbhc-v2.h
index b977e8f87d7c..a5d52b9643f5 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.h
+++ b/sound/soc/codecs/wcd-mbhc-v2.h
@@ -194,7 +194,7 @@ struct wcd_mbhc_config {
int num_btn;
bool mono_stero_detection;
bool typec_analog_mux;
- bool (*swap_gnd_mic)(struct snd_soc_component *component, bool active);
+ bool (*swap_gnd_mic)(struct snd_soc_component *component);
bool hs_ext_micbias;
bool gnd_det_en;
uint32_t linein_th;
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 7cef43bb2a88..1c050b8c19de 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -17,7 +17,7 @@
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <sound/tlv.h>
@@ -312,7 +312,6 @@ struct wcd9335_codec {
u32 num_rx_port;
u32 num_tx_port;
- int sido_input_src;
enum wcd9335_sido_voltage sido_voltage;
struct wcd_slim_codec_dai_data dai[NUM_CODEC_DAIS];
@@ -331,8 +330,7 @@ struct wcd9335_codec {
int comp_enabled[COMPANDER_MAX];
int intr1;
- int reset_gpio;
- struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY];
+ struct gpio_desc *reset_gpio;
unsigned int rx_port_value[WCD9335_RX_MAX];
unsigned int tx_port_value[WCD9335_TX_MAX];
@@ -355,6 +353,10 @@ struct wcd9335_irq {
char *name;
};
+static const char * const wcd9335_supplies[] = {
+ "vdd-buck", "vdd-buck-sido", "vdd-tx", "vdd-rx", "vdd-io",
+};
+
static const struct wcd9335_slim_ch wcd9335_tx_chs[WCD9335_TX_MAX] = {
WCD9335_SLIM_TX_CH(0),
WCD9335_SLIM_TX_CH(1),
@@ -4722,8 +4724,6 @@ static const struct snd_soc_dapm_widget wcd9335_dapm_widgets[] = {
static void wcd9335_enable_sido_buck(struct snd_soc_component *component)
{
- struct wcd9335_codec *wcd = dev_get_drvdata(component->dev);
-
snd_soc_component_update_bits(component, WCD9335_ANA_RCO,
WCD9335_ANA_RCO_BG_EN_MASK,
WCD9335_ANA_RCO_BG_ENABLE);
@@ -4737,7 +4737,6 @@ static void wcd9335_enable_sido_buck(struct snd_soc_component *component)
WCD9335_ANA_BUCK_CTL_VOUT_D_VREF_EXT);
/* 100us sleep needed after VREF settings */
usleep_range(100, 110);
- wcd->sido_input_src = SIDO_SOURCE_RCO_BG;
}
static int wcd9335_enable_efuse_sensing(struct snd_soc_component *comp)
@@ -4868,7 +4867,6 @@ static int wcd9335_probe(struct wcd9335_codec *wcd)
memcpy(wcd->rx_chs, wcd9335_rx_chs, sizeof(wcd9335_rx_chs));
memcpy(wcd->tx_chs, wcd9335_tx_chs, sizeof(wcd9335_tx_chs));
- wcd->sido_input_src = SIDO_SOURCE_INTERNAL;
wcd->sido_voltage = SIDO_VOLTAGE_NOMINAL_MV;
return devm_snd_soc_register_component(dev, &wcd9335_component_drv,
@@ -4975,12 +4973,11 @@ static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = {
static int wcd9335_parse_dt(struct wcd9335_codec *wcd)
{
struct device *dev = wcd->dev;
- struct device_node *np = dev->of_node;
int ret;
- wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0);
- if (wcd->reset_gpio < 0)
- return dev_err_probe(dev, wcd->reset_gpio, "Reset GPIO missing from DT\n");
+ wcd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd->reset_gpio), "Reset GPIO missing from DT\n");
wcd->mclk = devm_clk_get(dev, "mclk");
if (IS_ERR(wcd->mclk))
@@ -4990,30 +4987,16 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd)
if (IS_ERR(wcd->native_clk))
return dev_err_probe(dev, PTR_ERR(wcd->native_clk), "slimbus clock not found\n");
- wcd->supplies[0].supply = "vdd-buck";
- wcd->supplies[1].supply = "vdd-buck-sido";
- wcd->supplies[2].supply = "vdd-tx";
- wcd->supplies[3].supply = "vdd-rx";
- wcd->supplies[4].supply = "vdd-io";
-
- ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies);
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd9335_supplies),
+ wcd9335_supplies);
if (ret)
- return dev_err_probe(dev, ret, "Failed to get supplies\n");
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
return 0;
}
static int wcd9335_power_on_reset(struct wcd9335_codec *wcd)
{
- struct device *dev = wcd->dev;
- int ret;
-
- ret = regulator_bulk_enable(WCD9335_MAX_SUPPLY, wcd->supplies);
- if (ret) {
- dev_err(dev, "Failed to get supplies: err = %d\n", ret);
- return ret;
- }
-
/*
* For WCD9335, it takes about 600us for the Vout_A and
* Vout_D to be ready after BUCK_SIDO is powered up.
@@ -5023,9 +5006,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd)
*/
usleep_range(600, 650);
- gpio_direction_output(wcd->reset_gpio, 0);
+ gpiod_set_value(wcd->reset_gpio, 1);
msleep(20);
- gpio_set_value(wcd->reset_gpio, 1);
+ gpiod_set_value(wcd->reset_gpio, 0);
msleep(20);
return 0;
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index fa69817c97ea..1bb7e1dc7e6b 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -537,8 +537,6 @@ struct wcd934x_codec {
int rate;
u32 version;
u32 hph_mode;
- int num_rx_port;
- int num_tx_port;
u32 tx_port_value[WCD934X_TX_MAX];
u32 rx_port_value[WCD934X_RX_MAX];
int sido_input_src;
@@ -1928,13 +1926,11 @@ static int wcd934x_set_channel_map(struct snd_soc_dai *dai,
return -EINVAL;
}
- wcd->num_rx_port = rx_num;
for (i = 0; i < rx_num; i++) {
wcd->rx_chs[i].ch_num = rx_slot[i];
INIT_LIST_HEAD(&wcd->rx_chs[i].list);
}
- wcd->num_tx_port = tx_num;
for (i = 0; i < tx_num; i++) {
wcd->tx_chs[i].ch_num = tx_slot[i];
INIT_LIST_HEAD(&wcd->tx_chs[i].list);
diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c
index dd2045a5d26d..3b0a8cc314e0 100644
--- a/sound/soc/codecs/wcd937x.c
+++ b/sound/soc/codecs/wcd937x.c
@@ -24,10 +24,8 @@
#include "wcd-mbhc-v2.h"
#include "wcd937x.h"
-enum {
- CHIPID_WCD9370 = 0,
- CHIPID_WCD9375 = 5,
-};
+#define CHIPID_WCD9370 0x0
+#define CHIPID_WCD9375 0x5
/* Z value defined in milliohm */
#define WCD937X_ZDET_VAL_32 (32000)
@@ -88,10 +86,7 @@ struct wcd937x_priv {
struct wcd_mbhc_intr intr_ids;
struct wcd_clsh_ctrl *clsh_info;
struct irq_domain *virq;
- struct regmap_irq_chip *wcd_regmap_irq_chip;
struct regmap_irq_chip_data *irq_chip;
- struct regulator_bulk_data supplies[WCD937X_MAX_BULK_SUPPLY];
- struct regulator *buck_supply;
struct snd_soc_jack *jack;
unsigned long status_mask;
s32 micb_ref[WCD937X_MAX_MICBIAS];
@@ -114,6 +109,10 @@ struct wcd937x_priv {
atomic_t ana_clk_count;
};
+static const char * const wcd937x_supplies[] = {
+ "vdd-rxtx", "vdd-px", "vdd-mic-bias", "vdd-buck",
+};
+
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
@@ -2656,7 +2655,7 @@ static void wcd937x_dt_parse_micbias_info(struct device *dev, struct wcd937x_pri
dev_warn(dev, "Micbias3 DT property not found\n");
}
-static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component, bool active)
+static bool wcd937x_swap_gnd_mic(struct snd_soc_component *component)
{
int value;
struct wcd937x_priv *wcd937x;
@@ -2935,20 +2934,10 @@ static int wcd937x_probe(struct platform_device *pdev)
cfg = &wcd937x->mbhc_cfg;
cfg->swap_gnd_mic = wcd937x_swap_gnd_mic;
- wcd937x->supplies[0].supply = "vdd-rxtx";
- wcd937x->supplies[1].supply = "vdd-px";
- wcd937x->supplies[2].supply = "vdd-mic-bias";
- wcd937x->supplies[3].supply = "vdd-buck";
-
- ret = devm_regulator_bulk_get(dev, WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies);
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd937x_supplies),
+ wcd937x_supplies);
if (ret)
- return dev_err_probe(dev, ret, "Failed to get supplies\n");
-
- ret = regulator_bulk_enable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies);
- if (ret) {
- regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies);
- return dev_err_probe(dev, ret, "Failed to enable supplies\n");
- }
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
wcd937x_dt_parse_micbias_info(dev, wcd937x);
@@ -2965,13 +2954,13 @@ static int wcd937x_probe(struct platform_device *pdev)
ret = wcd937x_add_slave_components(wcd937x, dev, &match);
if (ret)
- goto err_disable_regulators;
+ return ret;
wcd937x_reset(wcd937x);
ret = component_master_add_with_match(dev, &wcd937x_comp_ops, match);
if (ret)
- goto err_disable_regulators;
+ return ret;
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
@@ -2981,27 +2970,17 @@ static int wcd937x_probe(struct platform_device *pdev)
pm_runtime_idle(dev);
return 0;
-
-err_disable_regulators:
- regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies);
- regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies);
-
- return ret;
}
static void wcd937x_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct wcd937x_priv *wcd937x = dev_get_drvdata(dev);
component_master_del(&pdev->dev, &wcd937x_comp_ops);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
-
- regulator_bulk_disable(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies);
- regulator_bulk_free(WCD937X_MAX_BULK_SUPPLY, wcd937x->supplies);
}
#if defined(CONFIG_OF)
diff --git a/sound/soc/codecs/wcd937x.h b/sound/soc/codecs/wcd937x.h
index 4ef57c496c37..3ab21bb5846e 100644
--- a/sound/soc/codecs/wcd937x.h
+++ b/sound/soc/codecs/wcd937x.h
@@ -487,7 +487,6 @@
#define WCD937X_MAX_REGISTER (WCD937X_DIGITAL_EFUSE_REG_31)
#define WCD937X_MAX_MICBIAS 3
-#define WCD937X_MAX_BULK_SUPPLY 4
#define WCD937X_MAX_SWR_CH_IDS 15
#define WCD937X_SWRM_CH_MASK(ch_idx) BIT(ch_idx - 1)
diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c
index cabddadc90ef..e822cc145250 100644
--- a/sound/soc/codecs/wcd938x-sdw.c
+++ b/sound/soc/codecs/wcd938x-sdw.c
@@ -1225,7 +1225,7 @@ static int wcd9380_probe(struct sdw_slave *pdev,
if (!wcd)
return -ENOMEM;
- /**
+ /*
* Port map index starts with 0, however the data port for this codec
* are from index 1
*/
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index 1ae498c32391..711f373ece24 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -11,7 +11,6 @@
#include <linux/pm_runtime.h>
#include <linux/component.h>
#include <sound/tlv.h>
-#include <linux/of_gpio.h>
#include <linux/of.h>
#include <sound/jack.h>
#include <sound/pcm.h>
@@ -19,14 +18,17 @@
#include <linux/regmap.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
+#include <linux/mux/consumer.h>
#include <linux/regulator/consumer.h>
#include "wcd-clsh-v2.h"
#include "wcd-mbhc-v2.h"
#include "wcd938x.h"
+#define CHIPID_WCD9380 0x0
+#define CHIPID_WCD9385 0x5
+
#define WCD938X_MAX_MICBIAS (4)
-#define WCD938X_MAX_SUPPLY (4)
#define WCD938X_MBHC_MAX_BUTTONS (8)
#define TX_ADC_MAX (4)
@@ -74,11 +76,6 @@
wcd938x_ear_pa_put_gain, tlv_array)
enum {
- WCD9380 = 0,
- WCD9385 = 5,
-};
-
-enum {
/* INTR_CTRL_INT_MASK_0 */
WCD938X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
WCD938X_IRQ_MBHC_BUTTON_RELEASE_DET,
@@ -159,9 +156,7 @@ struct wcd938x_priv {
struct wcd_mbhc_intr intr_ids;
struct wcd_clsh_ctrl *clsh_info;
struct irq_domain *virq;
- struct regmap_irq_chip *wcd_regmap_irq_chip;
struct regmap_irq_chip_data *irq_chip;
- struct regulator_bulk_data supplies[WCD938X_MAX_SUPPLY];
struct snd_soc_jack *jack;
unsigned long status_mask;
s32 micb_ref[WCD938X_MAX_MICBIAS];
@@ -170,9 +165,10 @@ struct wcd938x_priv {
u32 tx_mode[TX_ADC_MAX];
int flyback_cur_det_disable;
int ear_rx_path;
- int variant;
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
struct gpio_desc *us_euro_gpio;
+ struct mux_control *us_euro_mux;
+ unsigned int mux_state;
u32 micb1_mv;
u32 micb2_mv;
u32 micb3_mv;
@@ -183,6 +179,11 @@ struct wcd938x_priv {
bool comp1_enable;
bool comp2_enable;
bool ldoh;
+ bool mux_setup_done;
+};
+
+static const char * const wcd938x_supplies[] = {
+ "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias",
};
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
@@ -272,7 +273,7 @@ static const struct regmap_irq wcd938x_irqs[WCD938X_NUM_IRQS] = {
REGMAP_IRQ_REG(WCD938X_IRQ_HPHR_SURGE_DET_INT, 2, 0x08),
};
-static struct regmap_irq_chip wcd938x_regmap_irq_chip = {
+static const struct regmap_irq_chip wcd938x_regmap_irq_chip = {
.name = "wcd938x",
.irqs = wcd938x_irqs,
.num_irqs = ARRAY_SIZE(wcd938x_irqs),
@@ -3043,6 +3044,7 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev;
struct device *dev = component->dev;
unsigned long time_left;
+ unsigned int variant;
int ret, i;
time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
@@ -3058,9 +3060,9 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
if (ret < 0)
return ret;
- wcd938x->variant = snd_soc_component_read_field(component,
- WCD938X_DIGITAL_EFUSE_REG_0,
- WCD938X_ID_MASK);
+ variant = snd_soc_component_read_field(component,
+ WCD938X_DIGITAL_EFUSE_REG_0,
+ WCD938X_ID_MASK);
wcd938x->clsh_info = wcd_clsh_ctrl_alloc(component, WCD938X);
if (IS_ERR(wcd938x->clsh_info)) {
@@ -3114,24 +3116,24 @@ static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
disable_irq_nosync(wcd938x->hphl_pdm_wd_int);
disable_irq_nosync(wcd938x->aux_pdm_wd_int);
- switch (wcd938x->variant) {
- case WCD9380:
+ switch (variant) {
+ case CHIPID_WCD9380:
ret = snd_soc_add_component_controls(component, wcd9380_snd_controls,
ARRAY_SIZE(wcd9380_snd_controls));
if (ret < 0) {
dev_err(component->dev,
"%s: Failed to add snd ctrls for variant: %d\n",
- __func__, wcd938x->variant);
+ __func__, variant);
goto err_free_aux_pdm_wd_int;
}
break;
- case WCD9385:
+ case CHIPID_WCD9385:
ret = snd_soc_add_component_controls(component, wcd9385_snd_controls,
ARRAY_SIZE(wcd9385_snd_controls));
if (ret < 0) {
dev_err(component->dev,
"%s: Failed to add snd ctrls for variant: %d\n",
- __func__, wcd938x->variant);
+ __func__, variant);
goto err_free_aux_pdm_wd_int;
}
break;
@@ -3230,17 +3232,28 @@ static void wcd938x_dt_parse_micbias_info(struct device *dev, struct wcd938x_pri
dev_info(dev, "%s: Micbias4 DT property not found\n", __func__);
}
-static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component, bool active)
+static bool wcd938x_swap_gnd_mic(struct snd_soc_component *component)
{
- int value;
-
- struct wcd938x_priv *wcd938x;
+ struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ int ret;
- wcd938x = snd_soc_component_get_drvdata(component);
+ if (wcd938x->us_euro_mux) {
+ if (wcd938x->mux_setup_done)
+ mux_control_deselect(wcd938x->us_euro_mux);
- value = gpiod_get_value(wcd938x->us_euro_gpio);
+ ret = mux_control_try_select(wcd938x->us_euro_mux, !wcd938x->mux_state);
+ if (ret) {
+ dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret);
+ wcd938x->mux_setup_done = false;
+ return false;
+ }
+ wcd938x->mux_setup_done = true;
+ } else {
+ gpiod_set_value(wcd938x->us_euro_gpio, !wcd938x->mux_state);
+ }
- gpiod_set_value(wcd938x->us_euro_gpio, !value);
+ wcd938x->mux_state = !wcd938x->mux_state;
return true;
}
@@ -3251,33 +3264,37 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
struct wcd_mbhc_config *cfg = &wcd938x->mbhc_cfg;
int ret;
- wcd938x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
- if (wcd938x->reset_gpio < 0)
- return dev_err_probe(dev, wcd938x->reset_gpio,
+ wcd938x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd938x->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd938x->reset_gpio),
"Failed to get reset gpio\n");
- wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro",
- GPIOD_OUT_LOW);
- if (IS_ERR(wcd938x->us_euro_gpio))
- return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio),
- "us-euro swap Control GPIO not found\n");
+ if (of_property_present(dev->of_node, "mux-controls")) {
+ wcd938x->us_euro_mux = devm_mux_control_get(dev, NULL);
+ if (IS_ERR(wcd938x->us_euro_mux)) {
+ ret = PTR_ERR(wcd938x->us_euro_mux);
+ return dev_err_probe(dev, ret, "failed to get mux control\n");
+ }
- cfg->swap_gnd_mic = wcd938x_swap_gnd_mic;
+ ret = mux_control_try_select(wcd938x->us_euro_mux, wcd938x->mux_state);
+ if (ret) {
+ dev_err(dev, "Error (%d) Unable to select us/euro mux state\n", ret);
+ return ret;
+ }
+ wcd938x->mux_setup_done = true;
+ } else {
+ wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd938x->us_euro_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio),
+ "us-euro swap Control GPIO not found\n");
+ }
- wcd938x->supplies[0].supply = "vdd-rxtx";
- wcd938x->supplies[1].supply = "vdd-io";
- wcd938x->supplies[2].supply = "vdd-buck";
- wcd938x->supplies[3].supply = "vdd-mic-bias";
+ cfg->swap_gnd_mic = wcd938x_swap_gnd_mic;
- ret = regulator_bulk_get(dev, WCD938X_MAX_SUPPLY, wcd938x->supplies);
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd938x_supplies),
+ wcd938x_supplies);
if (ret)
- return dev_err_probe(dev, ret, "Failed to get supplies\n");
-
- ret = regulator_bulk_enable(WCD938X_MAX_SUPPLY, wcd938x->supplies);
- if (ret) {
- regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies);
- return dev_err_probe(dev, ret, "Failed to enable supplies\n");
- }
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
wcd938x_dt_parse_micbias_info(dev, wcd938x);
@@ -3297,10 +3314,10 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
static int wcd938x_reset(struct wcd938x_priv *wcd938x)
{
- gpio_direction_output(wcd938x->reset_gpio, 0);
+ gpiod_set_value(wcd938x->reset_gpio, 1);
/* 20us sleep required after pulling the reset gpio to LOW */
usleep_range(20, 30);
- gpio_set_value(wcd938x->reset_gpio, 1);
+ gpiod_set_value(wcd938x->reset_gpio, 0);
/* 20us sleep required after pulling the reset gpio to HIGH */
usleep_range(20, 30);
@@ -3541,13 +3558,13 @@ static int wcd938x_probe(struct platform_device *pdev)
ret = wcd938x_add_slave_components(wcd938x, dev, &match);
if (ret)
- goto err_disable_regulators;
+ return ret;
wcd938x_reset(wcd938x);
ret = component_master_add_with_match(dev, &wcd938x_comp_ops, match);
if (ret)
- goto err_disable_regulators;
+ return ret;
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
@@ -3557,12 +3574,6 @@ static int wcd938x_probe(struct platform_device *pdev)
pm_runtime_idle(dev);
return 0;
-
-err_disable_regulators:
- regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies);
- regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies);
-
- return ret;
}
static void wcd938x_remove(struct platform_device *pdev)
@@ -3576,8 +3587,8 @@ static void wcd938x_remove(struct platform_device *pdev)
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
- regulator_bulk_disable(WCD938X_MAX_SUPPLY, wcd938x->supplies);
- regulator_bulk_free(WCD938X_MAX_SUPPLY, wcd938x->supplies);
+ if (wcd938x->us_euro_mux && wcd938x->mux_setup_done)
+ mux_control_deselect(wcd938x->us_euro_mux);
}
#if defined(CONFIG_OF)
diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c
index 0a87a79772da..64f082e474c1 100644
--- a/sound/soc/codecs/wcd939x.c
+++ b/sound/soc/codecs/wcd939x.c
@@ -15,7 +15,6 @@
#include <linux/pm_runtime.h>
#include <linux/component.h>
#include <sound/tlv.h>
-#include <linux/of_gpio.h>
#include <linux/of_graph.h>
#include <linux/of.h>
#include <sound/jack.h>
@@ -33,11 +32,18 @@
#include "wcd939x.h"
#define WCD939X_MAX_MICBIAS (4)
-#define WCD939X_MAX_SUPPLY (4)
#define WCD939X_MBHC_MAX_BUTTONS (8)
#define TX_ADC_MAX (4)
#define WCD_MBHC_HS_V_MAX 1600
+#define CHIPID_WCD9390 0x0
+#define CHIPID_WCD9395 0x5
+
+/* Version major: 1.x */
+#define CHIPID_WCD939X_VER_MAJOR_1 0x0
+/* Version minor: x.1 */
+#define CHIPID_WCD939X_VER_MINOR_1 0x3
+
enum {
WCD939X_VERSION_1_0 = 0,
WCD939X_VERSION_1_1,
@@ -88,11 +94,6 @@ enum {
#define WCD939X_ANA_MBHC_ZDET_CONST (1018 * 1024)
enum {
- WCD9390 = 0,
- WCD9395 = 5,
-};
-
-enum {
/* INTR_CTRL_INT_MASK_0 */
WCD939X_IRQ_MBHC_BUTTON_PRESS_DET = 0,
WCD939X_IRQ_MBHC_BUTTON_RELEASE_DET,
@@ -191,9 +192,7 @@ struct wcd939x_priv {
struct wcd_mbhc_intr intr_ids;
struct wcd_clsh_ctrl *clsh_info;
struct irq_domain *virq;
- struct regmap_irq_chip *wcd_regmap_irq_chip;
struct regmap_irq_chip_data *irq_chip;
- struct regulator_bulk_data supplies[WCD939X_MAX_SUPPLY];
struct snd_soc_jack *jack;
unsigned long status_mask;
s32 micb_ref[WCD939X_MAX_MICBIAS];
@@ -201,7 +200,7 @@ struct wcd939x_priv {
u32 hph_mode;
u32 tx_mode[TX_ADC_MAX];
int variant;
- int reset_gpio;
+ struct gpio_desc *reset_gpio;
u32 micb1_mv;
u32 micb2_mv;
u32 micb3_mv;
@@ -214,6 +213,10 @@ struct wcd939x_priv {
bool ldoh;
};
+static const char * const wcd939x_supplies[] = {
+ "vdd-rxtx", "vdd-io", "vdd-buck", "vdd-mic-bias", "vdd-px",
+};
+
static const SNDRV_CTL_TLVD_DECLARE_DB_MINMAX(ear_pa_gain, 600, -1800);
static const DECLARE_TLV_DB_SCALE(line_gain, 0, 7, 1);
static const DECLARE_TLV_DB_SCALE(analog_gain, 0, 25, 1);
@@ -1483,7 +1486,7 @@ static int wcd939x_rx_hph_mode_put(struct snd_kcontrol *kcontrol,
if (mode_val == wcd939x->hph_mode)
return 0;
- if (wcd939x->variant == WCD9390) {
+ if (wcd939x->variant == CHIPID_WCD9390) {
switch (mode_val) {
case CLS_H_NORMAL:
case CLS_H_LP:
@@ -3065,7 +3068,7 @@ static int wcd939x_soc_codec_probe(struct snd_soc_component *component)
disable_irq_nosync(wcd939x->ear_pdm_wd_int);
switch (wcd939x->variant) {
- case WCD9390:
+ case CHIPID_WCD9390:
ret = snd_soc_add_component_controls(component, wcd9390_snd_controls,
ARRAY_SIZE(wcd9390_snd_controls));
if (ret < 0) {
@@ -3075,7 +3078,7 @@ static int wcd939x_soc_codec_probe(struct snd_soc_component *component)
goto err_free_ear_pdm_wd_int;
}
break;
- case WCD9395:
+ case CHIPID_WCD9395:
ret = snd_soc_add_component_controls(component, wcd9395_snd_controls,
ARRAY_SIZE(wcd9395_snd_controls));
if (ret < 0) {
@@ -3215,7 +3218,7 @@ static void wcd939x_dt_parse_micbias_info(struct device *dev, struct wcd939x_pri
}
#if IS_ENABLED(CONFIG_TYPEC)
-static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component, bool active)
+static bool wcd939x_swap_gnd_mic(struct snd_soc_component *component)
{
struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
@@ -3239,25 +3242,15 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device
#endif /* CONFIG_TYPEC */
int ret;
- wcd939x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
- if (wcd939x->reset_gpio < 0)
- return dev_err_probe(dev, wcd939x->reset_gpio,
+ wcd939x->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(wcd939x->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd939x->reset_gpio),
"Failed to get reset gpio\n");
- wcd939x->supplies[0].supply = "vdd-rxtx";
- wcd939x->supplies[1].supply = "vdd-io";
- wcd939x->supplies[2].supply = "vdd-buck";
- wcd939x->supplies[3].supply = "vdd-mic-bias";
-
- ret = regulator_bulk_get(dev, WCD939X_MAX_SUPPLY, wcd939x->supplies);
+ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd939x_supplies),
+ wcd939x_supplies);
if (ret)
- return dev_err_probe(dev, ret, "Failed to get supplies\n");
-
- ret = regulator_bulk_enable(WCD939X_MAX_SUPPLY, wcd939x->supplies);
- if (ret) {
- regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies);
- return dev_err_probe(dev, ret, "Failed to enable supplies\n");
- }
+ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n");
wcd939x_dt_parse_micbias_info(dev, wcd939x);
@@ -3290,10 +3283,10 @@ static int wcd939x_populate_dt_data(struct wcd939x_priv *wcd939x, struct device
static int wcd939x_reset(struct wcd939x_priv *wcd939x)
{
- gpio_direction_output(wcd939x->reset_gpio, 0);
+ gpiod_set_value(wcd939x->reset_gpio, 1);
/* 20us sleep required after pulling the reset gpio to LOW */
usleep_range(20, 30);
- gpio_set_value(wcd939x->reset_gpio, 1);
+ gpiod_set_value(wcd939x->reset_gpio, 0);
/* 20us sleep required after pulling the reset gpio to HIGH */
usleep_range(20, 30);
@@ -3461,8 +3454,8 @@ static int wcd939x_bind(struct device *dev)
regmap_read(wcd939x->regmap, WCD939X_DIGITAL_CHIP_ID1, &id1);
regmap_read(wcd939x->regmap, WCD939X_EAR_STATUS_REG_1, &status1);
- if (id1 == 0)
- version = ((status1 & 0x3) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0);
+ if (id1 == CHIPID_WCD939X_VER_MAJOR_1)
+ version = ((status1 & CHIPID_WCD939X_VER_MINOR_1) ? WCD939X_VERSION_1_1 : WCD939X_VERSION_1_0);
else
version = WCD939X_VERSION_2_0;
@@ -3629,17 +3622,17 @@ static int wcd939x_probe(struct platform_device *pdev)
ret = wcd939x_add_typec(wcd939x, dev);
if (ret)
- goto err_disable_regulators;
+ return ret;
ret = wcd939x_add_slave_components(wcd939x, dev, &match);
if (ret)
- goto err_disable_regulators;
+ return ret;
wcd939x_reset(wcd939x);
ret = component_master_add_with_match(dev, &wcd939x_comp_ops, match);
if (ret)
- goto err_disable_regulators;
+ return ret;
pm_runtime_set_autosuspend_delay(dev, 1000);
pm_runtime_use_autosuspend(dev);
@@ -3649,27 +3642,17 @@ static int wcd939x_probe(struct platform_device *pdev)
pm_runtime_idle(dev);
return 0;
-
-err_disable_regulators:
- regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies);
- regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies);
-
- return ret;
}
static void wcd939x_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct wcd939x_priv *wcd939x = dev_get_drvdata(dev);
component_master_del(dev, &wcd939x_comp_ops);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
pm_runtime_dont_use_autosuspend(dev);
-
- regulator_bulk_disable(WCD939X_MAX_SUPPLY, wcd939x->supplies);
- regulator_bulk_free(WCD939X_MAX_SUPPLY, wcd939x->supplies);
}
#if defined(CONFIG_OF)
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index d9e5762324df..fb5ed4ba7f60 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2236,12 +2236,14 @@ static irqreturn_t wm5100_edge_irq(int irq, void *data)
}
#ifdef CONFIG_GPIOLIB
-static void wm5100_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int wm5100_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct wm5100_priv *wm5100 = gpiochip_get_data(chip);
- regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
- WM5100_GP1_LVL, !!value << WM5100_GP1_LVL_SHIFT);
+ return regmap_update_bits(wm5100->regmap, WM5100_GPIO_CTRL_1 + offset,
+ WM5100_GP1_LVL,
+ !!value << WM5100_GP1_LVL_SHIFT);
}
static int wm5100_gpio_direction_out(struct gpio_chip *chip,
@@ -2288,7 +2290,7 @@ static const struct gpio_chip wm5100_template_chip = {
.label = "wm5100",
.owner = THIS_MODULE,
.direction_output = wm5100_gpio_direction_out,
- .set = wm5100_gpio_set,
+ .set_rv = wm5100_gpio_set,
.direction_input = wm5100_gpio_direction_in,
.get = wm5100_gpio_get,
.can_sleep = 1,
diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c
index 403e513f3fa8..6b1a7450b0ac 100644
--- a/sound/soc/codecs/wm8524.c
+++ b/sound/soc/codecs/wm8524.c
@@ -21,7 +21,7 @@
#include <sound/soc.h>
#include <sound/initval.h>
-#define WM8524_NUM_RATES 7
+#define WM8524_NUM_RATES 12
/* codec private data */
struct wm8524_priv {
@@ -46,7 +46,7 @@ static const struct snd_soc_dapm_route wm8524_dapm_routes[] = {
static const struct {
int value;
int ratio;
-} lrclk_ratios[WM8524_NUM_RATES] = {
+} lrclk_ratios[] = {
{ 1, 128 },
{ 2, 192 },
{ 3, 256 },
@@ -63,17 +63,12 @@ static int wm8524_startup(struct snd_pcm_substream *substream,
struct wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
/* The set of sample rates that can be supported depends on the
- * MCLK supplied to the CODEC - enforce this.
+ * MCLK supplied to the CODEC.
*/
- if (!wm8524->sysclk) {
- dev_err(component->dev,
- "No MCLK configured, call set_sysclk() on init\n");
- return -EINVAL;
- }
-
- snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &wm8524->rate_constraint);
+ if (wm8524->sysclk)
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &wm8524->rate_constraint);
gpiod_set_value_cansleep(wm8524->mute, 1);
@@ -97,9 +92,11 @@ static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai,
unsigned int val;
int i, j = 0;
+ wm8524->rate_constraint.count = 0;
wm8524->sysclk = freq;
+ if (!wm8524->sysclk)
+ return 0;
- wm8524->rate_constraint.count = 0;
for (i = 0; i < ARRAY_SIZE(lrclk_ratios); i++) {
val = freq / lrclk_ratios[i].ratio;
/* Check that it's a standard rate since core can't
@@ -108,9 +105,13 @@ static int wm8524_set_dai_sysclk(struct snd_soc_dai *codec_dai,
*/
switch (val) {
case 8000:
+ case 11025:
+ case 16000:
+ case 22050:
case 32000:
case 44100:
case 48000:
+ case 64000:
case 88200:
case 96000:
case 176400:
@@ -157,6 +158,33 @@ static int wm8524_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
return 0;
}
+static int wm8524_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 wm8524_priv *wm8524 = snd_soc_component_get_drvdata(component);
+ int i;
+
+ /* If sysclk is not configured, no need to check the rate */
+ if (!wm8524->sysclk)
+ return 0;
+
+ /* Find a supported LRCLK rate */
+ for (i = 0; i < wm8524->rate_constraint.count; i++) {
+ if (wm8524->rate_constraint.list[i] == params_rate(params))
+ break;
+ }
+
+ if (i == wm8524->rate_constraint.count) {
+ dev_err(component->dev, "LRCLK %d unsupported with MCLK %d\n",
+ params_rate(params), wm8524->sysclk);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
#define WM8524_RATES SNDRV_PCM_RATE_8000_192000
#define WM8524_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
@@ -169,6 +197,7 @@ static const struct snd_soc_dai_ops wm8524_dai_ops = {
.set_sysclk = wm8524_set_dai_sysclk,
.set_fmt = wm8524_set_fmt,
.mute_stream = wm8524_mute_stream,
+ .hw_params = wm8524_hw_params,
};
static struct snd_soc_dai_driver wm8524_dai = {
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 03902909f27e..2ed9f493d507 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1825,13 +1825,15 @@ static int wm8903_gpio_direction_out(struct gpio_chip *chip,
return 0;
}
-static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int wm8903_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct wm8903_priv *wm8903 = gpiochip_get_data(chip);
- regmap_update_bits(wm8903->regmap, WM8903_GPIO_CONTROL_1 + offset,
- WM8903_GP1_LVL_MASK,
- !!value << WM8903_GP1_LVL_SHIFT);
+ return regmap_update_bits(wm8903->regmap,
+ WM8903_GPIO_CONTROL_1 + offset,
+ WM8903_GP1_LVL_MASK,
+ !!value << WM8903_GP1_LVL_SHIFT);
}
static const struct gpio_chip wm8903_template_chip = {
@@ -1841,7 +1843,7 @@ static const struct gpio_chip wm8903_template_chip = {
.direction_input = wm8903_gpio_direction_in,
.get = wm8903_gpio_get,
.direction_output = wm8903_gpio_direction_out,
- .set = wm8903_gpio_set,
+ .set_rv = wm8903_gpio_set,
.can_sleep = 1,
};
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 68f746626c33..d69aa8b15629 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3407,13 +3407,16 @@ static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset)
return 0;
}
-static void wm8962_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int wm8962_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct wm8962_priv *wm8962 = gpiochip_get_data(chip);
struct snd_soc_component *component = wm8962->component;
- snd_soc_component_update_bits(component, WM8962_GPIO_BASE + offset,
- WM8962_GP2_LVL, !!value << WM8962_GP2_LVL_SHIFT);
+ return snd_soc_component_update_bits(component,
+ WM8962_GPIO_BASE + offset,
+ WM8962_GP2_LVL,
+ !!value << WM8962_GP2_LVL_SHIFT);
}
static int wm8962_gpio_direction_out(struct gpio_chip *chip,
@@ -3439,7 +3442,7 @@ static const struct gpio_chip wm8962_template_chip = {
.owner = THIS_MODULE,
.request = wm8962_gpio_request,
.direction_output = wm8962_gpio_direction_out,
- .set = wm8962_gpio_set,
+ .set_rv = wm8962_gpio_set,
.can_sleep = 1,
};
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index c2af8d7ecdd8..e364d0da9044 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -2136,12 +2136,14 @@ static int wm8996_set_fll(struct snd_soc_component *component, int fll_id, int s
}
#ifdef CONFIG_GPIOLIB
-static void wm8996_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+static int wm8996_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct wm8996_priv *wm8996 = gpiochip_get_data(chip);
- regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
- WM8996_GP1_LVL, !!value << WM8996_GP1_LVL_SHIFT);
+ return regmap_update_bits(wm8996->regmap, WM8996_GPIO_1 + offset,
+ WM8996_GP1_LVL,
+ !!value << WM8996_GP1_LVL_SHIFT);
}
static int wm8996_gpio_direction_out(struct gpio_chip *chip,
@@ -2184,7 +2186,7 @@ static const struct gpio_chip wm8996_template_chip = {
.label = "wm8996",
.owner = THIS_MODULE,
.direction_output = wm8996_gpio_direction_out,
- .set = wm8996_gpio_set,
+ .set_rv = wm8996_gpio_set,
.direction_input = wm8996_gpio_direction_in,
.get = wm8996_gpio_get,
.can_sleep = 1,
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 91c8697c29c3..8a1d5cc75d6c 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -8,6 +8,7 @@
*/
#include <linux/array_size.h>
+#include <linux/cleanup.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -19,7 +20,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
-#include <linux/vmalloc.h>
+#include <linux/string.h>
#include <linux/workqueue.h>
#include <linux/debugfs.h>
#include <sound/core.h>
@@ -415,21 +416,12 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
struct cs_dsp_coeff_ctl *cs_ctl = ctl->cs_ctl;
- void *scratch;
- int ret = 0;
-
- scratch = vmalloc(size);
- if (!scratch)
- return -ENOMEM;
-
- if (copy_from_user(scratch, bytes, size))
- ret = -EFAULT;
- else
- ret = cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, scratch, size);
+ void *scratch __free(kvfree) = vmemdup_user(bytes, size);
- vfree(scratch);
+ if (IS_ERR(scratch))
+ return PTR_ERR(scratch);
- return ret;
+ return cs_dsp_coeff_lock_and_write_ctrl(cs_ctl, 0, scratch, size);
}
static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
@@ -718,12 +710,10 @@ static void wm_adsp_release_firmware_files(struct wm_adsp *dsp,
const struct firmware *coeff_firmware,
char *coeff_filename)
{
- if (wmfw_firmware)
- release_firmware(wmfw_firmware);
+ release_firmware(wmfw_firmware);
kfree(wmfw_filename);
- if (coeff_firmware)
- release_firmware(coeff_firmware);
+ release_firmware(coeff_firmware);
kfree(coeff_filename);
}
@@ -785,7 +775,7 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
return ret;
}
-static const char *cirrus_dir = "cirrus/";
+static const char * const cirrus_dir = "cirrus/";
static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
const struct firmware **wmfw_firmware,
char **wmfw_filename,
@@ -793,16 +783,19 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
char **coeff_filename)
{
const char *system_name = dsp->system_name;
- const char *asoc_component_prefix = dsp->component->name_prefix;
+ const char *suffix = dsp->component->name_prefix;
int ret = 0;
- if (system_name && asoc_component_prefix) {
+ if (dsp->fwf_suffix)
+ suffix = dsp->fwf_suffix;
+
+ if (system_name && suffix) {
if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
cirrus_dir, system_name,
- asoc_component_prefix, "wmfw")) {
+ suffix, "wmfw")) {
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
cirrus_dir, system_name,
- asoc_component_prefix, "bin");
+ suffix, "bin");
return 0;
}
}
@@ -811,10 +804,10 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
cirrus_dir, system_name,
NULL, "wmfw")) {
- if (asoc_component_prefix)
+ if (suffix)
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
cirrus_dir, system_name,
- asoc_component_prefix, "bin");
+ suffix, "bin");
if (!*coeff_firmware)
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
@@ -826,10 +819,10 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
/* Check system-specific bin without wmfw before falling back to generic */
if (dsp->wmfw_optional && system_name) {
- if (asoc_component_prefix)
+ if (suffix)
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
cirrus_dir, system_name,
- asoc_component_prefix, "bin");
+ suffix, "bin");
if (!*coeff_firmware)
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
@@ -860,7 +853,7 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
cirrus_dir, dsp->part,
dsp->fwf_name ? dsp->fwf_name : dsp->cs_dsp.name,
- wm_adsp_fw[dsp->fw].file, system_name, asoc_component_prefix);
+ wm_adsp_fw[dsp->fw].file, system_name, suffix);
return -ENOENT;
}
@@ -1007,11 +1000,17 @@ int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware)
return ret;
}
+ if (dsp->bin_mandatory && !coeff_firmware) {
+ ret = -ENOENT;
+ goto err;
+ }
+
ret = cs_dsp_power_up(&dsp->cs_dsp,
wmfw_firmware, wmfw_filename,
coeff_firmware, coeff_filename,
wm_adsp_fw_text[dsp->fw]);
+err:
wm_adsp_release_firmware_files(dsp,
wmfw_firmware, wmfw_filename,
coeff_firmware, coeff_filename);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index edc5b02ae765..25210d404bf1 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -29,12 +29,14 @@ struct wm_adsp {
const char *part;
const char *fwf_name;
const char *system_name;
+ const char *fwf_suffix;
struct snd_soc_component *component;
unsigned int sys_config_size;
int fw;
bool wmfw_optional;
+ bool bin_mandatory;
struct work_struct boot_work;
int (*control_add)(struct wm_adsp *dsp, struct cs_dsp_coeff_ctl *cs_ctl);
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 6627d2da3722..636e59abc377 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -202,7 +202,7 @@
SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
snd_soc_get_volsw, wsa881x_put_pa_gain, tlv_array)
-static struct reg_default wsa881x_defaults[] = {
+static const struct reg_default wsa881x_defaults[] = {
{ WSA881X_CHIP_ID0, 0x00 },
{ WSA881X_CHIP_ID1, 0x00 },
{ WSA881X_CHIP_ID2, 0x00 },
@@ -346,7 +346,7 @@ static const struct reg_sequence wsa881x_vi_txfe_en_2_0[] = {
};
/* Default register reset values for WSA881x rev 2.0 */
-static struct reg_sequence wsa881x_rev_2_0[] = {
+static const struct reg_sequence wsa881x_rev_2_0[] = {
{ WSA881X_RESET_CTL, 0x00, 0x00 },
{ WSA881X_TADC_VALUE_CTL, 0x01, 0x00 },
{ WSA881X_INTR_MASK, 0x1B, 0x00 },
@@ -775,7 +775,6 @@ static int wsa881x_put_pa_gain(struct snd_kcontrol *kc,
usleep_range(1000, 1010);
}
- pm_runtime_mark_last_busy(comp->dev);
pm_runtime_put_autosuspend(comp->dev);
return 1;
diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c
index 1c9df7c061bd..188363b03b93 100644
--- a/sound/soc/codecs/wsa883x.c
+++ b/sound/soc/codecs/wsa883x.c
@@ -572,7 +572,7 @@ static const struct sdw_port_config wsa883x_pconfig[WSA883X_MAX_SWR_PORTS] = {
},
};
-static struct reg_default wsa883x_defaults[] = {
+static const struct reg_default wsa883x_defaults[] = {
{ WSA883X_REF_CTRL, 0xD5 },
{ WSA883X_TEST_CTL_0, 0x06 },
{ WSA883X_BIAS_0, 0xD2 },
@@ -1491,7 +1491,6 @@ static int wsa883x_get_temp(struct wsa883x_priv *wsa883x, long *temp)
ret = -EAGAIN;
}
out:
- pm_runtime_mark_last_busy(wsa883x->dev);
pm_runtime_put_autosuspend(wsa883x->dev);
return ret;
@@ -1584,7 +1583,7 @@ static int wsa883x_probe(struct sdw_slave *pdev,
wsa883x->sconfig.type = SDW_STREAM_PDM;
mutex_init(&wsa883x->sp_lock);
- /**
+ /*
* Port map index starts with 0, however the data port for this codec
* are from index 1
*/
diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c
index daada1a2a34c..2484d4b8e2d9 100644
--- a/sound/soc/codecs/wsa884x.c
+++ b/sound/soc/codecs/wsa884x.c
@@ -899,7 +899,7 @@ static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = {
},
};
-static struct reg_default wsa884x_defaults[] = {
+static const struct reg_default wsa884x_defaults[] = {
{ WSA884X_BG_CTRL, 0xa5 },
{ WSA884X_ADC_CTRL, 0x00 },
{ WSA884X_BOP1_PROG, 0x22 },
@@ -1941,7 +1941,6 @@ static int wsa884x_get_temp(struct wsa884x_priv *wsa884x, long *temp)
}
out:
- pm_runtime_mark_last_busy(wsa884x->dev);
pm_runtime_put_autosuspend(wsa884x->dev);
return ret;
@@ -2085,7 +2084,7 @@ static int wsa884x_probe(struct sdw_slave *pdev,
wsa884x->sconfig.direction = SDW_DATA_DIR_RX;
wsa884x->sconfig.type = SDW_STREAM_PDM;
- /**
+ /*
* Port map index starts with 0, however the data port for this codec
* are from index 1
*/
diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c
index 28c92d90299e..180d45a349ac 100644
--- a/sound/soc/codecs/zl38060.c
+++ b/sound/soc/codecs/zl38060.c
@@ -387,12 +387,12 @@ static const struct snd_soc_component_driver zl38_component_dev = {
.endianness = 1,
};
-static void chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
+static int chip_gpio_set(struct gpio_chip *c, unsigned int offset, int val)
{
struct regmap *regmap = gpiochip_get_data(c);
unsigned int mask = BIT(offset);
- regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0);
+ return regmap_update_bits(regmap, REG_GPIO_DAT, mask, val ? mask : 0);
}
static int chip_gpio_get(struct gpio_chip *c, unsigned int offset)
@@ -422,8 +422,12 @@ chip_direction_output(struct gpio_chip *c, unsigned int offset, int val)
{
struct regmap *regmap = gpiochip_get_data(c);
unsigned int mask = BIT(offset);
+ int ret;
+
+ ret = chip_gpio_set(c, offset, val);
+ if (ret)
+ return ret;
- chip_gpio_set(c, offset, val);
return regmap_update_bits(regmap, REG_GPIO_DIR, mask, mask);
}
@@ -436,7 +440,7 @@ static const struct gpio_chip template_chip = {
.direction_input = chip_direction_input,
.direction_output = chip_direction_output,
.get = chip_gpio_get,
- .set = chip_gpio_set,
+ .set_rv = chip_gpio_set,
.can_sleep = true,
};
diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig
index 71a58f7ac13a..6bb31b64210a 100644
--- a/sound/soc/dwc/Kconfig
+++ b/sound/soc/dwc/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "DesignWare"
+
config SND_DESIGNWARE_I2S
tristate "Synopsys I2S Device Driver"
depends on HAVE_CLK
@@ -18,3 +20,4 @@ config SND_DESIGNWARE_PCM
This functionality is specially suited for I2S devices that don't have
DMA support.
+endmenu
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index c4cf3cff58de..c4a00b22bc2a 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-menu "SoC Audio for Freescale CPUs"
+menu "Freescale"
comment "Common SoC Audio options for Freescale CPUs:"
diff --git a/sound/soc/fsl/fsl-asoc-card.c b/sound/soc/fsl/fsl-asoc-card.c
index ab583b432c60..71113886e494 100644
--- a/sound/soc/fsl/fsl-asoc-card.c
+++ b/sound/soc/fsl/fsl-asoc-card.c
@@ -63,6 +63,7 @@ struct codec_priv {
* @sysclk_freq: SYSCLK rates for set_sysclk()
* @sysclk_dir: SYSCLK directions for set_sysclk()
* @sysclk_id: SYSCLK ids for set_sysclk()
+ * @sysclk_ratio: SYSCLK ratio on sample rate
* @slot_width: Slot width of each frame
* @slot_num: Number of slots of each frame
*
@@ -72,6 +73,7 @@ struct cpu_priv {
unsigned long sysclk_freq[2];
u32 sysclk_dir[2];
u32 sysclk_id[2];
+ u32 sysclk_ratio[2];
u32 slot_width;
u32 slot_num;
};
@@ -176,7 +178,7 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai;
struct cpu_priv *cpu_priv = &priv->cpu_priv;
struct device *dev = rtd->card->dev;
- unsigned int pll_out;
+ unsigned int pll_out, sysclk_freq;
int codec_idx;
int ret;
@@ -187,9 +189,14 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
if (fsl_asoc_card_is_ac97(priv))
return 0;
+ if (!cpu_priv->sysclk_freq[tx] && cpu_priv->sysclk_ratio[tx])
+ sysclk_freq = priv->sample_rate * cpu_priv->sysclk_ratio[tx];
+ else
+ sysclk_freq = cpu_priv->sysclk_freq[tx];
+
/* Specific configurations of DAIs starts from here */
ret = snd_soc_dai_set_sysclk(snd_soc_rtd_to_cpu(rtd, 0), cpu_priv->sysclk_id[tx],
- cpu_priv->sysclk_freq[tx],
+ sysclk_freq,
cpu_priv->sysclk_dir[tx]);
if (ret && ret != -ENOTSUPP) {
dev_err(dev, "failed to set sysclk for cpu dai\n");
@@ -799,6 +806,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
priv->cpu_priv.slot_width = 32;
priv->card.dapm_routes = audio_map_tx;
priv->card.num_dapm_routes = ARRAY_SIZE(audio_map_tx);
+ priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
+ priv->cpu_priv.sysclk_ratio[TX] = 256;
} else if (of_device_is_compatible(np, "fsl,imx-audio-si476x")) {
codec_dai_name[0] = "si476x-codec";
priv->dai_fmt |= SND_SOC_DAIFMT_CBC_CFC;
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index 677529916dc0..92fb16f7be45 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -517,7 +517,8 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
regmap_update_bits(asrc->regmap, REG_ASRCTR,
ASRCTR_ATSi_MASK(index), ASRCTR_ATS(index));
regmap_update_bits(asrc->regmap, REG_ASRCTR,
- ASRCTR_USRi_MASK(index), 0);
+ ASRCTR_IDRi_MASK(index) | ASRCTR_USRi_MASK(index),
+ ASRCTR_USR(index));
/* Set the input and output clock sources */
regmap_update_bits(asrc->regmap, REG_ASRCSR,
@@ -930,7 +931,7 @@ static bool fsl_asrc_writeable_reg(struct device *dev, unsigned int reg)
}
}
-static struct reg_default fsl_asrc_reg[] = {
+static const struct reg_default fsl_asrc_reg[] = {
{ REG_ASRCTR, 0x0000 }, { REG_ASRIER, 0x0000 },
{ REG_ASRCNCR, 0x0000 }, { REG_ASRCFG, 0x0000 },
{ REG_ASRCSR, 0x0000 }, { REG_ASRCDR1, 0x0000 },
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
index e34e5ea98de5..901f840df904 100644
--- a/sound/soc/fsl/fsl_mqs.c
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -39,6 +39,7 @@ enum reg_type {
* struct fsl_mqs_soc_data - soc specific data
*
* @type: control register space type
+ * @sm_index: index from definition in system manager
* @ctrl_off: control register offset
* @en_mask: enable bit mask
* @en_shift: enable bit shift
@@ -51,6 +52,7 @@ enum reg_type {
*/
struct fsl_mqs_soc_data {
enum reg_type type;
+ int sm_index;
int ctrl_off;
int en_mask;
int en_shift;
@@ -82,7 +84,7 @@ static int fsl_mqs_sm_read(void *context, unsigned int reg, unsigned int *val)
if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
mqs_priv->soc->ctrl_off == reg)
- return scmi_imx_misc_ctrl_get(SCMI_IMX_CTRL_MQS1_SETTINGS, &num, val);
+ return scmi_imx_misc_ctrl_get(mqs_priv->soc->sm_index, &num, val);
return -EINVAL;
};
@@ -93,7 +95,7 @@ static int fsl_mqs_sm_write(void *context, unsigned int reg, unsigned int val)
if (IS_ENABLED(CONFIG_IMX_SCMI_MISC_DRV) &&
mqs_priv->soc->ctrl_off == reg)
- return scmi_imx_misc_ctrl_set(SCMI_IMX_CTRL_MQS1_SETTINGS, val);
+ return scmi_imx_misc_ctrl_set(mqs_priv->soc->sm_index, val);
return -EINVAL;
};
@@ -386,6 +388,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx93_data = {
static const struct fsl_mqs_soc_data fsl_mqs_imx95_aon_data = {
.type = TYPE_REG_SM,
+ .sm_index = SCMI_IMX95_CTRL_MQS1_SETTINGS,
.ctrl_off = 0x88,
.en_mask = BIT(1),
.en_shift = 1,
@@ -412,6 +415,7 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx95_netc_data = {
static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = {
.type = TYPE_REG_SM,
+ .sm_index = SCMI_IMX94_CTRL_MQS1_SETTINGS,
.ctrl_off = 0x88,
.en_mask = BIT(1),
.en_shift = 1,
@@ -424,7 +428,8 @@ static const struct fsl_mqs_soc_data fsl_mqs_imx943_aon_data = {
};
static const struct fsl_mqs_soc_data fsl_mqs_imx943_wakeup_data = {
- .type = TYPE_REG_GPR,
+ .type = TYPE_REG_SM,
+ .sm_index = SCMI_IMX94_CTRL_MQS2_SETTINGS,
.ctrl_off = 0x10,
.en_mask = BIT(1),
.en_shift = 1,
diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c
index 0a551be3053b..5708b3a9878d 100644
--- a/sound/soc/fsl/fsl_rpmsg.c
+++ b/sound/soc/fsl/fsl_rpmsg.c
@@ -24,6 +24,8 @@
/* 192kHz/32bit/2ch/60s size is 0x574e00 */
#define LPA_LARGE_BUFFER_SIZE (0x6000000)
+/* 16kHz/32bit/8ch/1s size is 0x7D000 */
+#define LPA_CAPTURE_BUFFER_SIZE (0x100000)
static const unsigned int fsl_rpmsg_rates[] = {
8000, 11025, 16000, 22050, 44100,
@@ -97,13 +99,9 @@ static int fsl_rpmsg_hw_free(struct snd_pcm_substream *substream,
static int fsl_rpmsg_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *cpu_dai)
{
- int ret;
-
- ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
- SNDRV_PCM_HW_PARAM_RATE,
- &fsl_rpmsg_rate_constraints);
-
- return ret;
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &fsl_rpmsg_rate_constraints);
}
static const struct snd_soc_dai_ops fsl_rpmsg_dai_ops = {
@@ -233,11 +231,23 @@ static int fsl_rpmsg_probe(struct platform_device *pdev)
}
dai_drv->name = dai_name;
+ /* Setup cpu dai for sound card that sits on rpmsg-micfil-channel */
+ if (!strcmp(dai_name, "rpmsg-micfil-channel")) {
+ dai_drv->capture.channels_min = 1;
+ dai_drv->capture.channels_max = 8;
+ dai_drv->capture.rates = SNDRV_PCM_RATE_8000_48000;
+ dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ if (of_device_is_compatible(np, "fsl,imx8mm-rpmsg-audio"))
+ dai_drv->capture.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ }
+
if (of_property_read_bool(np, "fsl,enable-lpa")) {
rpmsg->enable_lpa = 1;
- rpmsg->buffer_size = LPA_LARGE_BUFFER_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = LPA_LARGE_BUFFER_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = LPA_CAPTURE_BUFFER_SIZE;
} else {
- rpmsg->buffer_size = IMX_DEFAULT_DMABUF_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK] = IMX_DEFAULT_DMABUF_SIZE;
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE] = IMX_DEFAULT_DMABUF_SIZE;
}
/* Get the optional clocks */
diff --git a/sound/soc/fsl/fsl_rpmsg.h b/sound/soc/fsl/fsl_rpmsg.h
index b04086fbf828..1b1683808507 100644
--- a/sound/soc/fsl/fsl_rpmsg.h
+++ b/sound/soc/fsl/fsl_rpmsg.h
@@ -42,6 +42,6 @@ struct fsl_rpmsg {
unsigned int mclk_streams;
int force_lpa;
int enable_lpa;
- int buffer_size;
+ int buffer_size[2];
};
#endif /* __FSL_RPMSG_H */
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index ed2b4780c470..c313b654236c 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -163,14 +163,46 @@ out:
return iret;
}
-static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
- u32 rx_mask, int slots, int slot_width)
+static int fsl_sai_set_dai_tdm_slot_tx(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = true;
+
+ sai->slots[tx] = slots;
+ sai->slot_width[tx] = slot_width;
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_tdm_slot_rx(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
{
struct fsl_sai *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ bool tx = false;
+
+ sai->slots[tx] = slots;
+ sai->slot_width[tx] = slot_width;
+
+ return 0;
+}
+
+static int fsl_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ int ret;
+
+ ret = fsl_sai_set_dai_tdm_slot_tx(cpu_dai, tx_mask, rx_mask, slots, slot_width);
+ if (ret)
+ return ret;
- sai->slots = slots;
- sai->slot_width = slot_width;
+ return fsl_sai_set_dai_tdm_slot_rx(cpu_dai, tx_mask, rx_mask, slots, slot_width);
+}
+static int fsl_sai_xlate_tdm_slot_mask(unsigned int slots,
+ unsigned int *tx_mask, unsigned int *rx_mask)
+{
+ /* Leave it empty, don't change the value of tx_mask and rx_mask */
return 0;
}
@@ -238,22 +270,22 @@ static int fsl_sai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
if (dir == SND_SOC_CLOCK_IN)
return 0;
- if (freq > 0 && clk_id != FSL_SAI_CLK_BUS) {
- if (clk_id < 0 || clk_id >= FSL_SAI_MCLK_MAX) {
- dev_err(cpu_dai->dev, "Unknown clock id: %d\n", clk_id);
- return -EINVAL;
- }
+ if (clk_id < 0 || clk_id >= FSL_SAI_MCLK_MAX) {
+ dev_err(cpu_dai->dev, "Unknown clock id: %d\n", clk_id);
+ return -EINVAL;
+ }
- if (IS_ERR_OR_NULL(sai->mclk_clk[clk_id])) {
- dev_err(cpu_dai->dev, "Unassigned clock: %d\n", clk_id);
- return -EINVAL;
- }
+ if (IS_ERR_OR_NULL(sai->mclk_clk[clk_id])) {
+ dev_err(cpu_dai->dev, "Unassigned clock: %d\n", clk_id);
+ return -EINVAL;
+ }
- if (sai->mclk_streams == 0) {
- ret = fsl_sai_set_mclk_rate(cpu_dai, clk_id, freq);
- if (ret < 0)
- return ret;
- }
+ if (sai->mclk_streams == 0 && freq > 0) {
+ ret = fsl_sai_set_mclk_rate(cpu_dai,
+ clk_id ? clk_id : FSL_SAI_CLK_MAST1,
+ freq);
+ if (ret < 0)
+ return ret;
}
ret = fsl_sai_set_dai_sysclk_tr(cpu_dai, clk_id, freq, true);
@@ -280,7 +312,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
val_cr4 |= FSL_SAI_CR4_MF;
sai->is_pdm_mode = false;
- sai->is_dsp_mode = false;
+ sai->is_dsp_mode[tx] = false;
/* DAI mode */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
@@ -309,7 +341,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
*/
val_cr2 |= FSL_SAI_CR2_BCP;
val_cr4 |= FSL_SAI_CR4_FSE;
- sai->is_dsp_mode = true;
+ sai->is_dsp_mode[tx] = true;
break;
case SND_SOC_DAIFMT_DSP_B:
/*
@@ -317,7 +349,7 @@ static int fsl_sai_set_dai_fmt_tr(struct snd_soc_dai *cpu_dai,
* frame sync asserts with the first bit of the frame.
*/
val_cr2 |= FSL_SAI_CR2_BCP;
- sai->is_dsp_mode = true;
+ sai->is_dsp_mode[tx] = true;
break;
case SND_SOC_DAIFMT_PDM:
val_cr2 |= FSL_SAI_CR2_BCP;
@@ -541,11 +573,11 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
u32 watermark;
int ret, i;
- if (sai->slot_width)
- slot_width = sai->slot_width;
+ if (sai->slot_width[tx])
+ slot_width = sai->slot_width[tx];
- if (sai->slots)
- slots = sai->slots;
+ if (sai->slots[tx])
+ slots = sai->slots[tx];
else if (sai->bclk_ratio)
slots = sai->bclk_ratio / slot_width;
@@ -600,7 +632,7 @@ static int fsl_sai_hw_params(struct snd_pcm_substream *substream,
}
}
- if (!sai->is_dsp_mode && !sai->is_pdm_mode)
+ if (!sai->is_dsp_mode[tx] && !sai->is_pdm_mode)
val_cr4 |= FSL_SAI_CR4_SYWD(slot_width);
val_cr5 |= FSL_SAI_CR5_WNW(slot_width);
@@ -771,13 +803,15 @@ static void fsl_sai_config_disable(struct fsl_sai *sai, int dir)
* anymore. Add software reset to fix this issue.
* This is a hardware bug, and will be fix in the
* next sai version.
+ *
+ * In consumer mode, this can happen even after a
+ * single open/close, especially if both tx and rx
+ * are running concurrently.
*/
- if (!sai->is_consumer_mode[tx]) {
- /* Software Reset */
- regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
- /* Clear SR bit to finish the reset */
- regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0);
- }
+ /* Software Reset */
+ regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), FSL_SAI_CSR_SR);
+ /* Clear SR bit to finish the reset */
+ regmap_write(sai->regmap, FSL_SAI_xCSR(tx, ofs), 0);
}
static int fsl_sai_trigger(struct snd_pcm_substream *substream, int cmd,
@@ -932,7 +966,8 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_tx_ops = {
.set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt_tx,
- .set_tdm_slot = fsl_sai_set_dai_tdm_slot,
+ .set_tdm_slot = fsl_sai_set_dai_tdm_slot_tx,
+ .xlate_tdm_slot_mask = fsl_sai_xlate_tdm_slot_mask,
.hw_params = fsl_sai_hw_params,
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
@@ -944,7 +979,8 @@ static const struct snd_soc_dai_ops fsl_sai_pcm_dai_rx_ops = {
.set_bclk_ratio = fsl_sai_set_dai_bclk_ratio,
.set_sysclk = fsl_sai_set_dai_sysclk,
.set_fmt = fsl_sai_set_dai_fmt_rx,
- .set_tdm_slot = fsl_sai_set_dai_tdm_slot,
+ .set_tdm_slot = fsl_sai_set_dai_tdm_slot_rx,
+ .xlate_tdm_slot_mask = fsl_sai_xlate_tdm_slot_mask,
.hw_params = fsl_sai_hw_params,
.hw_free = fsl_sai_hw_free,
.trigger = fsl_sai_trigger,
@@ -1025,7 +1061,7 @@ static const struct snd_soc_component_driver fsl_component = {
.legacy_dai_naming = 1,
};
-static struct reg_default fsl_sai_reg_defaults_ofs0[] = {
+static const struct reg_default fsl_sai_reg_defaults_ofs0[] = {
{FSL_SAI_TCR1(0), 0},
{FSL_SAI_TCR2(0), 0},
{FSL_SAI_TCR3(0), 0},
@@ -1048,7 +1084,7 @@ static struct reg_default fsl_sai_reg_defaults_ofs0[] = {
{FSL_SAI_RMR, 0},
};
-static struct reg_default fsl_sai_reg_defaults_ofs8[] = {
+static const struct reg_default fsl_sai_reg_defaults_ofs8[] = {
{FSL_SAI_TCR1(8), 0},
{FSL_SAI_TCR2(8), 0},
{FSL_SAI_TCR3(8), 0},
diff --git a/sound/soc/fsl/fsl_sai.h b/sound/soc/fsl/fsl_sai.h
index 0e25e2fc7ce0..6c917f79c6b0 100644
--- a/sound/soc/fsl/fsl_sai.h
+++ b/sound/soc/fsl/fsl_sai.h
@@ -286,7 +286,7 @@ struct fsl_sai {
bool is_consumer_mode[2];
bool is_lsb_first;
- bool is_dsp_mode;
+ bool is_dsp_mode[2];
bool is_pdm_mode;
bool is_multi_fifo_dma;
bool synchronous[2];
@@ -296,8 +296,8 @@ struct fsl_sai {
unsigned int mclk_id[2];
unsigned int mclk_streams;
- unsigned int slots;
- unsigned int slot_width;
+ unsigned int slots[2];
+ unsigned int slot_width[2];
unsigned int bclk_ratio;
const struct fsl_sai_soc_data *soc_data;
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index 83aea341c1b6..e3111dd80be4 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -1827,7 +1827,7 @@ static const struct dev_pm_ops fsl_xcvr_pm_ops = {
static struct platform_driver fsl_xcvr_driver = {
.probe = fsl_xcvr_probe,
.driver = {
- .name = "fsl,imx8mp-audio-xcvr",
+ .name = "fsl-xcvr",
.pm = pm_ptr(&fsl_xcvr_pm_ops),
.of_match_table = fsl_xcvr_dt_ids,
},
diff --git a/sound/soc/fsl/imx-card.c b/sound/soc/fsl/imx-card.c
index 45e000f61ecc..ea5dbb54b584 100644
--- a/sound/soc/fsl/imx-card.c
+++ b/sound/soc/fsl/imx-card.c
@@ -513,7 +513,6 @@ static int imx_card_parse_of(struct imx_card_data *data)
struct device_node *platform = NULL;
struct device_node *codec = NULL;
struct device_node *cpu = NULL;
- struct device_node *np;
struct device *dev = card->dev;
struct snd_soc_dai_link *link;
struct dai_link_data *link_data;
@@ -552,11 +551,10 @@ static int imx_card_parse_of(struct imx_card_data *data)
link = card->dai_link;
link_data = data->link_data;
- for_each_child_of_node(dev->of_node, np) {
+ for_each_child_of_node_scoped(dev->of_node, np) {
dlc = devm_kzalloc(dev, 2 * sizeof(*dlc), GFP_KERNEL);
if (!dlc) {
- ret = -ENOMEM;
- goto err_put_np;
+ return -ENOMEM;
}
link->cpus = &dlc[0];
@@ -567,8 +565,8 @@ static int imx_card_parse_of(struct imx_card_data *data)
ret = of_property_read_string(np, "link-name", &link->name);
if (ret) {
- dev_err(card->dev, "error getting codec dai_link name\n");
- goto err_put_np;
+ return dev_err_probe(card->dev, ret,
+ "error getting codec dai_link name\n");
}
cpu = of_get_child_by_name(np, "cpu");
@@ -670,9 +668,12 @@ static int imx_card_parse_of(struct imx_card_data *data)
}
} else if (!strncmp(link->name, "HiFi-ASRC-BE", 12)) {
/* DPCM backend */
+ /*
+ * No need to have link->platforms. alloced dlc[1] will be just wasted,
+ * but it won't leak.
+ */
link->no_pcm = 1;
- link->platforms->of_node = NULL;
- link->platforms->name = "snd-soc-dummy";
+ link->platforms = NULL;
link->be_hw_params_fixup = be_hw_params_fixup;
link->ops = &imx_aif_ops_be;
@@ -722,8 +723,7 @@ err:
of_node_put(cpu);
of_node_put(codec);
of_node_put(platform);
-err_put_np:
- of_node_put(np);
+
return ret;
}
diff --git a/sound/soc/fsl/imx-pcm-rpmsg.c b/sound/soc/fsl/imx-pcm-rpmsg.c
index de5f87600fac..edab68ae8366 100644
--- a/sound/soc/fsl/imx-pcm-rpmsg.c
+++ b/sound/soc/fsl/imx-pcm-rpmsg.c
@@ -209,7 +209,7 @@ static snd_pcm_uframes_t imx_rpmsg_pcm_pointer(struct snd_soc_component *compone
static void imx_rpmsg_timer_callback(struct timer_list *t)
{
struct stream_timer *stream_timer =
- from_timer(stream_timer, t, timer);
+ timer_container_of(stream_timer, t, timer);
struct snd_pcm_substream *substream = stream_timer->substream;
struct rpmsg_info *info = stream_timer->info;
struct rpmsg_msg *msg;
@@ -261,7 +261,7 @@ static int imx_rpmsg_pcm_open(struct snd_soc_component *component,
info->send_message(msg, info);
pcm_hardware = imx_rpmsg_pcm_hardware;
- pcm_hardware.buffer_bytes_max = rpmsg->buffer_size;
+ pcm_hardware.buffer_bytes_max = rpmsg->buffer_size[substream->stream];
pcm_hardware.period_bytes_max = pcm_hardware.buffer_bytes_max / 2;
snd_soc_set_runtime_hwparams(substream, &pcm_hardware);
@@ -597,14 +597,29 @@ static int imx_rpmsg_pcm_new(struct snd_soc_component *component,
struct snd_pcm *pcm = rtd->pcm;
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct fsl_rpmsg *rpmsg = dev_get_drvdata(cpu_dai->dev);
+ struct snd_pcm_substream *substream;
int ret;
ret = dma_coerce_mask_and_coherent(card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
- return snd_pcm_set_fixed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV_WC,
- pcm->card->dev, rpmsg->buffer_size);
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (substream) {
+ ret = snd_pcm_set_fixed_buffer(substream, SNDRV_DMA_TYPE_DEV_WC, pcm->card->dev,
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_PLAYBACK]);
+ if (ret < 0)
+ return ret;
+ }
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (substream) {
+ ret = snd_pcm_set_fixed_buffer(substream, SNDRV_DMA_TYPE_DEV_WC, pcm->card->dev,
+ rpmsg->buffer_size[SNDRV_PCM_STREAM_CAPTURE]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return ret;
}
static const struct snd_soc_component_driver imx_rpmsg_soc_component = {
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index b6df4e26bc4a..64b0817e2955 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Generic drivers"
+
config SND_SIMPLE_CARD_UTILS
tristate
@@ -37,3 +39,5 @@ config SND_TEST_COMPONENT
depends on OF
help
This option enables test component sound driver support.
+
+endmenu
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 3ae2a212a2e3..355f7ec8943c 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -1119,12 +1119,16 @@ int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep,
args.np = ep;
dai = snd_soc_get_dai_via_args(&args);
if (dai) {
+ const char *dai_name = snd_soc_dai_name_get(dai);
+ const struct of_phandle_args *dai_args = snd_soc_copy_dai_args(dev, &args);
+
ret = -ENOMEM;
+ if (!dai_args)
+ goto err;
+
dlc->of_node = node;
- dlc->dai_name = snd_soc_dai_name_get(dai);
- dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
- if (!dlc->dai_args)
- goto end;
+ dlc->dai_name = dai_name;
+ dlc->dai_args = dai_args;
goto parse_dai_end;
}
@@ -1154,16 +1158,17 @@ int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep,
* if he unbinded CPU or Codec.
*/
ret = snd_soc_get_dlc(&args, dlc);
- if (ret < 0) {
- of_node_put(node);
- goto end;
- }
+ if (ret < 0)
+ goto err;
parse_dai_end:
if (is_single_link)
*is_single_link = of_graph_get_endpoint_count(node) == 1;
ret = 0;
-end:
+err:
+ if (ret < 0)
+ of_node_put(node);
+
return simple_ret(priv, ret);
}
EXPORT_SYMBOL_GPL(graph_util_parse_dai);
diff --git a/sound/soc/generic/test-component.c b/sound/soc/generic/test-component.c
index 5430d25deaef..89b995987e2d 100644
--- a/sound/soc/generic/test-component.c
+++ b/sound/soc/generic/test-component.c
@@ -140,6 +140,15 @@ static int test_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+static int test_dai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ dev_info(dai->dev, "set tdm slot: tx_mask=0x%08X, rx_mask=0x%08X, slots=%d, slot_width=%d\n",
+ tx_mask, rx_mask, slots, slot_width);
+ return 0;
+}
+
static int test_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
{
mile_stone(dai);
@@ -203,6 +212,7 @@ static const u64 test_dai_formats =
static const struct snd_soc_dai_ops test_ops = {
.set_fmt = test_dai_set_fmt,
+ .set_tdm_slot = test_dai_set_tdm_slot,
.startup = test_dai_startup,
.shutdown = test_dai_shutdown,
.auto_selectable_formats = &test_dai_formats,
@@ -214,6 +224,7 @@ static const struct snd_soc_dai_ops test_verbose_ops = {
.set_pll = test_dai_set_pll,
.set_clkdiv = test_dai_set_clkdiv,
.set_fmt = test_dai_set_fmt,
+ .set_tdm_slot = test_dai_set_tdm_slot,
.mute_stream = test_dai_mute_stream,
.startup = test_dai_startup,
.shutdown = test_dai_shutdown,
diff --git a/sound/soc/google/Kconfig b/sound/soc/google/Kconfig
index 7603782fb060..6005653170a8 100644
--- a/sound/soc/google/Kconfig
+++ b/sound/soc/google/Kconfig
@@ -1,6 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Google"
config SND_SOC_CHV3_I2S
tristate "Google Chameleon v3 I2S device"
help
Enable support for the Google Chameleon v3 I2S device.
+
+endmenu
diff --git a/sound/soc/hisilicon/Kconfig b/sound/soc/hisilicon/Kconfig
index df8fbd8bb4b0..d95da932f352 100644
--- a/sound/soc/hisilicon/Kconfig
+++ b/sound/soc/hisilicon/Kconfig
@@ -1,6 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Hisilicon"
+
config SND_I2S_HI6210_I2S
tristate "Hisilicon I2S controller"
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Hisilicon I2S
+
+endmenu
diff --git a/sound/soc/img/Kconfig b/sound/soc/img/Kconfig
index 568efa606ca4..22b75a8144a1 100644
--- a/sound/soc/img/Kconfig
+++ b/sound/soc/img/Kconfig
@@ -1,12 +1,9 @@
# SPDX-License-Identifier: GPL-2.0-only
-config SND_SOC_IMG
- bool "Audio support for Imagination Technologies designs"
- help
- Audio support for Imagination Technologies audio hardware
+menu "Imagination Technologies"
+ depends on MIPS || COMPILE_TEST
config SND_SOC_IMG_I2S_IN
tristate "Imagination I2S Input Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S in driver for
@@ -14,7 +11,6 @@ config SND_SOC_IMG_I2S_IN
config SND_SOC_IMG_I2S_OUT
tristate "Imagination I2S Output Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S out driver for
@@ -22,7 +18,6 @@ config SND_SOC_IMG_I2S_OUT
config SND_SOC_IMG_PARALLEL_OUT
tristate "Imagination Parallel Output Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for parallel out driver for
@@ -30,7 +25,6 @@ config SND_SOC_IMG_PARALLEL_OUT
config SND_SOC_IMG_SPDIF_IN
tristate "Imagination SPDIF Input Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF input driver for
@@ -38,7 +32,6 @@ config SND_SOC_IMG_SPDIF_IN
config SND_SOC_IMG_SPDIF_OUT
tristate "Imagination SPDIF Output Device Driver"
- depends on SND_SOC_IMG
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF out driver for
@@ -47,7 +40,8 @@ config SND_SOC_IMG_SPDIF_OUT
config SND_SOC_IMG_PISTACHIO_INTERNAL_DAC
tristate "Support for Pistachio SoC Internal DAC Driver"
- depends on SND_SOC_IMG
help
Say Y or M if you want to add support for Pistachio internal DAC
driver for Imagination Technologies Pistachio internal DAC device.
+
+endmenu
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 2db494b0e3cf..412555e626b8 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Intel"
+
config SND_SOC_INTEL_SST_TOPLEVEL
bool "Intel ASoC SST drivers"
default y
@@ -115,3 +117,5 @@ source "sound/soc/intel/avs/boards/Kconfig"
# ASoC codec drivers
source "sound/soc/intel/boards/Kconfig"
+
+endmenu
diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h
index 126903e126e4..c43946c5ecee 100644
--- a/sound/soc/intel/atom/sst/sst.h
+++ b/sound/soc/intel/atom/sst/sst.h
@@ -443,9 +443,6 @@ int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
int sst_set_metadata(int str_id, char *params);
int sst_get_stream(struct intel_sst_drv *ctx,
struct snd_sst_params *str_param);
-int sst_get_stream_allocated(struct intel_sst_drv *ctx,
- struct snd_sst_params *str_param,
- struct snd_sst_lib_download **lib_dnld);
int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
int str_id, bool partial_drain);
int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
@@ -461,8 +458,6 @@ void sst_post_download_mrfld(struct intel_sst_drv *ctx);
int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx);
-int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
- struct sst_block *block);
int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
struct sst_block *block);
int sst_create_ipc_msg(struct ipc_post **arg, bool large);
@@ -470,7 +465,6 @@ int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
void sst_clean_stream(struct stream_info *stream);
int intel_sst_register_compress(struct intel_sst_drv *sst);
int intel_sst_remove_compress(struct intel_sst_drv *sst);
-void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
int sst_send_sync_msg(int ipc, int str_id);
int sst_get_num_channel(struct snd_sst_params *str_param);
int sst_get_sfreq(struct snd_sst_params *str_param);
diff --git a/sound/soc/intel/atom/sst/sst_drv_interface.c b/sound/soc/intel/atom/sst/sst_drv_interface.c
index dc31c2c8f54c..8bb27f86eb65 100644
--- a/sound/soc/intel/atom/sst/sst_drv_interface.c
+++ b/sound/soc/intel/atom/sst/sst_drv_interface.c
@@ -55,19 +55,6 @@ int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
return ret;
}
-int sst_get_stream_allocated(struct intel_sst_drv *ctx,
- struct snd_sst_params *str_param,
- struct snd_sst_lib_download **lib_dnld)
-{
- int retval;
-
- retval = ctx->ops->alloc_stream(ctx, str_param);
- if (retval > 0)
- dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
- return retval;
-
-}
-
/*
* sst_get_sfreq - this function returns the frequency of the stream
*
@@ -430,17 +417,6 @@ static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
return 0;
}
-void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
-{
- struct stream_info *stream;
-
- dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
- str_id);
- stream = &ctx->streams[str_id];
- if (stream->compr_cb)
- stream->compr_cb(stream->compr_cb_param);
-}
-
/*
* sst_close_pcm_stream - Close PCM interface
*
diff --git a/sound/soc/intel/atom/sst/sst_pci.c b/sound/soc/intel/atom/sst/sst_pci.c
index d1e64c3500be..22ae2d22f121 100644
--- a/sound/soc/intel/atom/sst/sst_pci.c
+++ b/sound/soc/intel/atom/sst/sst_pci.c
@@ -26,7 +26,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
int ddr_base, ret = 0;
struct pci_dev *pci = ctx->pci;
- ret = pci_request_regions(pci, SST_DRV_NAME);
+ ret = pcim_request_all_regions(pci, SST_DRV_NAME);
if (ret)
return ret;
@@ -38,67 +38,57 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
if (!ctx->pdata->lib_info) {
dev_err(ctx->dev, "lib_info pointer NULL\n");
- ret = -EINVAL;
- goto do_release_regions;
+ return -EINVAL;
}
if (ddr_base != ctx->pdata->lib_info->mod_base) {
dev_err(ctx->dev,
"FW LSP DDR BASE does not match with IFWI\n");
- ret = -EINVAL;
- goto do_release_regions;
+ return -EINVAL;
}
ctx->ddr_end = pci_resource_end(pci, 0);
- ctx->ddr = pcim_iomap(pci, 0,
- pci_resource_len(pci, 0));
- if (!ctx->ddr) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->ddr = pcim_iomap(pci, 0, 0);
+ if (!ctx->ddr)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
} else {
ctx->ddr = NULL;
}
/* SHIM */
ctx->shim_phy_add = pci_resource_start(pci, 1);
- ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
- if (!ctx->shim) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->shim = pcim_iomap(pci, 1, 0);
+ if (!ctx->shim)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
/* Shared SRAM */
ctx->mailbox_add = pci_resource_start(pci, 2);
- ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
- if (!ctx->mailbox) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->mailbox = pcim_iomap(pci, 2, 0);
+ if (!ctx->mailbox)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
/* IRAM */
ctx->iram_end = pci_resource_end(pci, 3);
ctx->iram_base = pci_resource_start(pci, 3);
- ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
- if (!ctx->iram) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->iram = pcim_iomap(pci, 3, 0);
+ if (!ctx->iram)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
/* DRAM */
ctx->dram_end = pci_resource_end(pci, 4);
ctx->dram_base = pci_resource_start(pci, 4);
- ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
- if (!ctx->dram) {
- ret = -EINVAL;
- goto do_release_regions;
- }
+ ctx->dram = pcim_iomap(pci, 4, 0);
+ if (!ctx->dram)
+ return -ENOMEM;
+
dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
-do_release_regions:
- pci_release_regions(pci);
- return ret;
+ return 0;
}
/*
@@ -167,7 +157,6 @@ static void intel_sst_remove(struct pci_dev *pci)
sst_context_cleanup(sst_drv_ctx);
pci_dev_put(sst_drv_ctx->pci);
- pci_release_regions(pci);
pci_set_drvdata(pci, NULL);
}
diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c
index e6a5c18a7018..c01b29616ebc 100644
--- a/sound/soc/intel/atom/sst/sst_pvt.c
+++ b/sound/soc/intel/atom/sst/sst_pvt.c
@@ -70,39 +70,6 @@ void sst_set_fw_state_locked(
}
/*
- * sst_wait_interruptible - wait on event
- *
- * @sst_drv_ctx: Driver context
- * @block: Driver block to wait on
- *
- * This function waits without a timeout (and is interruptable) for a
- * given block event
- */
-int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
- struct sst_block *block)
-{
- int retval = 0;
-
- if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
- block->condition)) {
- /* event wake */
- if (block->ret_code < 0) {
- dev_err(sst_drv_ctx->dev,
- "stream failed %d\n", block->ret_code);
- retval = -EBUSY;
- } else {
- dev_dbg(sst_drv_ctx->dev, "event up\n");
- retval = 0;
- }
- } else {
- dev_err(sst_drv_ctx->dev, "signal interrupted\n");
- retval = -EINTR;
- }
- return retval;
-
-}
-
-/*
* sst_wait_timeout - wait on event for timeout
*
* @sst_drv_ctx: Driver context
@@ -292,7 +259,6 @@ int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
{
int ret;
- pm_runtime_mark_last_busy(sst_drv->dev);
ret = pm_runtime_put_autosuspend(sst_drv->dev);
if (ret < 0)
return ret;
diff --git a/sound/soc/intel/avs/Makefile b/sound/soc/intel/avs/Makefile
index 5139a019a4ad..576dc0da381d 100644
--- a/sound/soc/intel/avs/Makefile
+++ b/sound/soc/intel/avs/Makefile
@@ -1,10 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
snd-soc-avs-y := dsp.o ipc.o messages.o utils.o core.o loader.o \
- topology.o path.o pcm.o board_selection.o control.o \
- sysfs.o
+ topology.o path.o pcm.o board_selection.o control.o \
+ sysfs.o
snd-soc-avs-y += cldma.o
-snd-soc-avs-y += skl.o apl.o cnl.o icl.o tgl.o
+snd-soc-avs-y += skl.o apl.o cnl.o icl.o tgl.o mtl.o lnl.o ptl.o
snd-soc-avs-y += trace.o
# tell define_trace.h where to find the trace header
diff --git a/sound/soc/intel/avs/avs.h b/sound/soc/intel/avs/avs.h
index 585543f872fc..4c096afc5848 100644
--- a/sound/soc/intel/avs/avs.h
+++ b/sound/soc/intel/avs/avs.h
@@ -69,9 +69,12 @@ extern const struct avs_dsp_ops avs_apl_dsp_ops;
extern const struct avs_dsp_ops avs_cnl_dsp_ops;
extern const struct avs_dsp_ops avs_icl_dsp_ops;
extern const struct avs_dsp_ops avs_tgl_dsp_ops;
+extern const struct avs_dsp_ops avs_ptl_dsp_ops;
#define AVS_PLATATTR_CLDMA BIT_ULL(0)
#define AVS_PLATATTR_IMR BIT_ULL(1)
+#define AVS_PLATATTR_ACE BIT_ULL(2)
+#define AVS_PLATATTR_ALTHDA BIT_ULL(3)
#define avs_platattr_test(adev, attr) \
((adev)->spec->attributes & AVS_PLATATTR_##attr)
@@ -79,7 +82,6 @@ extern const struct avs_dsp_ops avs_tgl_dsp_ops;
struct avs_sram_spec {
const u32 base_offset;
const u32 window_size;
- const u32 rom_status_offset;
};
struct avs_hipc_spec {
@@ -91,6 +93,7 @@ struct avs_hipc_spec {
const u32 rsp_offset;
const u32 rsp_busy_mask;
const u32 ctl_offset;
+ const u32 sts_offset;
};
/* Platform specific descriptor */
@@ -265,8 +268,14 @@ 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 avs_mtl_core_power(struct avs_dev *adev, u32 core_mask, bool power);
+int avs_mtl_core_reset(struct avs_dev *adev, u32 core_mask, bool power);
+int avs_mtl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
+int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall);
+void avs_mtl_interrupt_control(struct avs_dev *adev, bool enable);
void avs_skl_ipc_interrupt(struct avs_dev *adev);
irqreturn_t avs_cnl_dsp_interrupt(struct avs_dev *adev);
+irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev);
int avs_apl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
u32 fifo_full_period, unsigned long resource_mask, u32 *priorities);
int avs_icl_enable_logs(struct avs_dev *adev, enum avs_log_enable enable, u32 aging_period,
@@ -340,7 +349,7 @@ struct avs_soc_component {
extern const struct snd_soc_dai_ops avs_dai_fe_ops;
int avs_soc_component_register(struct device *dev, const char *name,
- const struct snd_soc_component_driver *drv,
+ struct snd_soc_component_driver *drv,
struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais);
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,
diff --git a/sound/soc/intel/avs/board_selection.c b/sound/soc/intel/avs/board_selection.c
index 2d706edcbf92..fb49167f5fc4 100644
--- a/sound/soc/intel/avs/board_selection.c
+++ b/sound/soc/intel/avs/board_selection.c
@@ -17,10 +17,15 @@
#include <sound/soc-acpi.h>
#include <sound/soc-component.h>
#include "avs.h"
+#include "utils.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 char *i2s_test;
+module_param(i2s_test, charp, 0444);
+MODULE_PARM_DESC(i2s_test, "Use I2S test-board instead of ACPI, i2s_test=ssp0tdm,ssp1tdm,... 0 to ignore port");
+
+bool obsolete_card_names = IS_ENABLED(CONFIG_SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE);
+module_param_named(obsolete_card_names, obsolete_card_names, bool, 0444);
+MODULE_PARM_DESC(obsolete_card_names, "Use obsolete card names 0=no, 1=yes");
static const struct dmi_system_id kbl_dmi_table[] = {
{
@@ -141,7 +146,7 @@ static struct snd_soc_acpi_mach avs_kbl_i2s_machines[] = {
.mach_params = {
.i2s_link_mask = AVS_SSP(0),
},
- .pdata = (unsigned long[]){ 0x2, 0, 0, 0, 0, 0 }, /* SSP0 TDMs */
+ .pdata = (struct avs_mach_pdata[]){ { .tdms = (unsigned long[]){ 0x2 } } },
.tplg_filename = "rt5514-tplg.bin",
},
{
@@ -202,7 +207,9 @@ static struct snd_soc_acpi_mach avs_apl_i2s_machines[] = {
.mach_params = {
.i2s_link_mask = AVS_SSP_RANGE(0, 5),
},
- .pdata = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 }, /* SSP2 TDMs */
+ .pdata = (struct avs_mach_pdata[]){ {
+ .tdms = (unsigned long[]){ 0x1, 0x1, 0x14, 0x1, 0x1, 0x1 }
+ } },
.tplg_filename = "tdf8532-tplg.bin",
},
{
@@ -302,72 +309,53 @@ static struct snd_soc_acpi_mach avs_tgl_i2s_machines[] = {
.tplg_filename = "rt1308-tplg.bin",
},
{
- .id = "ESSX8336",
- .drv_name = "avs_es8336",
- .mach_params = {
- .i2s_link_mask = AVS_SSP(0),
- },
- .tplg_filename = "es8336-tplg.bin",
- },
- {},
-};
-
-static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = {
- {
- .id = "PCM3168A",
- .drv_name = "avs_pcm3168a",
- .mach_params = {
- .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2),
- },
- .tplg_filename = "pcm3168a-tplg.bin",
- },
- {}
-};
-
-static struct snd_soc_acpi_mach avs_test_i2s_machines[] = {
- {
- .drv_name = "avs_i2s_test",
+ .id = "10EC5640",
+ .uid = "1",
+ .drv_name = "avs_rt5640",
.mach_params = {
.i2s_link_mask = AVS_SSP(0),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt5640-tplg.bin",
},
{
- .drv_name = "avs_i2s_test",
+ .id = "10EC5640",
+ .uid = "3",
+ .drv_name = "avs_rt5640",
.mach_params = {
.i2s_link_mask = AVS_SSP(1),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "rt5640-tplg.bin",
},
{
- .drv_name = "avs_i2s_test",
+ .id = "10EC5640",
+ .uid = "2",
+ .drv_name = "avs_rt5640",
.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",
+ .tplg_filename = "rt5640-tplg.bin",
},
{
- .drv_name = "avs_i2s_test",
+ .id = "ESSX8336",
+ .drv_name = "avs_es8336",
.mach_params = {
- .i2s_link_mask = AVS_SSP(4),
+ .i2s_link_mask = AVS_SSP(0),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "es8336-tplg.bin",
},
+ {},
+};
+
+static struct snd_soc_acpi_mach avs_mbl_i2s_machines[] = {
{
- .drv_name = "avs_i2s_test",
+ .id = "PCM3168A",
+ .drv_name = "avs_pcm3168a",
.mach_params = {
- .i2s_link_mask = AVS_SSP(5),
+ .i2s_link_mask = AVS_SSP(0) | AVS_SSP(2),
},
- .tplg_filename = "i2s-test-tplg.bin",
+ .tplg_filename = "pcm3168a-tplg.bin",
},
- /* no NULL terminator, as we depend on ARRAY SIZE due to .id == NULL */
+ {}
};
struct avs_acpi_boards {
@@ -394,7 +382,8 @@ static const struct avs_acpi_boards i2s_boards[] = {
AVS_MACH_ENTRY(HDA_ADL_P, avs_tgl_i2s_machines),
AVS_MACH_ENTRY(HDA_RPL_P_0, avs_tgl_i2s_machines),
AVS_MACH_ENTRY(HDA_RPL_M, avs_mbl_i2s_machines),
- {}
+ AVS_MACH_ENTRY(HDA_FCL, avs_tgl_i2s_machines),
+ { },
};
static const struct avs_acpi_boards *avs_get_i2s_boards(struct avs_dev *adev)
@@ -445,6 +434,7 @@ static int avs_register_dmic_board(struct avs_dev *adev)
{
struct platform_device *codec, *board;
struct snd_soc_acpi_mach mach = {{0}};
+ struct avs_mach_pdata *pdata;
int ret;
if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_PDM, -1, -1, -1)) {
@@ -468,6 +458,11 @@ static int avs_register_dmic_board(struct avs_dev *adev)
if (ret < 0)
return ret;
+ pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ pdata->obsolete_card_names = obsolete_card_names;
+ mach.pdata = pdata;
mach.tplg_filename = "dmic-tplg.bin";
mach.mach_params.platform = "dmic-platform";
@@ -490,9 +485,11 @@ static int avs_register_dmic_board(struct avs_dev *adev)
static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach *mach)
{
struct platform_device *board;
+ struct avs_mach_pdata *pdata;
int num_ssps;
char *name;
int ret;
+ int uid;
num_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
if (fls(mach->mach_params.i2s_link_mask) > num_ssps) {
@@ -502,18 +499,29 @@ static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach
return -ENODEV;
}
- name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name,
- mach->mach_params.i2s_link_mask);
+ pdata = mach->pdata;
+ if (!pdata)
+ pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ pdata->obsolete_card_names = obsolete_card_names;
+ mach->pdata = pdata;
+
+ uid = mach->mach_params.i2s_link_mask;
+ if (avs_mach_singular_ssp(mach))
+ uid = (uid << AVS_CHANNELS_MAX) + avs_mach_ssp_tdm(mach, avs_mach_ssp_port(mach));
+
+ name = devm_kasprintf(adev->dev, GFP_KERNEL, "%s.%d-platform", mach->drv_name, uid);
if (!name)
return -ENOMEM;
- ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, mach->pdata);
+ ret = avs_i2s_platform_register(adev, name, mach->mach_params.i2s_link_mask, pdata->tdms);
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,
+ board = platform_device_register_data(NULL, mach->drv_name, uid,
(const void *)mach, sizeof(*mach));
if (IS_ERR(board)) {
dev_err(adev->dev, "ssp board register failed\n");
@@ -529,35 +537,82 @@ static int avs_register_i2s_board(struct avs_dev *adev, struct snd_soc_acpi_mach
return 0;
}
-static int avs_register_i2s_boards(struct avs_dev *adev)
+static int avs_register_i2s_test_board(struct avs_dev *adev, int ssp_port, int tdm_slot)
{
- const struct avs_acpi_boards *boards;
struct snd_soc_acpi_mach *mach;
+ int tdm_mask = BIT(tdm_slot);
+ unsigned long *tdm_cfg;
+ char *tplg_name;
int ret;
- if (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, -1, -1, -1)) {
- dev_dbg(adev->dev, "no I2S endpoints present\n");
- return 0;
+ mach = devm_kzalloc(adev->dev, sizeof(*mach), GFP_KERNEL);
+ tdm_cfg = devm_kcalloc(adev->dev, ssp_port + 1, sizeof(unsigned long), GFP_KERNEL);
+ tplg_name = devm_kasprintf(adev->dev, GFP_KERNEL, AVS_STRING_FMT("i2s", "-test-tplg.bin",
+ ssp_port, tdm_slot));
+ if (!mach || !tdm_cfg || !tplg_name)
+ return -ENOMEM;
+
+ mach->drv_name = "avs_i2s_test";
+ mach->mach_params.i2s_link_mask = AVS_SSP(ssp_port);
+ tdm_cfg[ssp_port] = tdm_mask;
+ mach->pdata = tdm_cfg;
+ mach->tplg_filename = tplg_name;
+
+ 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 ret;
}
- if (i2s_test) {
- int i, num_ssps;
+ return 0;
+}
- 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);
+static int avs_register_i2s_test_boards(struct avs_dev *adev)
+{
+ int max_ssps = adev->hw_cfg.i2s_caps.ctrl_count;
+ int ssp_port, tdm_slot, ret;
+ unsigned long tdm_slots;
+ u32 *array, num_elems;
+
+ ret = parse_int_array(i2s_test, strlen(i2s_test), (int **)&array);
+ if (ret) {
+ dev_err(adev->dev, "failed to parse i2s_test parameter\n");
+ return ret;
+ }
- mach = avs_test_i2s_machines;
+ num_elems = *array;
+ if (num_elems > max_ssps) {
+ dev_err(adev->dev, "board supports only %d SSP, %d specified\n",
+ max_ssps, num_elems);
+ return -EINVAL;
+ }
- 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);
+ for (ssp_port = 0; ssp_port < num_elems; ssp_port++) {
+ tdm_slots = array[1 + ssp_port];
+ for_each_set_bit(tdm_slot, &tdm_slots, 16) {
+ ret = avs_register_i2s_test_board(adev, ssp_port, tdm_slot);
+ if (ret)
+ 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 (!acpi_nhlt_find_endpoint(ACPI_NHLT_LINKTYPE_SSP, -1, -1, -1)) {
+ dev_dbg(adev->dev, "no I2S endpoints present\n");
return 0;
}
+ if (i2s_test)
+ return avs_register_i2s_test_boards(adev);
+
boards = avs_get_i2s_boards(adev);
if (!boards) {
dev_dbg(adev->dev, "no I2S endpoints supported\n");
@@ -584,6 +639,7 @@ 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 avs_mach_pdata *pdata;
struct hdac_device *hdev = &codec->core;
char *pname;
int ret, id;
@@ -592,11 +648,17 @@ static int avs_register_hda_board(struct avs_dev *adev, struct hda_codec *codec)
if (!pname)
return -ENOMEM;
+ pdata = devm_kzalloc(adev->dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ pdata->obsolete_card_names = obsolete_card_names;
+ pdata->codec = codec;
+
ret = avs_hda_platform_register(adev, pname);
if (ret < 0)
return ret;
- mach.pdata = codec;
+ mach.pdata = pdata;
mach.mach_params.platform = pname;
mach.tplg_filename = devm_kasprintf(adev->dev, GFP_KERNEL, "hda-%08x-tplg.bin",
hdev->vendor_id);
diff --git a/sound/soc/intel/avs/boards/Kconfig b/sound/soc/intel/avs/boards/Kconfig
index ba4bee42124c..82f50207bb2f 100644
--- a/sound/soc/intel/avs/boards/Kconfig
+++ b/sound/soc/intel/avs/boards/Kconfig
@@ -4,6 +4,14 @@ menu "Intel AVS Machine drivers"
comment "Available DSP configurations"
+config SND_SOC_INTEL_AVS_CARDNAME_OBSOLETE
+ bool "Use obsolete card names"
+ default n
+ help
+ Use obsolete names for some of avs cards. This option should be
+ used if your system depends on old card names, for example having
+ not up to date UCM files.
+
config SND_SOC_INTEL_AVS_MACH_DA7219
tristate "da7219 I2S board"
depends on I2C
@@ -145,6 +153,18 @@ config SND_SOC_INTEL_AVS_MACH_RT5514
Say Y or m if you have such a device. This is a recommended option.
If unsure select "N".
+config SND_SOC_INTEL_AVS_MACH_RT5640
+ tristate "rt5640 in I2S mode"
+ depends on I2C
+ depends on MFD_INTEL_LPSS || COMPILE_TEST
+ select SND_SOC_RT5640
+ help
+ This adds support for ASoC machine board connecting AVS with RT5640,
+ components representing Intel AudioDSP and Realtek 5640 codec respectively.
+ The codec chip is present on I2C bus and the streaming occurs over I2S
+ interface.
+ Say Y or m if you have such a device.
+
config SND_SOC_INTEL_AVS_MACH_RT5663
tristate "rt5663 in I2S mode"
depends on I2C
diff --git a/sound/soc/intel/avs/boards/Makefile b/sound/soc/intel/avs/boards/Makefile
index a95256b94dc8..46ef1babda34 100644
--- a/sound/soc/intel/avs/boards/Makefile
+++ b/sound/soc/intel/avs/boards/Makefile
@@ -15,6 +15,7 @@ snd-soc-avs-rt274-y := rt274.o
snd-soc-avs-rt286-y := rt286.o
snd-soc-avs-rt298-y := rt298.o
snd-soc-avs-rt5514-y := rt5514.o
+snd-soc-avs-rt5640-y := rt5640.o
snd-soc-avs-rt5663-y := rt5663.o
snd-soc-avs-rt5682-y := rt5682.o
snd-soc-avs-ssm4567-y := ssm4567.o
@@ -34,6 +35,7 @@ obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT274) += snd-soc-avs-rt274.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT286) += snd-soc-avs-rt286.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT298) += snd-soc-avs-rt298.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5514) += snd-soc-avs-rt5514.o
+obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5640) += snd-soc-avs-rt5640.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5663) += snd-soc-avs-rt5663.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_RT5682) += snd-soc-avs-rt5682.o
obj-$(CONFIG_SND_SOC_INTEL_AVS_MACH_SSM4567) += snd-soc-avs-ssm4567.o
diff --git a/sound/soc/intel/avs/boards/da7219.c b/sound/soc/intel/avs/boards/da7219.c
index 7217ae51e89c..3ef0db254142 100644
--- a/sound/soc/intel/avs/boards/da7219.c
+++ b/sound/soc/intel/avs/boards/da7219.c
@@ -214,6 +214,7 @@ static int avs_da7219_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
@@ -222,6 +223,7 @@ static int avs_da7219_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -238,7 +240,12 @@ static int avs_da7219_probe(struct platform_device *pdev)
if (!jack || !card)
return -ENOMEM;
- card->name = "avs_da7219";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_da7219";
+ } else {
+ card->driver_name = "avs_da7219";
+ card->long_name = card->name = "AVS I2S DA7219";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
@@ -256,7 +263,7 @@ static int avs_da7219_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_da7219_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/dmic.c b/sound/soc/intel/avs/boards/dmic.c
index 4dd9591ee98b..a1448a98874d 100644
--- a/sound/soc/intel/avs/boards/dmic.c
+++ b/sound/soc/intel/avs/boards/dmic.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/soc-acpi.h>
+#include "../utils.h"
SND_SOC_DAILINK_DEF(dmic_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC Pin")));
SND_SOC_DAILINK_DEF(dmic_wov_pin, DAILINK_COMP_ARRAY(COMP_CPU("DMIC WoV Pin")));
@@ -49,17 +50,24 @@ static const struct snd_soc_dapm_route card_routes[] = {
static int avs_dmic_probe(struct platform_device *pdev)
{
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
int ret;
mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
- card->name = "avs_dmic";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_dmic";
+ } else {
+ card->driver_name = "avs_dmic";
+ card->long_name = card->name = "AVS DMIC";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = card_dai_links;
@@ -74,7 +82,7 @@ static int avs_dmic_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_dmic_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/es8336.c b/sound/soc/intel/avs/boards/es8336.c
index 6f3c4f6c9302..1955f2d383c5 100644
--- a/sound/soc/intel/avs/boards/es8336.c
+++ b/sound/soc/intel/avs/boards/es8336.c
@@ -259,6 +259,7 @@ static int avs_es8336_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct avs_card_drvdata *data;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
@@ -267,6 +268,7 @@ static int avs_es8336_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -283,7 +285,12 @@ static int avs_es8336_probe(struct platform_device *pdev)
if (!data || !card)
return -ENOMEM;
- card->name = "avs_es8336";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_es8336";
+ } else {
+ card->driver_name = "avs_es8336";
+ card->long_name = card->name = "AVS I2S ES8336";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
@@ -303,7 +310,7 @@ static int avs_es8336_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_es8336_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
index cb6d54db7189..19b2255a8ac3 100644
--- a/sound/soc/intel/avs/boards/hdaudio.c
+++ b/sound/soc/intel/avs/boards/hdaudio.c
@@ -13,6 +13,7 @@
#include <sound/soc.h>
#include <sound/soc-acpi.h>
#include "../../../codecs/hda.h"
+#include "../utils.h"
static int avs_create_dai_links(struct device *dev, struct hda_codec *codec, int pcm_count,
const char *platform_name, struct snd_soc_dai_link **links)
@@ -95,7 +96,8 @@ avs_card_hdmi_pcm_at(struct snd_soc_card *card, int hdmi_idx)
static int avs_card_late_probe(struct snd_soc_card *card)
{
struct snd_soc_acpi_mach *mach = dev_get_platdata(card->dev);
- struct hda_codec *codec = mach->pdata;
+ struct avs_mach_pdata *pdata = mach->pdata;
+ struct hda_codec *codec = pdata->codec;
struct hda_pcm *hpcm;
/* Topology pcm indexing is 1-based */
int i = 1;
@@ -124,6 +126,7 @@ static int avs_card_late_probe(struct snd_soc_card *card)
static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
{
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_dai_link *links = NULL;
struct snd_soc_card *card = rtm->card;
struct hda_codec *codec;
@@ -131,7 +134,8 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
int ret, pcm_count = 0;
mach = dev_get_platdata(card->dev);
- codec = mach->pdata;
+ pdata = mach->pdata;
+ codec = pdata->codec;
if (list_empty(&codec->pcm_list_head))
return -EINVAL;
@@ -167,12 +171,14 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *binder;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
struct hda_codec *codec;
mach = dev_get_platdata(dev);
- codec = mach->pdata;
+ pdata = mach->pdata;
+ codec = pdata->codec;
/* codec may be unloaded before card's probe() fires */
if (!device_is_registered(&codec->core.dev))
@@ -200,7 +206,16 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = binder->codecs->name;
+ if (pdata->obsolete_card_names) {
+ card->name = binder->codecs->name;
+ } else {
+ card->driver_name = "avs_hdaudio";
+ if (hda_codec_is_display(codec))
+ card->long_name = card->name = "AVS HDMI";
+ else
+ card->long_name = card->name = "AVS HD-Audio";
+ }
+
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = binder;
@@ -209,7 +224,7 @@ static int avs_hdaudio_probe(struct platform_device *pdev)
if (hda_codec_is_display(codec))
card->late_probe = avs_card_late_probe;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_hdaudio_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/i2s_test.c b/sound/soc/intel/avs/boards/i2s_test.c
index 4556f105c793..f7b6d7715738 100644
--- a/sound/soc/intel/avs/boards/i2s_test.c
+++ b/sound/soc/intel/avs/boards/i2s_test.c
@@ -56,6 +56,7 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
@@ -63,6 +64,7 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
if (!avs_mach_singular_ssp(mach)) {
dev_err(dev, "Invalid SSP configuration\n");
@@ -80,8 +82,15 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = devm_kasprintf(dev, GFP_KERNEL,
- AVS_STRING_FMT("ssp", "-loopback", ssp_port, tdm_slot));
+ if (pdata->obsolete_card_names) {
+ card->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("ssp", "-loopback", ssp_port, tdm_slot));
+ } else {
+ card->driver_name = "avs_i2s_test";
+ card->long_name = card->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("AVS I2S TEST-", "",
+ ssp_port, tdm_slot));
+ }
if (!card->name)
return -ENOMEM;
@@ -101,7 +110,7 @@ static int avs_i2s_test_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_i2s_test_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/max98357a.c b/sound/soc/intel/avs/boards/max98357a.c
index c55c1d60b777..72053f83e98b 100644
--- a/sound/soc/intel/avs/boards/max98357a.c
+++ b/sound/soc/intel/avs/boards/max98357a.c
@@ -93,6 +93,7 @@ static int avs_max98357a_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
@@ -100,6 +101,7 @@ static int avs_max98357a_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -115,7 +117,12 @@ static int avs_max98357a_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = "avs_max98357a";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_max98357a";
+ } else {
+ card->driver_name = "avs_max98357a";
+ card->long_name = card->name = "AVS I2S MAX98357A";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
@@ -132,7 +139,7 @@ static int avs_max98357a_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_max98357a_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/max98373.c b/sound/soc/intel/avs/boards/max98373.c
index 970f82bf4ce8..cdba1c3ee20b 100644
--- a/sound/soc/intel/avs/boards/max98373.c
+++ b/sound/soc/intel/avs/boards/max98373.c
@@ -146,6 +146,7 @@ static int avs_max98373_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
@@ -153,6 +154,7 @@ static int avs_max98373_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -168,7 +170,12 @@ static int avs_max98373_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = "avs_max98373";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_max98373";
+ } else {
+ card->driver_name = "avs_max98373";
+ card->long_name = card->name = "AVS I2S MAX98373";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
@@ -187,7 +194,7 @@ static int avs_max98373_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_max98373_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/max98927.c b/sound/soc/intel/avs/boards/max98927.c
index e4ce553bf1d6..a68e227044c5 100644
--- a/sound/soc/intel/avs/boards/max98927.c
+++ b/sound/soc/intel/avs/boards/max98927.c
@@ -143,6 +143,7 @@ static int avs_max98927_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
@@ -150,6 +151,7 @@ static int avs_max98927_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -165,7 +167,12 @@ static int avs_max98927_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = "avs_max98927";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_max98927";
+ } else {
+ card->driver_name = "avs_max98927";
+ card->long_name = card->name = "AVS I2S MAX98927";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
@@ -184,7 +191,7 @@ static int avs_max98927_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_max98927_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/nau8825.c b/sound/soc/intel/avs/boards/nau8825.c
index e203ceb83b87..3fb1a5d07ae1 100644
--- a/sound/soc/intel/avs/boards/nau8825.c
+++ b/sound/soc/intel/avs/boards/nau8825.c
@@ -246,6 +246,7 @@ static int avs_nau8825_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
@@ -254,6 +255,7 @@ static int avs_nau8825_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -270,7 +272,12 @@ static int avs_nau8825_probe(struct platform_device *pdev)
if (!jack || !card)
return -ENOMEM;
- card->name = "avs_nau8825";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_nau8825";
+ } else {
+ card->driver_name = "avs_nau8825";
+ card->long_name = card->name = "AVS I2S NAU8825";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
@@ -290,7 +297,7 @@ static int avs_nau8825_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_nau8825_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/pcm3168a.c b/sound/soc/intel/avs/boards/pcm3168a.c
index 5d0e7a5bdc74..b5bebadbbcb2 100644
--- a/sound/soc/intel/avs/boards/pcm3168a.c
+++ b/sound/soc/intel/avs/boards/pcm3168a.c
@@ -11,6 +11,8 @@
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../utils.h"
static const struct snd_soc_dapm_widget card_widgets[] = {
SND_SOC_DAPM_HP("CPB Stereo HP 1", NULL),
@@ -95,10 +97,15 @@ static int avs_create_dai_links(struct device *dev, struct snd_soc_dai_link **li
static int avs_pcm3168a_probe(struct platform_device *pdev)
{
+ struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct device *dev = &pdev->dev;
struct snd_soc_card *card;
int ret;
+ mach = dev_get_platdata(dev);
+ pdata = mach->pdata;
+
card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
if (!card)
return -ENOMEM;
@@ -107,7 +114,12 @@ static int avs_pcm3168a_probe(struct platform_device *pdev)
if (ret)
return ret;
- card->name = "avs_pcm3168a";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_pcm3168a";
+ } else {
+ card->driver_name = "avs_pcm3168a";
+ card->long_name = card->name = "AVS I2S PCM3168A";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dapm_widgets = card_widgets;
@@ -116,7 +128,7 @@ static int avs_pcm3168a_probe(struct platform_device *pdev)
card->num_dapm_routes = ARRAY_SIZE(card_routes);
card->fully_routed = true;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_pcm3168a_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/probe.c b/sound/soc/intel/avs/boards/probe.c
index 1cdc285ab810..06c1f19f27aa 100644
--- a/sound/soc/intel/avs/boards/probe.c
+++ b/sound/soc/intel/avs/boards/probe.c
@@ -36,7 +36,8 @@ static int avs_probe_mb_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = "avs_probe_mb";
+ card->driver_name = "avs_probe_mb";
+ card->long_name = card->name = "AVS PROBE";
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = probe_mb_dai_links;
@@ -47,7 +48,7 @@ static int avs_probe_mb_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_probe_mb_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/rt274.c b/sound/soc/intel/avs/boards/rt274.c
index 29977aee2153..ec5382925157 100644
--- a/sound/soc/intel/avs/boards/rt274.c
+++ b/sound/soc/intel/avs/boards/rt274.c
@@ -210,6 +210,7 @@ static int avs_rt274_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
@@ -218,6 +219,7 @@ static int avs_rt274_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -234,7 +236,12 @@ static int avs_rt274_probe(struct platform_device *pdev)
if (!jack || !card)
return -ENOMEM;
- card->name = "avs_rt274";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt274";
+ } else {
+ card->driver_name = "avs_rt274";
+ card->long_name = card->name = "AVS I2S ALC274";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
@@ -254,7 +261,7 @@ static int avs_rt274_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt274_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/rt286.c b/sound/soc/intel/avs/boards/rt286.c
index 6a27c6a9f394..2566e971ce1c 100644
--- a/sound/soc/intel/avs/boards/rt286.c
+++ b/sound/soc/intel/avs/boards/rt286.c
@@ -179,6 +179,7 @@ static int avs_rt286_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
@@ -187,6 +188,7 @@ static int avs_rt286_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -204,7 +206,12 @@ static int avs_rt286_probe(struct platform_device *pdev)
if (!jack || !card)
return -ENOMEM;
- card->name = "avs_rt286";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt286";
+ } else {
+ card->driver_name = "avs_rt286";
+ card->long_name = card->name = "AVS I2S ALC286";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
@@ -224,7 +231,7 @@ static int avs_rt286_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt286_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/rt298.c b/sound/soc/intel/avs/boards/rt298.c
index a7a463d6f852..7be34c8ad167 100644
--- a/sound/soc/intel/avs/boards/rt298.c
+++ b/sound/soc/intel/avs/boards/rt298.c
@@ -199,6 +199,7 @@ static int avs_rt298_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
@@ -207,6 +208,7 @@ static int avs_rt298_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -223,7 +225,12 @@ static int avs_rt298_probe(struct platform_device *pdev)
if (!jack || !card)
return -ENOMEM;
- card->name = "avs_rt298";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt298";
+ } else {
+ card->driver_name = "avs_rt298";
+ card->long_name = card->name = "AVS I2S ALC298";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
@@ -243,7 +250,7 @@ static int avs_rt298_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt298_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/rt5514.c b/sound/soc/intel/avs/boards/rt5514.c
index d1c3e29a28ff..45f091f2ce22 100644
--- a/sound/soc/intel/avs/boards/rt5514.c
+++ b/sound/soc/intel/avs/boards/rt5514.c
@@ -133,6 +133,7 @@ static int avs_rt5514_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
@@ -140,6 +141,7 @@ static int avs_rt5514_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -155,7 +157,12 @@ static int avs_rt5514_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = "avs_rt5514";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt5514";
+ } else {
+ card->driver_name = "avs_rt5514";
+ card->long_name = card->name = "AVS I2S ALC5514";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
@@ -170,7 +177,7 @@ static int avs_rt5514_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt5514_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/rt5640.c b/sound/soc/intel/avs/boards/rt5640.c
new file mode 100644
index 000000000000..706b84ffe1ef
--- /dev/null
+++ b/sound/soc/intel/avs/boards/rt5640.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Copyright(c) 2022-2025 Intel Corporation
+//
+// Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+// Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+//
+
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "../../../codecs/rt5640.h"
+#include "../utils.h"
+
+#define AVS_RT5640_MCLK_HZ 19200000
+#define RT5640_CODEC_DAI "rt5640-aif1"
+
+static const struct snd_soc_dapm_widget card_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_soc_dapm_route card_routes[] = {
+ { "Headphone Jack", NULL, "HPOR" },
+ { "Headphone Jack", NULL, "HPOL" },
+ { "IN2P", NULL, "Mic Jack" },
+ { "IN2P", NULL, "MICBIAS1" },
+ { "Speaker", NULL, "SPOLP" },
+ { "Speaker", NULL, "SPOLN" },
+ { "Speaker", NULL, "SPORP" },
+ { "Speaker", NULL, "SPORN" },
+};
+
+static const struct snd_soc_jack_pin card_headset_pins[] = {
+ {
+ .pin = "Headphone Jack",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Mic Jack",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static int avs_rt5640_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ struct snd_soc_card *card = runtime->card;
+ struct snd_soc_jack_pin *pins;
+ struct snd_soc_jack *jack;
+ int num_pins, ret;
+
+ jack = snd_soc_card_get_drvdata(card);
+ num_pins = ARRAY_SIZE(card_headset_pins);
+
+ pins = devm_kmemdup(card->dev, card_headset_pins, sizeof(*pins) * num_pins, GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ ret = snd_soc_card_jack_new_pins(card, "Headset Jack", SND_JACK_HEADSET, jack, pins,
+ num_pins);
+ if (ret)
+ return ret;
+
+ snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+ card->dapm.idle_bias_off = true;
+
+ return 0;
+}
+
+static void avs_rt5640_codec_exit(struct snd_soc_pcm_runtime *runtime)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+
+ snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_rt5640_be_fixup(struct snd_soc_pcm_runtime *runtime,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_mask *fmask = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT);
+
+ /* Format 24/32 is MSB-aligned for HDAudio and LSB-aligned for I2S. */
+ if (params_format(params) == SNDRV_PCM_FORMAT_S32_LE)
+ snd_mask_set_format(fmask, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int avs_rt5640_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(runtime, 0);
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_MCLK, AVS_RT5640_MCLK_HZ,
+ params_rate(params) * 512);
+ if (ret < 0) {
+ dev_err(runtime->dev, "Set codec PLL failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1, params_rate(params) * 512,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0) {
+ dev_err(runtime->dev, "Set codec SCLK failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = rt5640_sel_asrc_clk_src(codec_dai->component,
+ RT5640_DA_STEREO_FILTER | RT5640_AD_STEREO_FILTER |
+ RT5640_DA_MONO_L_FILTER | RT5640_DA_MONO_R_FILTER |
+ RT5640_AD_MONO_L_FILTER | RT5640_AD_MONO_R_FILTER,
+ RT5640_CLK_SEL_ASRC);
+ if (ret)
+ dev_err(runtime->dev, "Set codec ASRC failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct snd_soc_ops avs_rt5640_ops = {
+ .hw_params = avs_rt5640_hw_params,
+};
+
+static int avs_create_dai_link(struct device *dev, int ssp_port, int tdm_slot,
+ struct snd_soc_acpi_mach *mach,
+ struct snd_soc_dai_link **dai_link)
+{
+ struct snd_soc_dai_link_component *platform;
+ struct snd_soc_dai_link *dl;
+ u32 uid = 0;
+ int ret;
+
+ if (mach->uid) {
+ ret = kstrtou32(mach->uid, 0, &uid);
+ if (ret)
+ return ret;
+ uid--; /* 0-based indexing. */
+ }
+
+ dl = devm_kzalloc(dev, sizeof(*dl), GFP_KERNEL);
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!dl || !platform)
+ return -ENOMEM;
+
+ dl->name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", "-Codec", ssp_port, tdm_slot));
+ dl->cpus = devm_kzalloc(dev, sizeof(*dl->cpus), GFP_KERNEL);
+ dl->codecs = devm_kzalloc(dev, sizeof(*dl->codecs), GFP_KERNEL);
+ if (!dl->name || !dl->cpus || !dl->codecs)
+ return -ENOMEM;
+
+ dl->cpus->dai_name = devm_kasprintf(dev, GFP_KERNEL,
+ AVS_STRING_FMT("SSP", " Pin", ssp_port, tdm_slot));
+ dl->codecs->name = devm_kasprintf(dev, GFP_KERNEL, "i2c-10EC5640:0%d", uid);
+ dl->codecs->dai_name = devm_kasprintf(dev, GFP_KERNEL, RT5640_CODEC_DAI);
+ if (!dl->cpus->dai_name || !dl->codecs->name || !dl->codecs->dai_name)
+ return -ENOMEM;
+
+ platform->name = dev_name(dev);
+ dl->num_cpus = 1;
+ dl->num_codecs = 1;
+ dl->platforms = platform;
+ dl->num_platforms = 1;
+ dl->id = 0;
+ dl->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBC_CFC;
+ dl->init = avs_rt5640_codec_init;
+ dl->exit = avs_rt5640_codec_exit;
+ dl->be_hw_params_fixup = avs_rt5640_be_fixup;
+ dl->ops = &avs_rt5640_ops;
+ dl->nonatomic = 1;
+ dl->no_pcm = 1;
+
+ *dai_link = dl;
+
+ return 0;
+}
+
+static int avs_card_suspend_pre(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI);
+
+ return snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+
+static int avs_card_resume_post(struct snd_soc_card *card)
+{
+ struct snd_soc_dai *codec_dai = snd_soc_card_get_codec_dai(card, RT5640_CODEC_DAI);
+ struct snd_soc_jack *jack = snd_soc_card_get_drvdata(card);
+
+ return snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+}
+
+static int avs_rt5640_probe(struct platform_device *pdev)
+{
+ struct snd_soc_dai_link *dai_link;
+ struct device *dev = &pdev->dev;
+ struct snd_soc_acpi_mach *mach;
+ struct snd_soc_card *card;
+ struct snd_soc_jack *jack;
+ int ssp_port, tdm_slot, ret;
+
+ mach = dev_get_platdata(dev);
+
+ ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
+ if (ret)
+ return ret;
+
+ ret = avs_create_dai_link(dev, ssp_port, tdm_slot, mach, &dai_link);
+ if (ret) {
+ dev_err(dev, "Failed to create dai link: %d", ret);
+ return ret;
+ }
+
+ jack = devm_kzalloc(dev, sizeof(*jack), GFP_KERNEL);
+ card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+ if (!jack || !card)
+ return -ENOMEM;
+
+ if (mach->uid) {
+ card->name = devm_kasprintf(dev, GFP_KERNEL, "AVS I2S ALC5640.%s", mach->uid);
+ if (!card->name)
+ return -ENOMEM;
+ } else {
+ card->name = "AVS I2S ALC5640";
+ }
+ card->driver_name = "avs_rt5640";
+ card->long_name = card->name;
+ card->dev = dev;
+ card->owner = THIS_MODULE;
+ card->suspend_pre = avs_card_suspend_pre;
+ card->resume_post = avs_card_resume_post;
+ card->dai_link = dai_link;
+ card->num_links = 1;
+ card->dapm_widgets = card_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(card_widgets);
+ card->dapm_routes = card_routes;
+ card->num_dapm_routes = ARRAY_SIZE(card_routes);
+ card->fully_routed = true;
+ snd_soc_card_set_drvdata(card, jack);
+
+ return devm_snd_soc_register_deferrable_card(dev, card);
+}
+
+static const struct platform_device_id avs_rt5640_driver_ids[] = {
+ {
+ .name = "avs_rt5640",
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(platform, avs_rt5640_driver_ids);
+
+static struct platform_driver avs_rt5640_driver = {
+ .probe = avs_rt5640_probe,
+ .driver = {
+ .name = "avs_rt5640",
+ .pm = &snd_soc_pm_ops,
+ },
+ .id_table = avs_rt5640_driver_ids,
+};
+
+module_platform_driver(avs_rt5640_driver);
+
+MODULE_DESCRIPTION("Intel rt5640 machine driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/intel/avs/boards/rt5663.c b/sound/soc/intel/avs/boards/rt5663.c
index e3310b3268ba..51648801710a 100644
--- a/sound/soc/intel/avs/boards/rt5663.c
+++ b/sound/soc/intel/avs/boards/rt5663.c
@@ -198,6 +198,7 @@ static int avs_rt5663_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct rt5663_private *priv;
struct device *dev = &pdev->dev;
@@ -206,6 +207,7 @@ static int avs_rt5663_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -222,7 +224,12 @@ static int avs_rt5663_probe(struct platform_device *pdev)
if (!priv || !card)
return -ENOMEM;
- card->name = "avs_rt5663";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt5663";
+ } else {
+ card->driver_name = "avs_rt5663";
+ card->long_name = card->name = "AVS I2S ALC5663";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
@@ -242,7 +249,7 @@ static int avs_rt5663_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt5663_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/rt5682.c b/sound/soc/intel/avs/boards/rt5682.c
index 339df0b944c1..9677b9ebeff1 100644
--- a/sound/soc/intel/avs/boards/rt5682.c
+++ b/sound/soc/intel/avs/boards/rt5682.c
@@ -268,6 +268,7 @@ static int avs_rt5682_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct snd_soc_jack *jack;
struct device *dev = &pdev->dev;
@@ -282,6 +283,7 @@ static int avs_rt5682_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -298,7 +300,12 @@ static int avs_rt5682_probe(struct platform_device *pdev)
if (!jack || !card)
return -ENOMEM;
- card->name = "avs_rt5682";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_rt5682";
+ } else {
+ card->driver_name = "avs_rt5682";
+ card->long_name = card->name = "AVS I2S ALC5682";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->suspend_pre = avs_card_suspend_pre;
@@ -318,7 +325,7 @@ static int avs_rt5682_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_rt5682_driver_ids[] = {
diff --git a/sound/soc/intel/avs/boards/ssm4567.c b/sound/soc/intel/avs/boards/ssm4567.c
index 7667790d5273..3786eef8c494 100644
--- a/sound/soc/intel/avs/boards/ssm4567.c
+++ b/sound/soc/intel/avs/boards/ssm4567.c
@@ -132,6 +132,7 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
{
struct snd_soc_dai_link *dai_link;
struct snd_soc_acpi_mach *mach;
+ struct avs_mach_pdata *pdata;
struct snd_soc_card *card;
struct device *dev = &pdev->dev;
const char *pname;
@@ -139,6 +140,7 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
mach = dev_get_platdata(dev);
pname = mach->mach_params.platform;
+ pdata = mach->pdata;
ret = avs_mach_get_ssp_tdm(dev, mach, &ssp_port, &tdm_slot);
if (ret)
@@ -154,7 +156,12 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
if (!card)
return -ENOMEM;
- card->name = "avs_ssm4567";
+ if (pdata->obsolete_card_names) {
+ card->name = "avs_ssm4567";
+ } else {
+ card->driver_name = "avs_ssm4567";
+ card->long_name = card->name = "AVS I2S SSM4567";
+ }
card->dev = dev;
card->owner = THIS_MODULE;
card->dai_link = dai_link;
@@ -173,7 +180,7 @@ static int avs_ssm4567_probe(struct platform_device *pdev)
if (ret)
return ret;
- return devm_snd_soc_register_card(dev, card);
+ return devm_snd_soc_register_deferrable_card(dev, card);
}
static const struct platform_device_id avs_ssm4567_driver_ids[] = {
diff --git a/sound/soc/intel/avs/core.c b/sound/soc/intel/avs/core.c
index 8fbf33e30dfc..7af324753673 100644
--- a/sound/soc/intel/avs/core.c
+++ b/sound/soc/intel/avs/core.c
@@ -54,14 +54,17 @@ void avs_hda_power_gating_enable(struct avs_dev *adev, bool enable)
{
u32 value = enable ? 0 : pgctl_mask;
- avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, pgctl_mask, value);
+ if (!avs_platattr_test(adev, ACE))
+ avs_hda_update_config_dword(&adev->base.core, AZX_PCIREG_PGCTL, pgctl_mask, value);
}
static void avs_hdac_clock_gating_enable(struct hdac_bus *bus, bool enable)
{
+ struct avs_dev *adev = hdac_to_avs(bus);
u32 value = enable ? cgctl_mask : 0;
- avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, cgctl_mask, value);
+ if (!avs_platattr_test(adev, ACE))
+ avs_hda_update_config_dword(bus, AZX_PCIREG_CGCTL, cgctl_mask, value);
}
void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
@@ -71,6 +74,8 @@ void avs_hda_clock_gating_enable(struct avs_dev *adev, bool enable)
void avs_hda_l1sen_enable(struct avs_dev *adev, bool enable)
{
+ if (avs_platattr_test(adev, ACE))
+ return;
if (enable) {
if (atomic_inc_and_test(&adev->l1sen_counter))
snd_hdac_chip_updatel(&adev->base.core, VS_EM2, AZX_VS_EM2_L1SEN,
@@ -99,6 +104,7 @@ static int avs_hdac_bus_init_streams(struct hdac_bus *bus)
static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
{
+ struct avs_dev *adev = hdac_to_avs(bus);
struct hdac_ext_link *hlink;
bool ret;
@@ -114,7 +120,8 @@ static bool avs_hdac_bus_init_chip(struct hdac_bus *bus, bool full_reset)
/* 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);
+ if (!avs_platattr_test(adev, ACE))
+ snd_hdac_chip_updatel(bus, VS_EM2, AZX_VS_EM2_DUM, AZX_VS_EM2_DUM);
return ret;
}
@@ -224,7 +231,6 @@ static void avs_hda_probe_work(struct work_struct *work)
/* 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);
}
@@ -445,7 +451,7 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
return ret;
}
- ret = pci_request_regions(pci, "AVS HDAudio");
+ ret = pcim_request_all_regions(pci, "AVS HDAudio");
if (ret < 0)
return ret;
@@ -454,8 +460,7 @@ static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
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;
+ return -ENXIO;
}
adev->dsp_ba = pci_ioremap_bar(pci, 4);
@@ -512,8 +517,6 @@ err_init_streams:
iounmap(adev->dsp_ba);
err_remap_bar4:
iounmap(bus->remap_addr);
-err_remap_bar0:
- pci_release_regions(pci);
return ret;
}
@@ -584,7 +587,6 @@ static void avs_pci_remove(struct pci_dev *pci)
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);
@@ -748,13 +750,16 @@ static const struct dev_pm_ops avs_dev_pm = {
static const struct avs_sram_spec skl_sram_spec = {
.base_offset = SKL_ADSP_SRAM_BASE_OFFSET,
.window_size = SKL_ADSP_SRAM_WINDOW_SIZE,
- .rom_status_offset = SKL_ADSP_SRAM_BASE_OFFSET,
};
static const struct avs_sram_spec apl_sram_spec = {
.base_offset = APL_ADSP_SRAM_BASE_OFFSET,
.window_size = APL_ADSP_SRAM_WINDOW_SIZE,
- .rom_status_offset = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_sram_spec mtl_sram_spec = {
+ .base_offset = MTL_ADSP_SRAM_BASE_OFFSET,
+ .window_size = MTL_ADSP_SRAM_WINDOW_SIZE,
};
static const struct avs_hipc_spec skl_hipc_spec = {
@@ -766,6 +771,19 @@ static const struct avs_hipc_spec skl_hipc_spec = {
.rsp_offset = SKL_ADSP_REG_HIPCT,
.rsp_busy_mask = SKL_ADSP_HIPCT_BUSY,
.ctl_offset = SKL_ADSP_REG_HIPCCTL,
+ .sts_offset = SKL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_hipc_spec apl_hipc_spec = {
+ .req_offset = SKL_ADSP_REG_HIPCI,
+ .req_ext_offset = SKL_ADSP_REG_HIPCIE,
+ .req_busy_mask = SKL_ADSP_HIPCI_BUSY,
+ .ack_offset = SKL_ADSP_REG_HIPCIE,
+ .ack_done_mask = SKL_ADSP_HIPCIE_DONE,
+ .rsp_offset = SKL_ADSP_REG_HIPCT,
+ .rsp_busy_mask = SKL_ADSP_HIPCT_BUSY,
+ .ctl_offset = SKL_ADSP_REG_HIPCCTL,
+ .sts_offset = APL_ADSP_SRAM_BASE_OFFSET,
};
static const struct avs_hipc_spec cnl_hipc_spec = {
@@ -777,6 +795,19 @@ static const struct avs_hipc_spec cnl_hipc_spec = {
.rsp_offset = CNL_ADSP_REG_HIPCTDR,
.rsp_busy_mask = CNL_ADSP_HIPCTDR_BUSY,
.ctl_offset = CNL_ADSP_REG_HIPCCTL,
+ .sts_offset = APL_ADSP_SRAM_BASE_OFFSET,
+};
+
+static const struct avs_hipc_spec lnl_hipc_spec = {
+ .req_offset = MTL_REG_HfIPCxIDR,
+ .req_ext_offset = MTL_REG_HfIPCxIDD,
+ .req_busy_mask = MTL_HfIPCxIDR_BUSY,
+ .ack_offset = MTL_REG_HfIPCxIDA,
+ .ack_done_mask = MTL_HfIPCxIDA_DONE,
+ .rsp_offset = MTL_REG_HfIPCxTDR,
+ .rsp_busy_mask = MTL_HfIPCxTDR_BUSY,
+ .ctl_offset = MTL_REG_HfIPCxCTL,
+ .sts_offset = LNL_REG_HfDFR(0),
};
static const struct avs_spec skl_desc = {
@@ -796,7 +827,7 @@ static const struct avs_spec apl_desc = {
.core_init_mask = 3,
.attributes = AVS_PLATATTR_IMR,
.sram = &apl_sram_spec,
- .hipc = &skl_hipc_spec,
+ .hipc = &apl_hipc_spec,
};
static const struct avs_spec cnl_desc = {
@@ -846,6 +877,16 @@ AVS_TGL_BASED_SPEC(ehl, 30);
AVS_TGL_BASED_SPEC(adl, 35);
AVS_TGL_BASED_SPEC(adl_n, 35);
+static const struct avs_spec fcl_desc = {
+ .name = "fcl",
+ .min_fw_version = { 0 },
+ .dsp_ops = &avs_ptl_dsp_ops,
+ .core_init_mask = 1,
+ .attributes = AVS_PLATATTR_IMR | AVS_PLATATTR_ACE | AVS_PLATATTR_ALTHDA,
+ .sram = &mtl_sram_spec,
+ .hipc = &lnl_hipc_spec,
+};
+
static const struct pci_device_id avs_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, &skl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_SKL, &skl_desc) },
@@ -881,6 +922,7 @@ static const struct pci_device_id avs_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, &adl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_RPL_M, &adl_desc) },
{ PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, &adl_desc) },
+ { PCI_DEVICE_DATA(INTEL, HDA_FCL, &fcl_desc) },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, avs_ids);
@@ -902,13 +944,14 @@ 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");
-MODULE_FIRMWARE("intel/skl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/apl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/cnl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/icl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/jsl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/lkf/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/tgl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/ehl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/adl/dsp_basefw.bin");
-MODULE_FIRMWARE("intel/adl_n/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/skl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/apl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/cnl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/icl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/jsl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/lkf/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/tgl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/ehl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/adl/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/avs/adl_n/dsp_basefw.bin");
+MODULE_FIRMWARE("intel/fcl/dsp_basefw.bin");
diff --git a/sound/soc/intel/avs/debugfs.c b/sound/soc/intel/avs/debugfs.c
index 8c4edda97f75..f508f215ecd2 100644
--- a/sound/soc/intel/avs/debugfs.c
+++ b/sound/soc/intel/avs/debugfs.c
@@ -144,7 +144,7 @@ static ssize_t probe_points_write(struct file *file, const char __user *from, si
int ret;
ret = parse_int_array_user(from, count, (int **)&array);
- if (ret < 0)
+ if (ret)
return ret;
num_elems = *array;
@@ -181,7 +181,7 @@ static ssize_t probe_points_disconnect_write(struct file *file, const char __use
int ret;
ret = parse_int_array_user(from, count, (int **)&array);
- if (ret < 0)
+ if (ret)
return ret;
num_elems = *array;
@@ -315,7 +315,6 @@ err_ipc:
if (!adev->logged_resources) {
avs_dsp_enable_d0ix(adev);
err_d0ix:
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
}
@@ -342,7 +341,6 @@ static int disable_logs(struct avs_dev *adev, u32 resource_mask)
/* If that's the last resource, allow for D3. */
if (!adev->logged_resources) {
avs_dsp_enable_d0ix(adev);
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
}
@@ -369,11 +367,14 @@ static ssize_t trace_control_write(struct file *file, const char __user *from, s
int ret;
ret = parse_int_array_user(from, count, (int **)&array);
- if (ret < 0)
+ if (ret)
return ret;
num_elems = *array;
- resource_mask = array[1];
+ if (!num_elems) {
+ ret = -EINVAL;
+ goto free_array;
+ }
/*
* Disable if just resource mask is provided - no log priority flags.
@@ -381,6 +382,7 @@ static ssize_t trace_control_write(struct file *file, const char __user *from, s
* Enable input format: mask, prio1, .., prioN
* Where 'N' equals number of bits set in the 'mask'.
*/
+ resource_mask = array[1];
if (num_elems == 1) {
ret = disable_logs(adev, resource_mask);
} else {
diff --git a/sound/soc/intel/avs/dsp.c b/sound/soc/intel/avs/dsp.c
index b9de691e9b9b..464bd6859182 100644
--- a/sound/soc/intel/avs/dsp.c
+++ b/sound/soc/intel/avs/dsp.c
@@ -12,8 +12,6 @@
#include "registers.h"
#include "trace.h"
-#define AVS_ADSPCS_INTERVAL_US 500
-#define AVS_ADSPCS_TIMEOUT_US 50000
#define AVS_ADSPCS_DELAY_US 1000
int avs_dsp_core_power(struct avs_dev *adev, u32 core_mask, bool power)
diff --git a/sound/soc/intel/avs/ipc.c b/sound/soc/intel/avs/ipc.c
index 08ed9d96738a..6bfb9d1a1ca8 100644
--- a/sound/soc/intel/avs/ipc.c
+++ b/sound/soc/intel/avs/ipc.c
@@ -141,7 +141,6 @@ static void avs_dsp_recovery(struct avs_dev *adev)
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);
@@ -169,7 +168,9 @@ static void avs_dsp_exception_caught(struct avs_dev *adev, union avs_notify_msg
dev_crit(adev->dev, "communication severed, rebooting dsp..\n");
- cancel_delayed_work_sync(&ipc->d0ix_work);
+ /* Avoid deadlock as the exception may be the response to SET_D0IX. */
+ if (current_work() != &ipc->d0ix_work.work)
+ cancel_delayed_work_sync(&ipc->d0ix_work);
ipc->in_d0ix = false;
/* Re-enabled on recovery completion. */
pm_runtime_disable(adev->dev);
diff --git a/sound/soc/intel/avs/lnl.c b/sound/soc/intel/avs/lnl.c
new file mode 100644
index 000000000000..03208596dfb1
--- /dev/null
+++ b/sound/soc/intel/avs/lnl.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2021-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "registers.h"
+
+int avs_lnl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+ struct hdac_bus *bus = &adev->base.core;
+ struct hdac_ext_link *hlink;
+ int ret;
+
+ ret = avs_mtl_core_stall(adev, core_mask, stall);
+
+ /* On unstall, route interrupts from the links to the DSP firmware. */
+ if (!ret && !stall)
+ list_for_each_entry(hlink, &bus->hlink_list, list)
+ snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_ML_LCTL_OFLEN,
+ AZX_ML_LCTL_OFLEN);
+ return ret;
+}
diff --git a/sound/soc/intel/avs/loader.c b/sound/soc/intel/avs/loader.c
index 0b29941feb0e..353e343b1d28 100644
--- a/sound/soc/intel/avs/loader.c
+++ b/sound/soc/intel/avs/loader.c
@@ -9,6 +9,7 @@
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/string.h>
#include <sound/hdaudio.h>
#include <sound/hdaudio_ext.h>
#include "avs.h"
@@ -310,7 +311,7 @@ avs_hda_init_rom(struct avs_dev *adev, unsigned int dma_id, bool purge)
}
/* await ROM init */
- ret = snd_hdac_adsp_readl_poll(adev, spec->sram->rom_status_offset, reg,
+ ret = snd_hdac_adsp_readl_poll(adev, spec->hipc->sts_offset, reg,
(reg & 0xF) == AVS_ROM_INIT_DONE ||
(reg & 0xF) == APL_ROM_FW_ENTERED,
AVS_ROM_INIT_POLLING_US, APL_ROM_INIT_TIMEOUT_US);
@@ -683,6 +684,7 @@ int avs_dsp_boot_firmware(struct avs_dev *adev, bool purge)
static int avs_dsp_alloc_resources(struct avs_dev *adev)
{
+ struct hdac_ext_link *link;
int ret, i;
ret = avs_ipc_get_hw_config(adev, &adev->hw_cfg);
@@ -693,6 +695,14 @@ static int avs_dsp_alloc_resources(struct avs_dev *adev)
if (ret)
return AVS_IPC_RET(ret);
+ /* If hw allows, read capabilities directly from it. */
+ if (avs_platattr_test(adev, ALTHDA)) {
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core,
+ AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (link)
+ adev->hw_cfg.i2s_caps.ctrl_count = link->slcount;
+ }
+
adev->core_refs = devm_kcalloc(adev->dev, adev->hw_cfg.dsp_cores,
sizeof(*adev->core_refs), GFP_KERNEL);
adev->lib_names = devm_kcalloc(adev->dev, adev->fw_cfg.max_libs_count,
diff --git a/sound/soc/intel/avs/messages.h b/sound/soc/intel/avs/messages.h
index 2f243802ccc2..55c04b0142ae 100644
--- a/sound/soc/intel/avs/messages.h
+++ b/sound/soc/intel/avs/messages.h
@@ -102,6 +102,8 @@ struct avs_tlv {
} __packed;
static_assert(sizeof(struct avs_tlv) == 8);
+#define avs_tlv_size(tlv) struct_size(tlv, value, (tlv)->length / 4)
+
enum avs_module_msg_type {
AVS_MOD_INIT_INSTANCE = 0,
AVS_MOD_LARGE_CONFIG_GET = 3,
@@ -697,8 +699,9 @@ enum avs_sample_type {
AVS_SAMPLE_TYPE_FLOAT = 4,
};
-#define AVS_CHANNELS_MAX 8
+#define AVS_COEFF_CHANNELS_MAX 8
#define AVS_ALL_CHANNELS_MASK UINT_MAX
+#define AVS_CHANNELS_MAX 16
struct avs_audio_format {
u32 sampling_freq;
@@ -786,6 +789,33 @@ union avs_gtw_attributes {
} __packed;
static_assert(sizeof(union avs_gtw_attributes) == 4);
+#define AVS_GTW_DMA_CONFIG_ID 0x1000
+#define AVS_DMA_METHOD_HDA 1
+
+struct avs_dma_device_stream_channel_map {
+ u32 device_address;
+ u32 channel_map;
+} __packed;
+static_assert(sizeof(struct avs_dma_device_stream_channel_map) == 8);
+
+struct avs_dma_stream_channel_map {
+ u32 device_count;
+ struct avs_dma_device_stream_channel_map map[16];
+} __packed;
+static_assert(sizeof(struct avs_dma_stream_channel_map) == 132);
+
+struct avs_dma_cfg {
+ u8 dma_method;
+ u8 pre_allocated;
+ u16 rsvd;
+ u32 dma_channel_id;
+ u32 stream_id;
+ struct avs_dma_stream_channel_map map;
+ u32 config_size;
+ u8 config[] __counted_by(config_size);
+} __packed;
+static_assert(sizeof(struct avs_dma_cfg) == 148);
+
struct avs_copier_gtw_cfg {
union avs_connector_node_id node_id;
u32 dma_buffer_size;
@@ -846,7 +876,7 @@ struct avs_updown_mixer_cfg {
struct avs_modcfg_base base;
u32 out_channel_config;
u32 coefficients_select;
- s32 coefficients[AVS_CHANNELS_MAX];
+ s32 coefficients[AVS_COEFF_CHANNELS_MAX];
u32 channel_map;
} __packed;
static_assert(sizeof(struct avs_updown_mixer_cfg) == 84);
diff --git a/sound/soc/intel/avs/mtl.c b/sound/soc/intel/avs/mtl.c
new file mode 100644
index 000000000000..e7b7915b2a82
--- /dev/null
+++ b/sound/soc/intel/avs/mtl.c
@@ -0,0 +1,200 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2021-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "registers.h"
+#include "trace.h"
+
+#define MTL_HfDSSGBL_BASE 0x1000
+#define MTL_REG_HfDSSCS (MTL_HfDSSGBL_BASE + 0x0)
+#define MTL_HfDSSCS_SPA BIT(16)
+#define MTL_HfDSSCS_CPA BIT(24)
+
+#define MTL_DSPCS_BASE 0x178D00
+#define MTL_REG_DSPCCTL (MTL_DSPCS_BASE + 0x4)
+#define MTL_DSPCCTL_SPA BIT(0)
+#define MTL_DSPCCTL_CPA BIT(8)
+#define MTL_DSPCCTL_OSEL GENMASK(25, 24)
+#define MTL_DSPCCTL_OSEL_HOST BIT(25)
+
+#define MTL_HfINT_BASE 0x1100
+#define MTL_REG_HfINTIPPTR (MTL_HfINT_BASE + 0x8)
+#define MTL_REG_HfHIPCIE (MTL_HfINT_BASE + 0x40)
+#define MTL_HfINTIPPTR_PTR GENMASK(20, 0)
+#define MTL_HfHIPCIE_IE BIT(0)
+
+#define MTL_DWICTL_INTENL_IE BIT(0)
+#define MTL_DWICTL_FINALSTATUSL_IPC BIT(0) /* same as ADSPIS_IPC */
+
+static int avs_mtl_core_power_on(struct avs_dev *adev)
+{
+ u32 reg;
+ int ret;
+
+ /* Power up DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, MTL_HfDSSCS_SPA);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "power dsp", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == MTL_HfDSSCS_CPA,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+ if (ret) {
+ dev_err(adev->dev, "power on domain dsp failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Prevent power gating of DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL, MTL_HfPWRCTL_WPDSPHPxPG,
+ MTL_HfPWRCTL_WPDSPHPxPG);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "prevent dsp PG", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfPWRSTS, reg,
+ (reg & MTL_HfPWRSTS_DSPHPxPGS) == MTL_HfPWRSTS_DSPHPxPGS,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+
+ /* Set ownership to HOST. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_OSEL, MTL_DSPCCTL_OSEL_HOST);
+ return ret;
+}
+
+static int avs_mtl_core_power_off(struct avs_dev *adev)
+{
+ u32 reg;
+
+ /* Allow power gating of DSP domain. No STS polling as HOST is only one of its users. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL, MTL_HfPWRCTL_WPDSPHPxPG, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "allow dsp pg", false);
+
+ /* Power down DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "power dsp", false);
+
+ return snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == 0,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+}
+
+int avs_mtl_core_power(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+ if (!core_mask)
+ return 0;
+
+ if (power)
+ return avs_mtl_core_power_on(adev);
+ return avs_mtl_core_power_off(adev);
+}
+
+int avs_mtl_core_reset(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ /* No logical equivalent on ACE 1.x. */
+ return 0;
+}
+
+int avs_mtl_core_stall(struct avs_dev *adev, u32 core_mask, bool stall)
+{
+ u32 value, reg;
+ int ret;
+
+ core_mask &= AVS_MAIN_CORE_MASK;
+ if (!core_mask)
+ return 0;
+
+ value = snd_hdac_adsp_readl(adev, MTL_REG_DSPCCTL);
+ trace_avs_dsp_core_op(value, core_mask, "stall", stall);
+ if (value == UINT_MAX)
+ return 0;
+
+ value = stall ? 0 : MTL_DSPCCTL_SPA;
+ snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_SPA, value);
+
+ value = stall ? 0 : MTL_DSPCCTL_CPA;
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_DSPCCTL,
+ reg, (reg & MTL_DSPCCTL_CPA) == value,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+ if (ret)
+ dev_err(adev->dev, "core_mask %d %sstall failed: %d\n",
+ core_mask, stall ? "" : "un", ret);
+ return ret;
+}
+
+static void avs_mtl_ipc_interrupt(struct avs_dev *adev)
+{
+ const struct avs_spec *spec = adev->spec;
+ u32 hipc_ack, hipc_rsp;
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY, 0);
+
+ hipc_ack = snd_hdac_adsp_readl(adev, spec->hipc->ack_offset);
+ hipc_rsp = snd_hdac_adsp_readl(adev, spec->hipc->rsp_offset);
+
+ /* DSP acked host's request. */
+ if (hipc_ack & spec->hipc->ack_done_mask) {
+ complete(&adev->ipc->done_completion);
+
+ /* Tell DSP it has our attention. */
+ snd_hdac_adsp_updatel(adev, spec->hipc->ack_offset, spec->hipc->ack_done_mask,
+ spec->hipc->ack_done_mask);
+ }
+
+ /* DSP sent new response to process. */
+ if (hipc_rsp & spec->hipc->rsp_busy_mask) {
+ union avs_reply_msg msg;
+
+ msg.primary = snd_hdac_adsp_readl(adev, MTL_REG_HfIPCxTDR);
+ msg.ext.val = snd_hdac_adsp_readl(adev, MTL_REG_HfIPCxTDD);
+
+ avs_dsp_process_response(adev, msg.val);
+
+ /* Tell DSP we accepted its message. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxTDR,
+ MTL_HfIPCxTDR_BUSY, MTL_HfIPCxTDR_BUSY);
+ /* Ack this response. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxTDA, MTL_HfIPCxTDA_BUSY, 0);
+ }
+
+ snd_hdac_adsp_updatel(adev, spec->hipc->ctl_offset,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY,
+ AVS_ADSP_HIPCCTL_DONE | AVS_ADSP_HIPCCTL_BUSY);
+}
+
+irqreturn_t avs_mtl_dsp_interrupt(struct avs_dev *adev)
+{
+ u32 adspis = snd_hdac_adsp_readl(adev, MTL_DWICTL_REG_FINALSTATUSL);
+ irqreturn_t ret = IRQ_NONE;
+
+ if (adspis == UINT_MAX)
+ return ret;
+
+ if (adspis & MTL_DWICTL_FINALSTATUSL_IPC) {
+ avs_mtl_ipc_interrupt(adev);
+ ret = IRQ_HANDLED;
+ }
+
+ return ret;
+}
+
+void avs_mtl_interrupt_control(struct avs_dev *adev, bool enable)
+{
+ if (enable) {
+ snd_hdac_adsp_updatel(adev, MTL_DWICTL_REG_INTENL, MTL_DWICTL_INTENL_IE,
+ MTL_DWICTL_INTENL_IE);
+ snd_hdac_adsp_updatew(adev, MTL_REG_HfHIPCIE, MTL_HfHIPCIE_IE, MTL_HfHIPCIE_IE);
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_DONE,
+ AVS_ADSP_HIPCCTL_DONE);
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_BUSY,
+ AVS_ADSP_HIPCCTL_BUSY);
+ } else {
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_BUSY, 0);
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfIPCxCTL, AVS_ADSP_HIPCCTL_DONE, 0);
+ snd_hdac_adsp_updatew(adev, MTL_REG_HfHIPCIE, MTL_HfHIPCIE_IE, 0);
+ snd_hdac_adsp_updatel(adev, MTL_DWICTL_REG_INTENL, MTL_DWICTL_INTENL_IE, 0);
+ }
+}
diff --git a/sound/soc/intel/avs/path.c b/sound/soc/intel/avs/path.c
index cafb8c6198be..e8e6b1c7fc90 100644
--- a/sound/soc/intel/avs/path.c
+++ b/sound/soc/intel/avs/path.c
@@ -131,9 +131,11 @@ int avs_path_set_constraint(struct avs_dev *adev, struct avs_tplg_path_template
list_for_each_entry(path_template, &template->path_list, node)
i++;
- rlist = kcalloc(i, sizeof(rlist), GFP_KERNEL);
- clist = kcalloc(i, sizeof(clist), GFP_KERNEL);
- slist = kcalloc(i, sizeof(slist), GFP_KERNEL);
+ rlist = kcalloc(i, sizeof(*rlist), GFP_KERNEL);
+ clist = kcalloc(i, sizeof(*clist), GFP_KERNEL);
+ slist = kcalloc(i, sizeof(*slist), GFP_KERNEL);
+ if (!rlist || !clist || !slist)
+ return -ENOMEM;
i = 0;
list_for_each_entry(path_template, &template->path_list, node) {
@@ -282,8 +284,51 @@ avs_nhlt_config_or_default(struct avs_dev *adev, struct avs_tplg_module *t)
return &fmtcfg->config;
}
+static int avs_append_dma_cfg(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw,
+ struct avs_tplg_module *t, u32 dma_id, size_t *cfg_size)
+{
+ u32 dma_type = t->cfg_ext->copier.dma_type;
+ struct avs_dma_cfg *dma;
+ struct avs_tlv *tlv;
+ size_t tlv_size;
+
+ if (!avs_platattr_test(adev, ALTHDA))
+ return 0;
+
+ switch (dma_type) {
+ case AVS_DMA_HDA_HOST_OUTPUT:
+ case AVS_DMA_HDA_HOST_INPUT:
+ case AVS_DMA_HDA_LINK_OUTPUT:
+ case AVS_DMA_HDA_LINK_INPUT:
+ return 0;
+ default:
+ break;
+ }
+
+ tlv_size = sizeof(*tlv) + sizeof(*dma);
+ if (*cfg_size + tlv_size > AVS_MAILBOX_SIZE)
+ return -E2BIG;
+
+ /* DMA config is a TLV tailing the existing payload. */
+ tlv = (struct avs_tlv *)&gtw->config.blob[gtw->config_length];
+ tlv->type = AVS_GTW_DMA_CONFIG_ID;
+ tlv->length = sizeof(*dma);
+
+ dma = (struct avs_dma_cfg *)tlv->value;
+ memset(dma, 0, sizeof(*dma));
+ dma->dma_method = AVS_DMA_METHOD_HDA;
+ dma->pre_allocated = true;
+ dma->dma_channel_id = dma_id;
+ dma->stream_id = dma_id + 1;
+
+ gtw->config_length += tlv_size / sizeof(u32);
+ *cfg_size += tlv_size;
+
+ return 0;
+}
+
static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *gtw,
- struct avs_tplg_module *t, size_t *cfg_size)
+ struct avs_tplg_module *t, u32 dma_id, size_t *cfg_size)
{
struct acpi_nhlt_config *blob;
size_t gtw_size;
@@ -300,7 +345,7 @@ static int avs_fill_gtw_config(struct avs_dev *adev, struct avs_copier_gtw_cfg *
memcpy(gtw->config.blob, blob->capabilities, blob->capabilities_size);
*cfg_size += gtw_size;
- return 0;
+ return avs_append_dma_cfg(adev, gtw, t, dma_id, cfg_size);
}
static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
@@ -317,7 +362,7 @@ static int avs_copier_create(struct avs_dev *adev, struct avs_path_module *mod)
dma_id = mod->owner->owner->dma_id;
cfg_size = offsetof(struct avs_copier_cfg, gtw_cfg.config);
- ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size);
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, dma_id, &cfg_size);
if (ret)
return ret;
@@ -351,7 +396,7 @@ static int avs_whm_create(struct avs_dev *adev, struct avs_path_module *mod)
dma_id = mod->owner->owner->dma_id;
cfg_size = offsetof(struct avs_whm_cfg, gtw_cfg.config);
- ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, &cfg_size);
+ ret = avs_fill_gtw_config(adev, &cfg->gtw_cfg, t, dma_id, &cfg_size);
if (ret)
return ret;
@@ -524,7 +569,7 @@ static int avs_updown_mix_create(struct avs_dev *adev, struct avs_path_module *m
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++)
+ for (i = 0; i < AVS_COEFF_CHANNELS_MAX; i++)
cfg.coefficients[i] = t->cfg_ext->updown_mix.coefficients[i];
cfg.channel_map = t->cfg_ext->updown_mix.channel_map;
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index d83ef504643b..67ce6675eea7 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -18,6 +18,7 @@
#include "path.h"
#include "pcm.h"
#include "topology.h"
+#include "utils.h"
#include "../../codecs/hda.h"
struct avs_dma_data {
@@ -36,6 +37,7 @@ struct avs_dma_data {
struct snd_pcm_hw_constraint_list sample_bits_list;
struct work_struct period_elapsed_work;
+ struct hdac_ext_link *link;
struct snd_pcm_substream *substream;
};
@@ -81,10 +83,8 @@ void avs_period_elapsed(struct snd_pcm_substream *substream)
static int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule);
static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_pcm_hw_constraint_list *r, *c, *s;
- struct avs_tplg_path_template *template;
struct avs_dma_data *data;
int ret;
@@ -97,8 +97,7 @@ static int avs_hw_constraints_init(struct snd_pcm_substream *substream, struct s
c = &(data->channels_list);
s = &(data->sample_bits_list);
- template = avs_dai_find_path_template(dai, !rtd->dai_link->no_pcm, substream->stream);
- ret = avs_path_set_constraint(data->adev, template, r, c, s);
+ ret = avs_path_set_constraint(data->adev, data->template, r, c, s);
if (ret <= 0)
return ret;
@@ -325,32 +324,75 @@ static const struct snd_soc_dai_ops avs_dai_nonhda_be_ops = {
.trigger = avs_dai_nonhda_be_trigger,
};
-static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+static int __avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai,
+ struct hdac_ext_link *link)
{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct hdac_ext_stream *link_stream;
struct avs_dma_data *data;
- struct hda_codec *codec;
int ret;
ret = avs_dai_startup(substream, dai);
if (ret)
return ret;
- codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
- link_stream = snd_hdac_ext_stream_assign(&codec->bus->core, substream,
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ link_stream = snd_hdac_ext_stream_assign(&data->adev->base.core, substream,
HDAC_EXT_STREAM_TYPE_LINK);
if (!link_stream) {
avs_dai_shutdown(substream, dai);
return -EBUSY;
}
- data = snd_soc_dai_get_dma_data(dai, substream);
data->link_stream = link_stream;
- substream->runtime->private_data = link_stream;
+ data->link = link;
return 0;
}
+static int avs_dai_hda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct hdac_ext_link *link;
+ struct avs_dma_data *data;
+ struct hda_codec *codec;
+ int ret;
+
+ codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
+
+ link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr);
+ if (!link)
+ return -EINVAL;
+
+ ret = __avs_dai_hda_be_startup(substream, dai, link);
+ if (!ret) {
+ data = snd_soc_dai_get_dma_data(dai, substream);
+ substream->runtime->private_data = data->link_stream;
+ }
+
+ return ret;
+}
+
+static int avs_dai_i2shda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->component->dev);
+ struct hdac_ext_link *link;
+
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_SSP);
+ if (!link)
+ return -EINVAL;
+ return __avs_dai_hda_be_startup(substream, dai, link);
+}
+
+static int avs_dai_dmichda_be_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dev *adev = to_avs_dev(dai->component->dev);
+ struct hdac_ext_link *link;
+
+ link = snd_hdac_ext_bus_get_hlink_by_id(&adev->base.core, AZX_REG_ML_LEPTR_ID_INTEL_DMIC);
+ if (!link)
+ return -EINVAL;
+ return __avs_dai_hda_be_startup(substream, dai, link);
+}
+
static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
{
struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
@@ -360,6 +402,14 @@ static void avs_dai_hda_be_shutdown(struct snd_pcm_substream *substream, struct
avs_dai_shutdown(substream, dai);
}
+static void avs_dai_althda_be_shutdown(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct avs_dma_data *data = snd_soc_dai_get_dma_data(dai, substream);
+
+ snd_hdac_ext_stream_release(data->link_stream, HDAC_EXT_STREAM_TYPE_LINK);
+ avs_dai_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)
{
@@ -375,13 +425,8 @@ static int avs_dai_hda_be_hw_params(struct snd_pcm_substream *substream,
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_soc_substream_to_rtd(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);
+ struct avs_dma_data *data;
data = snd_soc_dai_get_dma_data(dai, substream);
if (!data->path)
@@ -393,54 +438,43 @@ static int avs_dai_hda_be_hw_free(struct snd_pcm_substream *substream, struct sn
data->path = NULL;
/* clear link <-> stream mapping */
- codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
- link = snd_hdac_ext_bus_get_hlink_by_addr(&codec->bus->core, codec->core.addr);
- if (!link)
- return -EINVAL;
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_hdac_ext_bus_link_clear_stream_id(link, hdac_stream(link_stream)->stream_tag);
+ snd_hdac_ext_bus_link_clear_stream_id(data->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_soc_substream_to_rtd(substream);
- struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *be = snd_soc_substream_to_rtd(substream);
const struct snd_soc_pcm_stream *stream_info;
struct hdac_ext_stream *link_stream;
- struct hdac_ext_link *link;
+ const struct snd_pcm_hw_params *p;
struct avs_dma_data *data;
- struct hda_codec *codec;
- struct hdac_bus *bus;
unsigned int format_val;
unsigned int bits;
int ret;
data = snd_soc_dai_get_dma_data(dai, substream);
link_stream = data->link_stream;
+ p = &be->dpcm[substream->stream].hw_params;
if (link_stream->link_prepared)
return 0;
- codec = dev_to_hda_codec(snd_soc_rtd_to_codec(rtd, 0)->dev);
- bus = &codec->bus->core;
stream_info = snd_soc_dai_get_pcm_stream(dai, substream->stream);
- bits = snd_hdac_stream_format_bits(runtime->format, runtime->subformat,
+ bits = snd_hdac_stream_format_bits(params_format(p), params_subformat(p),
stream_info->sig_bits);
- format_val = snd_hdac_stream_format(runtime->channels, bits, runtime->rate);
+ format_val = snd_hdac_stream_format(params_channels(p), bits, params_rate(p));
- snd_hdac_ext_stream_decouple(bus, link_stream, true);
+ snd_hdac_ext_stream_decouple(&data->adev->base.core, link_stream, true);
snd_hdac_ext_stream_reset(link_stream);
snd_hdac_ext_stream_setup(link_stream, format_val);
- link = snd_hdac_ext_bus_get_hlink_by_addr(bus, codec->core.addr);
- if (!link)
- return -EINVAL;
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_hdac_ext_bus_link_set_stream_id(link, hdac_stream(link_stream)->stream_tag);
+ snd_hdac_ext_bus_link_set_stream_id(data->link,
+ hdac_stream(link_stream)->stream_tag);
ret = avs_dai_prepare(substream, dai);
if (ret)
@@ -515,6 +549,24 @@ static const struct snd_soc_dai_ops avs_dai_hda_be_ops = {
.trigger = avs_dai_hda_be_trigger,
};
+static const struct snd_soc_dai_ops avs_dai_i2shda_be_ops = {
+ .startup = avs_dai_i2shda_be_startup,
+ .shutdown = avs_dai_althda_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 struct snd_soc_dai_ops avs_dai_dmichda_be_ops = {
+ .startup = avs_dai_dmichda_be_startup,
+ .shutdown = avs_dai_althda_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 int hw_rule_param_size(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule)
{
struct snd_interval *interval = hw_param_interval(params, rule->var);
@@ -927,7 +979,6 @@ static int avs_component_load_libraries(struct avs_soc_component *acomp)
if (!ret)
ret = avs_module_info_init(adev, false);
- pm_runtime_mark_last_busy(adev->dev);
pm_runtime_put_autosuspend(adev->dev);
return ret;
@@ -1314,7 +1365,7 @@ static int avs_component_construct(struct snd_soc_component *component,
return 0;
}
-static const struct snd_soc_component_driver avs_component_driver = {
+static struct snd_soc_component_driver avs_component_driver = {
.name = "avs-pcm",
.probe = avs_component_probe,
.remove = avs_component_remove,
@@ -1329,7 +1380,7 @@ static const struct snd_soc_component_driver avs_component_driver = {
};
int avs_soc_component_register(struct device *dev, const char *name,
- const struct snd_soc_component_driver *drv,
+ struct snd_soc_component_driver *drv,
struct snd_soc_dai_driver *cpu_dais, int num_cpu_dais)
{
struct avs_soc_component *acomp;
@@ -1347,13 +1398,14 @@ int avs_soc_component_register(struct device *dev, const char *name,
acomp->base.name = name;
INIT_LIST_HEAD(&acomp->node);
+ drv->use_dai_pcm_id = !obsolete_card_names;
+
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,
@@ -1364,7 +1416,6 @@ static struct snd_soc_dai_driver dmic_cpu_dais[] = {
},
{
.name = "DMIC WoV Pin",
- .ops = &avs_dai_nonhda_be_ops,
.capture = {
.stream_name = "DMIC WoV Rx",
.channels_min = 1,
@@ -1377,15 +1428,23 @@ static struct snd_soc_dai_driver dmic_cpu_dais[] = {
int avs_dmic_platform_register(struct avs_dev *adev, const char *name)
{
+ const struct snd_soc_dai_ops *ops;
+
+ if (avs_platattr_test(adev, ALTHDA))
+ ops = &avs_dai_dmichda_be_ops;
+ else
+ ops = &avs_dai_nonhda_be_ops;
+
+ dmic_cpu_dais[0].ops = ops;
+ dmic_cpu_dais[1].ops = ops;
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,
+ .channels_max = AVS_CHANNELS_MAX,
.rates = SNDRV_PCM_RATE_8000_192000 |
SNDRV_PCM_RATE_12000 |
SNDRV_PCM_RATE_24000 |
@@ -1398,7 +1457,7 @@ static const struct snd_soc_dai_driver i2s_dai_template = {
},
.capture = {
.channels_min = 1,
- .channels_max = 8,
+ .channels_max = AVS_CHANNELS_MAX,
.rates = SNDRV_PCM_RATE_8000_192000 |
SNDRV_PCM_RATE_12000 |
SNDRV_PCM_RATE_24000 |
@@ -1415,10 +1474,15 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
unsigned long *tdms)
{
struct snd_soc_dai_driver *cpus, *dai;
+ const struct snd_soc_dai_ops *ops;
size_t ssp_count, cpu_count;
int i, j;
ssp_count = adev->hw_cfg.i2s_caps.ctrl_count;
+ if (avs_platattr_test(adev, ALTHDA))
+ ops = &avs_dai_i2shda_be_ops;
+ else
+ ops = &avs_dai_nonhda_be_ops;
cpu_count = 0;
for_each_set_bit(i, &port_mask, ssp_count)
@@ -1446,6 +1510,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
return -ENOMEM;
+ dai->ops = ops;
dai++;
}
}
@@ -1454,7 +1519,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
goto plat_register;
for_each_set_bit(i, &port_mask, ssp_count) {
- for_each_set_bit(j, &tdms[i], ssp_count) {
+ for_each_set_bit(j, &tdms[i], AVS_CHANNELS_MAX) {
memcpy(dai, &i2s_dai_template, sizeof(*dai));
dai->name =
@@ -1466,6 +1531,7 @@ int avs_i2s_platform_register(struct avs_dev *adev, const char *name, unsigned l
if (!dai->name || !dai->playback.stream_name || !dai->capture.stream_name)
return -ENOMEM;
+ dai->ops = ops;
dai++;
}
}
@@ -1479,7 +1545,7 @@ static const struct snd_soc_dai_driver hda_cpu_dai = {
.ops = &avs_dai_hda_be_ops,
.playback = {
.channels_min = 1,
- .channels_max = 8,
+ .channels_max = AVS_CHANNELS_MAX,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
@@ -1489,7 +1555,7 @@ static const struct snd_soc_dai_driver hda_cpu_dai = {
},
.capture = {
.channels_min = 1,
- .channels_max = 8,
+ .channels_max = AVS_CHANNELS_MAX,
.rates = SNDRV_PCM_RATE_8000_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S32_LE,
@@ -1503,11 +1569,13 @@ static void avs_component_hda_unregister_dais(struct snd_soc_component *componen
{
struct snd_soc_acpi_mach *mach;
struct snd_soc_dai *dai, *save;
+ struct avs_mach_pdata *pdata;
struct hda_codec *codec;
char name[32];
mach = dev_get_platdata(component->card->dev);
- codec = mach->pdata;
+ pdata = mach->pdata;
+ codec = pdata->codec;
snprintf(name, sizeof(name), "%s-cpu", dev_name(&codec->core.dev));
for_each_component_dais_safe(component, dai, save) {
@@ -1528,6 +1596,7 @@ 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 avs_mach_pdata *pdata;
struct hda_codec *codec;
struct hda_pcm *pcm;
const char *cname;
@@ -1537,7 +1606,8 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
if (!mach)
return -EINVAL;
- codec = mach->pdata;
+ pdata = mach->pdata;
+ codec = pdata->codec;
if (list_empty(&codec->pcm_list_head))
return -EINVAL;
list_for_each_entry(pcm, &codec->pcm_list_head, list)
@@ -1671,7 +1741,7 @@ static int avs_component_hda_open(struct snd_soc_component *component,
return 0;
}
-static const struct snd_soc_component_driver avs_hda_component_driver = {
+static struct snd_soc_component_driver avs_hda_component_driver = {
.name = "avs-hda-pcm",
.probe = avs_component_hda_probe,
.remove = avs_component_hda_remove,
diff --git a/sound/soc/intel/avs/probes.c b/sound/soc/intel/avs/probes.c
index f0b010956303..a42736b9aa55 100644
--- a/sound/soc/intel/avs/probes.c
+++ b/sound/soc/intel/avs/probes.c
@@ -284,7 +284,7 @@ static struct snd_soc_dai_driver probe_cpu_dais[] = {
},
};
-static const struct snd_soc_component_driver avs_probe_component_driver = {
+static struct snd_soc_component_driver avs_probe_component_driver = {
.name = "avs-probe-compr",
.compress_ops = &avs_probe_compress_ops,
.module_get_upon_open = 1, /* increment refcount when a stream is opened */
diff --git a/sound/soc/intel/avs/ptl.c b/sound/soc/intel/avs/ptl.c
new file mode 100644
index 000000000000..2be4b545c91d
--- /dev/null
+++ b/sound/soc/intel/avs/ptl.c
@@ -0,0 +1,98 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright(c) 2024-2025 Intel Corporation
+ *
+ * Authors: Cezary Rojewski <cezary.rojewski@intel.com>
+ * Amadeusz Slawinski <amadeuszx.slawinski@linux.intel.com>
+ */
+
+#include <sound/hdaudio_ext.h>
+#include "avs.h"
+#include "registers.h"
+#include "trace.h"
+
+#define MTL_HfDSSGBL_BASE 0x1000
+#define MTL_REG_HfDSSCS (MTL_HfDSSGBL_BASE + 0x0)
+#define MTL_HfDSSCS_SPA BIT(16)
+#define MTL_HfDSSCS_CPA BIT(24)
+
+#define MTL_DSPCS_BASE 0x178D00
+#define MTL_REG_DSPCCTL (MTL_DSPCS_BASE + 0x4)
+#define MTL_DSPCCTL_OSEL GENMASK(25, 24)
+#define MTL_DSPCCTL_OSEL_HOST BIT(25)
+
+static int avs_ptl_core_power_on(struct avs_dev *adev)
+{
+ u32 reg;
+ int ret;
+
+ /* Power up DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, MTL_HfDSSCS_SPA);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "power dsp", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == MTL_HfDSSCS_CPA,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+ if (ret) {
+ dev_err(adev->dev, "power on domain dsp failed: %d\n", ret);
+ return ret;
+ }
+
+ /* Prevent power gating of DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL2, MTL_HfPWRCTL2_WPDSPHPxPG,
+ MTL_HfPWRCTL2_WPDSPHPxPG);
+ trace_avs_dsp_core_op(1, AVS_MAIN_CORE_MASK, "prevent dsp PG", true);
+
+ ret = snd_hdac_adsp_readl_poll(adev, MTL_REG_HfPWRSTS2, reg,
+ (reg & MTL_HfPWRSTS2_DSPHPxPGS) == MTL_HfPWRSTS2_DSPHPxPGS,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+
+ /* Set ownership to HOST. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_DSPCCTL, MTL_DSPCCTL_OSEL, MTL_DSPCCTL_OSEL_HOST);
+ return ret;
+}
+
+static int avs_ptl_core_power_off(struct avs_dev *adev)
+{
+ u32 reg;
+
+ /* Allow power gating of DSP domain. No STS polling as HOST is only one of its users. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfPWRCTL2, MTL_HfPWRCTL2_WPDSPHPxPG, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "allow dsp pg", false);
+
+ /* Power down DSP domain. */
+ snd_hdac_adsp_updatel(adev, MTL_REG_HfDSSCS, MTL_HfDSSCS_SPA, 0);
+ trace_avs_dsp_core_op(0, AVS_MAIN_CORE_MASK, "power dsp", false);
+
+ return snd_hdac_adsp_readl_poll(adev, MTL_REG_HfDSSCS, reg,
+ (reg & MTL_HfDSSCS_CPA) == 0,
+ AVS_ADSPCS_INTERVAL_US, AVS_ADSPCS_TIMEOUT_US);
+}
+
+static int avs_ptl_core_power(struct avs_dev *adev, u32 core_mask, bool power)
+{
+ core_mask &= AVS_MAIN_CORE_MASK;
+ if (!core_mask)
+ return 0;
+
+ if (power)
+ return avs_ptl_core_power_on(adev);
+ return avs_ptl_core_power_off(adev);
+}
+
+const struct avs_dsp_ops avs_ptl_dsp_ops = {
+ .power = avs_ptl_core_power,
+ .reset = avs_mtl_core_reset,
+ .stall = avs_lnl_core_stall,
+ .dsp_interrupt = avs_mtl_dsp_interrupt,
+ .int_control = avs_mtl_interrupt_control,
+ .load_basefw = avs_hda_load_basefw,
+ .load_lib = avs_hda_load_library,
+ .transfer_mods = avs_hda_transfer_modules,
+ .log_buffer_offset = avs_icl_log_buffer_offset,
+ .log_buffer_status = avs_apl_log_buffer_status,
+ .coredump = avs_apl_coredump,
+ .d0ix_toggle = avs_icl_d0ix_toggle,
+ .set_d0ix = avs_icl_set_d0ix,
+ AVS_SET_ENABLE_LOGS_OP(icl)
+};
diff --git a/sound/soc/intel/avs/registers.h b/sound/soc/intel/avs/registers.h
index 368ede05f2cd..97767882ffa1 100644
--- a/sound/soc/intel/avs/registers.h
+++ b/sound/soc/intel/avs/registers.h
@@ -35,6 +35,8 @@
#define AVS_ADSPCS_CSTALL_MASK(cm) ((cm) << 8)
#define AVS_ADSPCS_SPA_MASK(cm) ((cm) << 16)
#define AVS_ADSPCS_CPA_MASK(cm) ((cm) << 24)
+#define AVS_ADSPCS_INTERVAL_US 500
+#define AVS_ADSPCS_TIMEOUT_US 10000
#define AVS_MAIN_CORE_MASK BIT(0)
#define AVS_ADSP_HIPCCTL_BUSY BIT(0)
@@ -67,14 +69,50 @@
#define CNL_ADSP_HIPCIDR_BUSY BIT(31)
#define CNL_ADSP_HIPCIDA_DONE BIT(31)
+/* MTL Intel HOST Inter-Processor Communication Registers */
+#define MTL_HfIPC_BASE 0x73000
+#define MTL_REG_HfIPCxTDR (MTL_HfIPC_BASE + 0x200)
+#define MTL_REG_HfIPCxTDA (MTL_HfIPC_BASE + 0x204)
+#define MTL_REG_HfIPCxIDR (MTL_HfIPC_BASE + 0x210)
+#define MTL_REG_HfIPCxIDA (MTL_HfIPC_BASE + 0x214)
+#define MTL_REG_HfIPCxCTL (MTL_HfIPC_BASE + 0x228)
+#define MTL_REG_HfIPCxTDD (MTL_HfIPC_BASE + 0x300)
+#define MTL_REG_HfIPCxIDD (MTL_HfIPC_BASE + 0x380)
+
+#define MTL_HfIPCxTDR_BUSY BIT(31)
+#define MTL_HfIPCxTDA_BUSY BIT(31)
+#define MTL_HfIPCxIDR_BUSY BIT(31)
+#define MTL_HfIPCxIDA_DONE BIT(31)
+
+#define MTL_HfFLV_BASE 0x162000
+#define MTL_REG_HfFLGP(x, y) (MTL_HfFLV_BASE + 0x1200 + (x) * 0x20 + (y) * 0x08)
+#define LNL_REG_HfDFR(x) (0x160200 + (x) * 0x8)
+
+#define MTL_DWICTL_BASE 0x1800
+#define MTL_DWICTL_REG_INTENL (MTL_DWICTL_BASE + 0x0)
+#define MTL_DWICTL_REG_FINALSTATUSL (MTL_DWICTL_BASE + 0x30)
+
+#define MTL_HfPMCCU_BASE 0x1D00
+#define MTL_REG_HfCLKCTL (MTL_HfPMCCU_BASE + 0x10)
+#define MTL_REG_HfPWRCTL (MTL_HfPMCCU_BASE + 0x18)
+#define MTL_REG_HfPWRSTS (MTL_HfPMCCU_BASE + 0x1C)
+#define MTL_REG_HfPWRCTL2 (MTL_HfPMCCU_BASE + 0x20)
+#define MTL_REG_HfPWRSTS2 (MTL_HfPMCCU_BASE + 0x24)
+#define MTL_HfPWRCTL_WPDSPHPxPG BIT(0)
+#define MTL_HfPWRSTS_DSPHPxPGS BIT(0)
+#define MTL_HfPWRCTL2_WPDSPHPxPG BIT(0)
+#define MTL_HfPWRSTS2_DSPHPxPGS BIT(0)
+
/* 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
+#define MTL_ADSP_SRAM_BASE_OFFSET 0x180000
+#define MTL_ADSP_SRAM_WINDOW_SIZE 0x8000
/* Constants used when accessing SRAM, space shared with firmware */
-#define AVS_FW_REG_BASE(adev) ((adev)->spec->sram->base_offset)
+#define AVS_FW_REG_BASE(adev) ((adev)->spec->hipc->sts_offset)
#define AVS_FW_REG_STATUS(adev) (AVS_FW_REG_BASE(adev) + 0x0)
#define AVS_FW_REG_ERROR(adev) (AVS_FW_REG_BASE(adev) + 0x4)
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index 3c222c352701..f2e4ad8b8e14 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -1668,8 +1668,8 @@ static int avs_widget_load(struct snd_soc_component *comp, int index,
/* See parse_link_formatted_string() for dynamic naming when(s). */
if (avs_mach_singular_tdm(mach, ssp_port)) {
- /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 10 + '\0' */
- size_t size = strlen(dw->name) + 2;
+ /* size is based on possible %d -> SSP:TDM, where SSP and TDM < 16 + '\0' */
+ size_t size = strlen(dw->name) + 3;
char *buf;
tdm_slot = avs_mach_ssp_tdm(mach, ssp_port);
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index 304880997717..f5601a4e3ec8 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -87,7 +87,7 @@ struct avs_tplg_modcfg_ext {
struct {
u32 out_channel_config;
u32 coefficients_select;
- s32 coefficients[AVS_CHANNELS_MAX];
+ s32 coefficients[AVS_COEFF_CHANNELS_MAX];
u32 channel_map;
} updown_mix;
struct {
diff --git a/sound/soc/intel/avs/utils.h b/sound/soc/intel/avs/utils.h
index 5ee569c39380..955a40d2c30c 100644
--- a/sound/soc/intel/avs/utils.h
+++ b/sound/soc/intel/avs/utils.h
@@ -11,6 +11,16 @@
#include <sound/soc-acpi.h>
+extern bool obsolete_card_names;
+
+struct avs_mach_pdata {
+ struct hda_codec *codec;
+ unsigned long *tdms;
+ char *codec_name; /* DMIC only */
+
+ bool obsolete_card_names;
+};
+
static inline bool avs_mach_singular_ssp(struct snd_soc_acpi_mach *mach)
{
return hweight_long(mach->mach_params.i2s_link_mask) == 1;
@@ -23,14 +33,16 @@ static inline u32 avs_mach_ssp_port(struct snd_soc_acpi_mach *mach)
static inline bool avs_mach_singular_tdm(struct snd_soc_acpi_mach *mach, u32 port)
{
- unsigned long *tdms = mach->pdata;
+ struct avs_mach_pdata *pdata = mach->pdata;
+ unsigned long *tdms = pdata->tdms;
return !tdms || (hweight_long(tdms[port]) == 1);
}
static inline u32 avs_mach_ssp_tdm(struct snd_soc_acpi_mach *mach, u32 port)
{
- unsigned long *tdms = mach->pdata;
+ struct avs_mach_pdata *pdata = mach->pdata;
+ unsigned long *tdms = pdata->tdms;
return tdms ? __ffs(tdms[port]) : 0;
}
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index 9b80b19bb8d0..c23fdb6aad4c 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -11,7 +11,7 @@ menuconfig SND_SOC_INTEL_MACH
kernel: saying N will just cause the configurator to skip all
the questions about Intel ASoC machine drivers.
-if SND_SOC_INTEL_MACH
+if SND_SOC_INTEL_MACH && (SND_SOC_SOF_INTEL_COMMON || !SND_SOC_SOF_INTEL_COMMON)
config SND_SOC_INTEL_USER_FRIENDLY_LONG_NAMES
bool "Use more user friendly long card names"
@@ -42,6 +42,7 @@ config SND_SOC_INTEL_SOF_NUVOTON_COMMON
tristate
config SND_SOC_INTEL_SOF_BOARD_HELPERS
+ select SND_SOC_ACPI_INTEL_MATCH
tristate
if SND_SOC_INTEL_CATPT
@@ -252,14 +253,6 @@ config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH
endif ## SND_SST_ATOM_HIFI2_PLATFORM
-config SND_SOC_INTEL_DA7219_MAX98357A_GENERIC
- tristate
- select SND_SOC_DA7219
- select SND_SOC_MAX98357A
- select SND_SOC_DMIC
- select SND_SOC_HDAC_HDMI
- select SND_SOC_INTEL_HDA_DSP_COMMON
-
if SND_SOC_SOF_APOLLOLAKE
config SND_SOC_INTEL_SOF_WM8804_MACH
diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c
index aaef212cf44e..54c1894ee96a 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5672.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5672.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/clk.h>
+#include <linux/string.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -458,7 +459,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
if (!drv)
return -ENOMEM;
- strcpy(drv->codec_name, RT5672_I2C_DEFAULT);
+ strscpy(drv->codec_name, RT5672_I2C_DEFAULT, sizeof(drv->codec_name));
/* find index of codec dai */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++) {
diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c
index 0554c7e2cb34..519218385fdf 100644
--- a/sound/soc/intel/boards/skl_hda_dsp_generic.c
+++ b/sound/soc/intel/boards/skl_hda_dsp_generic.c
@@ -85,6 +85,18 @@ skl_hda_get_board_quirk(struct snd_soc_acpi_mach_params *mach_params)
return board_quirk;
}
+static int skl_hda_add_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link)
+{
+ struct sof_card_private *ctx = snd_soc_card_get_drvdata(card);
+
+ /* Ignore the HDMI PCM link if iDisp is not present */
+ if (strstr(link->stream_name, "HDMI") && !ctx->hdmi.idisp_codec)
+ link->ignore = true;
+
+ return 0;
+}
+
static int skl_hda_audio_probe(struct platform_device *pdev)
{
struct snd_soc_acpi_mach *mach = pdev->dev.platform_data;
@@ -101,6 +113,7 @@ static int skl_hda_audio_probe(struct platform_device *pdev)
card->owner = THIS_MODULE;
card->fully_routed = true;
card->late_probe = skl_hda_card_late_probe;
+ card->add_dai_link = skl_hda_add_dai_link;
dev_dbg(&pdev->dev, "board_quirk = %lx\n", board_quirk);
diff --git a/sound/soc/intel/boards/sof_es8336.c b/sound/soc/intel/boards/sof_es8336.c
index a0b3679b17b4..1211a2b8a2a2 100644
--- a/sound/soc/intel/boards/sof_es8336.c
+++ b/sound/soc/intel/boards/sof_es8336.c
@@ -826,6 +826,16 @@ static const struct platform_device_id board_ids[] = {
SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK |
SOF_ES8336_JD_INVERTED),
},
+ {
+ .name = "ptl_es83x6_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_ES8336_SSP_CODEC(1) |
+ SOF_NO_OF_HDMI_CAPTURE_SSP(2) |
+ SOF_HDMI_CAPTURE_1_SSP(0) |
+ SOF_HDMI_CAPTURE_2_SSP(2) |
+ SOF_SSP_HDMI_CAPTURE_PRESENT |
+ SOF_ES8336_SPEAKERS_EN_GPIO1_QUIRK |
+ SOF_ES8336_JD_INVERTED),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_rt5682.c b/sound/soc/intel/boards/sof_rt5682.c
index f5925bd0a3fc..4994aaccc583 100644
--- a/sound/soc/intel/boards/sof_rt5682.c
+++ b/sound/soc/intel/boards/sof_rt5682.c
@@ -892,6 +892,13 @@ static const struct platform_device_id board_ids[] = {
SOF_SSP_PORT_BT_OFFLOAD(2) |
SOF_BT_OFFLOAD_PRESENT),
},
+ {
+ .name = "ptl_rt5682_c1_h02",
+ .driver_data = (kernel_ulong_t)(SOF_RT5682_MCLK_EN |
+ SOF_SSP_PORT_CODEC(1) |
+ /* SSP 0 and SSP 2 are used for HDMI IN */
+ SOF_SSP_MASK_HDMI_CAPTURE(0x5)),
+ },
{ }
};
MODULE_DEVICE_TABLE(platform, board_ids);
diff --git a/sound/soc/intel/boards/sof_sdw.c b/sound/soc/intel/boards/sof_sdw.c
index 095d08b3fc82..c639df2cacdd 100644
--- a/sound/soc/intel/boards/sof_sdw.c
+++ b/sound/soc/intel/boards/sof_sdw.c
@@ -759,12 +759,33 @@ static const struct dmi_system_id sof_sdw_quirk_table[] = {
SOF_BT_OFFLOAD_SSP(2) |
SOF_SSP_BT_OFFLOAD_PRESENT),
},
+ /* Wildcatlake devices*/
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Intel_wclrvp"),
+ },
+ .driver_data = (void *)(SOC_SDW_PCH_DMIC),
+ },
+ {
+ .callback = sof_sdw_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Google"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Ocelot"),
+ },
+ .driver_data = (void *)(SOC_SDW_PCH_DMIC |
+ SOF_BT_OFFLOAD_SSP(2) |
+ SOF_SSP_BT_OFFLOAD_PRESENT),
+ },
{}
};
static const struct snd_pci_quirk sof_sdw_ssid_quirk_table[] = {
SND_PCI_QUIRK(0x1043, 0x1e13, "ASUS Zenbook S14", SOC_SDW_CODEC_MIC),
SND_PCI_QUIRK(0x1043, 0x1f43, "ASUS Zenbook S16", SOC_SDW_CODEC_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x2347, "Lenovo P16", SOC_SDW_CODEC_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x2348, "Lenovo P16", SOC_SDW_CODEC_MIC),
+ SND_PCI_QUIRK(0x17aa, 0x2349, "Lenovo P1", SOC_SDW_CODEC_MIC),
{}
};
@@ -780,13 +801,6 @@ static void sof_sdw_check_ssid_quirk(const struct snd_soc_acpi_mach *mach)
sof_sdw_quirk = quirk_entry->value;
}
-static struct snd_soc_dai_link_component platform_component[] = {
- {
- /* name might be overridden during probe */
- .name = "0000:00:1f.3"
- }
-};
-
static const struct snd_soc_ops sdw_ops = {
.startup = asoc_sdw_startup,
.prepare = asoc_sdw_prepare,
@@ -836,6 +850,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
struct snd_soc_dai_link_ch_map *codec_maps;
struct snd_soc_dai_link_component *codecs;
struct snd_soc_dai_link_component *cpus;
+ struct snd_soc_dai_link_component *platform;
int num_cpus = hweight32(sof_dai->link_mask[stream]);
int num_codecs = sof_dai->num_devs[stream];
int playback, capture;
@@ -876,6 +891,10 @@ static int create_sdw_dailink(struct snd_soc_card *card,
if (!codecs)
return -ENOMEM;
+ platform = devm_kzalloc(dev, sizeof(*platform), GFP_KERNEL);
+ if (!platform)
+ return -ENOMEM;
+
codec_maps = devm_kcalloc(dev, num_codecs, sizeof(*codec_maps), GFP_KERNEL);
if (!codec_maps)
return -ENOMEM;
@@ -917,8 +936,7 @@ static int create_sdw_dailink(struct snd_soc_card *card,
capture = (stream == SNDRV_PCM_STREAM_CAPTURE);
asoc_sdw_init_dai_link(dev, *dai_links, be_id, name, playback, capture,
- cpus, num_cpus, platform_component,
- ARRAY_SIZE(platform_component), codecs, num_codecs,
+ cpus, num_cpus, platform, 1, codecs, num_codecs,
1, asoc_sdw_rtd_init, &sdw_ops);
/*
@@ -994,8 +1012,7 @@ static int create_ssp_dailinks(struct snd_soc_card *card,
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
playback, capture, cpu_dai_name,
- platform_component->name,
- ARRAY_SIZE(platform_component), codec_name,
+ "dummy", codec_name,
ssp_info->dais[0].dai_name, 1, NULL,
ssp_info->ops);
if (ret)
@@ -1019,8 +1036,7 @@ static int create_dmic_dailinks(struct snd_soc_card *card,
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "dmic01",
0, 1, // DMIC only supports capture
- "DMIC01 Pin", platform_component->name,
- ARRAY_SIZE(platform_component),
+ "DMIC01 Pin", "dummy",
"dmic-codec", "dmic-hifi", 1,
asoc_sdw_dmic_init, NULL);
if (ret)
@@ -1030,8 +1046,7 @@ static int create_dmic_dailinks(struct snd_soc_card *card,
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, "dmic16k",
0, 1, // DMIC only supports capture
- "DMIC16k Pin", platform_component->name,
- ARRAY_SIZE(platform_component),
+ "DMIC16k Pin", "dummy",
"dmic-codec", "dmic-hifi", 1,
/* don't call asoc_sdw_dmic_init() twice */
NULL, NULL);
@@ -1074,8 +1089,7 @@ static int create_hdmi_dailinks(struct snd_soc_card *card,
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
1, 0, // HDMI only supports playback
- cpu_dai_name, platform_component->name,
- ARRAY_SIZE(platform_component),
+ cpu_dai_name, "dummy",
codec_name, codec_dai_name, 1,
i == 0 ? sof_sdw_hdmi_init : NULL, NULL);
if (ret)
@@ -1101,8 +1115,7 @@ static int create_bt_dailinks(struct snd_soc_card *card,
int ret;
ret = asoc_sdw_init_simple_dai_link(dev, *dai_links, be_id, name,
- 1, 1, cpu_dai_name, platform_component->name,
- ARRAY_SIZE(platform_component),
+ 1, 1, cpu_dai_name, "dummy",
snd_soc_dummy_dlc.name, snd_soc_dummy_dlc.dai_name,
1, NULL, NULL);
if (ret)
@@ -1285,6 +1298,19 @@ static int sof_sdw_card_late_probe(struct snd_soc_card *card)
return ret;
}
+static int sof_sdw_add_dai_link(struct snd_soc_card *card,
+ struct snd_soc_dai_link *link)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct intel_mc_ctx *intel_ctx = (struct intel_mc_ctx *)ctx->private;
+
+ /* Ignore the HDMI PCM link if iDisp is not present */
+ if (strstr(link->stream_name, "HDMI") && !intel_ctx->hdmi.idisp_codec)
+ link->ignore = true;
+
+ return 0;
+}
+
static int mc_probe(struct platform_device *pdev)
{
struct snd_soc_acpi_mach *mach = dev_get_platdata(&pdev->dev);
@@ -1311,6 +1337,7 @@ static int mc_probe(struct platform_device *pdev)
card->name = "soundwire";
card->owner = THIS_MODULE;
card->late_probe = sof_sdw_card_late_probe;
+ card->add_dai_link = sof_sdw_add_dai_link;
snd_soc_card_set_drvdata(card, ctx);
diff --git a/sound/soc/intel/catpt/pcm.c b/sound/soc/intel/catpt/pcm.c
index 81a2f0339e05..46acb7fdc547 100644
--- a/sound/soc/intel/catpt/pcm.c
+++ b/sound/soc/intel/catpt/pcm.c
@@ -673,7 +673,6 @@ static int catpt_dai_pcm_new(struct snd_soc_pcm_runtime *rtm,
ret = catpt_ipc_set_device_format(cdev, &devfmt);
- pm_runtime_mark_last_busy(cdev->dev);
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
@@ -871,7 +870,6 @@ static int catpt_mixer_volume_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
}
- pm_runtime_mark_last_busy(cdev->dev);
pm_runtime_put_autosuspend(cdev->dev);
return 0;
@@ -892,7 +890,6 @@ static int catpt_mixer_volume_put(struct snd_kcontrol *kcontrol,
ret = catpt_set_dspvol(cdev, cdev->mixer.mixer_hw_id,
ucontrol->value.integer.value);
- pm_runtime_mark_last_busy(cdev->dev);
pm_runtime_put_autosuspend(cdev->dev);
return ret;
@@ -927,7 +924,6 @@ static int catpt_stream_volume_get(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[i] = dspvol_to_ctlvol(dspvol);
}
- pm_runtime_mark_last_busy(cdev->dev);
pm_runtime_put_autosuspend(cdev->dev);
return 0;
@@ -958,7 +954,6 @@ static int catpt_stream_volume_put(struct snd_kcontrol *kcontrol,
ret = catpt_set_dspvol(cdev, stream->info.stream_hw_id,
ucontrol->value.integer.value);
- pm_runtime_mark_last_busy(cdev->dev);
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
@@ -1035,7 +1030,6 @@ static int catpt_loopback_switch_put(struct snd_kcontrol *kcontrol,
ret = catpt_ipc_mute_loopback(cdev, stream->info.stream_hw_id, mute);
- pm_runtime_mark_last_busy(cdev->dev);
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
diff --git a/sound/soc/intel/catpt/sysfs.c b/sound/soc/intel/catpt/sysfs.c
index 936ac9d503ff..048253002ec8 100644
--- a/sound/soc/intel/catpt/sysfs.c
+++ b/sound/soc/intel/catpt/sysfs.c
@@ -21,7 +21,6 @@ static ssize_t fw_version_show(struct device *dev,
ret = catpt_ipc_get_fw_version(cdev, &version);
- pm_runtime_mark_last_busy(cdev->dev);
pm_runtime_put_autosuspend(cdev->dev);
if (ret)
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index 0afd114be9e5..7822bcae6c69 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -12,7 +12,7 @@ snd-soc-acpi-intel-match-y := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-matc
soc-acpi-intel-lnl-match.o \
soc-acpi-intel-ptl-match.o \
soc-acpi-intel-hda-match.o \
- soc-acpi-intel-sdw-mockup-match.o
+ soc-acpi-intel-sdw-mockup-match.o sof-function-topology-lib.o
snd-soc-acpi-intel-match-y += soc-acpi-intel-ssp-common.o
diff --git a/sound/soc/intel/common/soc-acpi-intel-arl-match.c b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
index 32147dc9d2d6..6bf7a6250ddc 100644
--- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
@@ -8,6 +8,7 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
#include <sound/soc-acpi-intel-ssp-common.h>
+#include "sof-function-topology-lib.h"
static const struct snd_soc_acpi_endpoint single_endpoint = {
.num = 0,
@@ -237,6 +238,15 @@ static const struct snd_soc_acpi_adr_device rt722_0_single_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt1316_3_single_adr[] = {
+ {
+ .adr = 0x000330025D131601ull,
+ .num_endpoints = 1,
+ .endpoints = &single_endpoint,
+ .name_prefix = "rt1316-1"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt1320_2_single_adr[] = {
{
.adr = 0x000230025D132001ull,
@@ -367,6 +377,20 @@ static const struct snd_soc_acpi_link_adr arl_sdca_rvp[] = {
{}
};
+static const struct snd_soc_acpi_link_adr arl_rt711_l0_rt1316_l3[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt711_sdca_0_adr),
+ .adr_d = rt711_sdca_0_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1316_3_single_adr),
+ .adr_d = rt1316_3_single_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr arl_rt722_l0_rt1320_l2[] = {
{
.mask = BIT(0),
@@ -436,42 +460,55 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
.links = arl_cs42l43_l0_cs35l56_l23,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0) | BIT(2) | BIT(3),
.links = arl_cs42l43_l0_cs35l56_2_l23,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0) | BIT(2) | BIT(3),
.links = arl_cs42l43_l0_cs35l56_3_l23,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0) | BIT(2),
.links = arl_cs42l43_l0_cs35l56_l2,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l0-cs35l56-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
.links = arl_cs42l43_l0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(2) | BIT(3),
+ .links = arl_cs42l43_l2_cs35l56_l3,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(2),
.links = arl_cs42l43_l2,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-cs42l43-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
- .link_mask = BIT(2) | BIT(3),
- .links = arl_cs42l43_l2_cs35l56_l3,
+ .link_mask = BIT(0) | BIT(3),
+ .links = arl_rt711_l0_rt1316_l3,
.drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-arl-cs42l43-l2-cs35l56-l3.tplg",
+ .sof_tplg_filename = "sof-arl-rt711-l0-rt1316-l3.tplg",
},
{
.link_mask = 0x1, /* link0 required */
@@ -490,6 +527,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
.links = arl_rt722_l0_rt1320_l2,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-arl-rt722-l0_rt1320-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{},
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
index e04f6de746eb..937a74a5d523 100644
--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
@@ -8,6 +8,7 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
+#include "sof-function-topology-lib.h"
#include "soc-acpi-intel-sdca-quirks.h"
#include "soc-acpi-intel-sdw-mockup-match.h"
@@ -136,7 +137,7 @@ static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
},
};
-static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints_endpoints[] = {
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
@@ -349,8 +350,8 @@ static const struct snd_soc_acpi_adr_device rt1712_3_single_adr[] = {
static const struct snd_soc_acpi_adr_device rt712_vb_2_group1_adr[] = {
{
.adr = 0x000230025D071201ull,
- .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints_endpoints),
- .endpoints = jack_amp_g1_dmic_endpoints_endpoints,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
.name_prefix = "rt712"
}
};
@@ -418,6 +419,15 @@ static const struct snd_soc_acpi_adr_device rt1320_1_group1_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt1320_2_group2_adr[] = {
+ {
+ .adr = 0x000231025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-2"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt1320_1_group2_adr[] = {
{
.adr = 0x000130025D132001ull,
@@ -608,6 +618,25 @@ static const struct snd_soc_acpi_link_adr lnl_sdw_rt1318_l12_rt714_l0[] = {
{}
};
+static const struct snd_soc_acpi_link_adr lnl_sdw_rt1320_l12_rt714_l0[] = {
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(rt1320_1_group2_adr),
+ .adr_d = rt1320_1_group2_adr,
+ },
+ {
+ .mask = BIT(2),
+ .num_adr = ARRAY_SIZE(rt1320_2_group2_adr),
+ .adr_d = rt1320_2_group2_adr,
+ },
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt714_0_adr),
+ .adr_d = rt714_0_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr lnl_sdw_rt713_l0_rt1318_l1[] = {
{
.mask = BIT(0),
@@ -712,6 +741,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.links = lnl_cs42l43_l0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-lnl-cs42l43-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
@@ -730,6 +760,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.links = lnl_rt722_only,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-lnl-rt722-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = GENMASK(2, 0),
@@ -738,6 +769,12 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.sof_tplg_filename = "sof-lnl-rt1318-l12-rt714-l0.tplg"
},
{
+ .link_mask = GENMASK(2, 0),
+ .links = lnl_sdw_rt1320_l12_rt714_l0,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-rt1320-l12-rt714-l0.tplg"
+ },
+ {
.link_mask = BIT(0) | BIT(1),
.links = lnl_sdw_rt713_l0_rt1318_l1,
.drv_name = "sof_sdw",
@@ -748,14 +785,16 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.links = lnl_sdw_rt712_vb_l2_rt1320_l1,
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
- .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg"
+ .sof_tplg_filename = "sof-lnl-rt712-l2-rt1320-l1.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(1) | BIT(2) | BIT(3),
.links = lnl_sdw_rt713_vb_l2_rt1320_l13,
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
- .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg"
+ .sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{},
};
diff --git a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
index 9e611e3667ad..75dc8935a794 100644
--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -11,6 +11,7 @@
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
#include <sound/soc-acpi-intel-ssp-common.h>
+#include "sof-function-topology-lib.h"
#include "soc-acpi-intel-sdca-quirks.h"
#include "soc-acpi-intel-sdw-mockup-match.h"
@@ -729,6 +730,24 @@ static const struct snd_soc_acpi_adr_device cs35l56_3_l_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device cs35l63_1_fb_adr[] = {
+ {
+ .adr = 0x00013001FA356301ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_l_fb_endpoints),
+ .endpoints = cs35l56_l_fb_endpoints,
+ .name_prefix = "AMP1"
+ },
+};
+
+static const struct snd_soc_acpi_adr_device cs35l63_3_fb_adr[] = {
+ {
+ .adr = 0x00033101FA356301ull,
+ .num_endpoints = ARRAY_SIZE(cs35l56_r_fb_endpoints),
+ .endpoints = cs35l56_r_fb_endpoints,
+ .name_prefix = "AMP2"
+ },
+};
+
static const struct snd_soc_acpi_link_adr rt5682_link2_max98373_link0[] = {
/* Expected order: jack -> amp */
{
@@ -1026,6 +1045,20 @@ static const struct snd_soc_acpi_link_adr mtl_cs35l56_x8_link0_link1_fb[] = {
{}
};
+static const struct snd_soc_acpi_link_adr mtl_cs35l63_x2_link1_link3_fb[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(cs35l63_3_fb_adr),
+ .adr_d = cs35l63_3_fb_adr,
+ },
+ {
+ .mask = BIT(1),
+ .num_adr = ARRAY_SIZE(cs35l63_1_fb_adr),
+ .adr_d = cs35l63_1_fb_adr,
+ },
+ {}
+};
+
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
/* mockup tests need to be first */
@@ -1083,12 +1116,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
.sof_tplg_filename = "sof-mtl-rt712-vb-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
.links = mtl_712_l0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-rt712-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = GENMASK(2, 0),
@@ -1101,30 +1136,41 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.links = cs42l43_link0_cs35l56_link2_link3,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l23.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0) | BIT(1) | BIT(3),
.links = cs42l43_link3_cs35l56_x4_link0_link1_spkagg,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l3-cs35l56-l01-spkagg.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = GENMASK(2, 0),
.links = mtl_cs42l43_cs35l56,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l0-cs35l56-l12.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0) | BIT(1),
.links = mtl_cs35l56_x8_link0_link1_fb,
.drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg"
+ .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
.links = mtl_cs42l43_l0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-cs42l43-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1) | BIT(3),
+ .links = mtl_cs35l63_x2_link1_link3_fb,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-cs35l56-l01-fb8.tplg",
},
{
.link_mask = GENMASK(3, 0),
@@ -1143,6 +1189,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.links = mtl_rt722_only,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-mtl-rt722-l0.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
diff --git a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
index c599eb43eeb1..e292701dfcfe 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
@@ -4,10 +4,14 @@
*
* Copyright (c) 2024, Intel Corporation.
*
+ * Order of entries in snd_soc_acpi_intel_ptl_sdw_machines[] matters.
+ * Check subset of link mask when matching the machine driver, rule is
+ * superset match should be ordered before subset matches.
*/
#include <sound/soc-acpi.h>
#include <sound/soc-acpi-intel-match.h>
+#include "sof-function-topology-lib.h"
#include "soc-acpi-intel-sdca-quirks.h"
#include "soc-acpi-intel-sdw-mockup-match.h"
#include <sound/soc-acpi-intel-ssp-common.h>
@@ -17,14 +21,46 @@ static const struct snd_soc_acpi_codecs ptl_rt5682_rt5682s_hp = {
.codecs = {RT5682_ACPI_HID, RT5682S_ACPI_HID},
};
+static const struct snd_soc_acpi_codecs ptl_essx_83x6 = {
+ .num_codecs = 3,
+ .codecs = { "ESSX8316", "ESSX8326", "ESSX8336"},
+};
+
+static const struct snd_soc_acpi_codecs ptl_lt6911_hdmi = {
+ .num_codecs = 1,
+ .codecs = {"INTC10B0"}
+};
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = {
{
.comp_ids = &ptl_rt5682_rt5682s_hp,
+ .drv_name = "ptl_rt5682_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &ptl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-ptl-rt5682-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &ptl_rt5682_rt5682s_hp,
.drv_name = "ptl_rt5682_def",
.sof_tplg_filename = "sof-ptl", /* the tplg suffix is added at run time */
.tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_AMP_NAME |
SND_SOC_ACPI_TPLG_INTEL_CODEC_NAME,
},
+ {
+ .comp_ids = &ptl_essx_83x6,
+ .drv_name = "ptl_es83x6_c1_h02",
+ .machine_quirk = snd_soc_acpi_codec_list,
+ .quirk_data = &ptl_lt6911_hdmi,
+ .sof_tplg_filename = "sof-ptl-es83x6-ssp1-hdmi-ssp02.tplg",
+ },
+ {
+ .comp_ids = &ptl_essx_83x6,
+ .drv_name = "sof-essx8336",
+ .sof_tplg_filename = "sof-ptl-es8336", /* the tplg suffix is added at run time */
+ .tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
+ SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
+ SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_machines);
@@ -134,7 +170,7 @@ static const struct snd_soc_acpi_endpoint jack_dmic_endpoints[] = {
},
};
-static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints_endpoints[] = {
+static const struct snd_soc_acpi_endpoint jack_amp_g1_dmic_endpoints[] = {
/* Jack Endpoint */
{
.num = 0,
@@ -284,8 +320,8 @@ static const struct snd_soc_acpi_adr_device rt711_sdca_0_adr[] = {
static const struct snd_soc_acpi_adr_device rt712_vb_2_group1_adr[] = {
{
.adr = 0x000230025D071201ull,
- .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints_endpoints),
- .endpoints = jack_amp_g1_dmic_endpoints_endpoints,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
.name_prefix = "rt712"
}
};
@@ -293,8 +329,8 @@ static const struct snd_soc_acpi_adr_device rt712_vb_2_group1_adr[] = {
static const struct snd_soc_acpi_adr_device rt712_vb_3_group1_adr[] = {
{
.adr = 0x000330025D071201ull,
- .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints_endpoints),
- .endpoints = jack_amp_g1_dmic_endpoints_endpoints,
+ .num_endpoints = ARRAY_SIZE(jack_amp_g1_dmic_endpoints),
+ .endpoints = jack_amp_g1_dmic_endpoints,
.name_prefix = "rt712"
}
};
@@ -317,6 +353,24 @@ static const struct snd_soc_acpi_adr_device rt713_vb_3_adr[] = {
}
};
+static const struct snd_soc_acpi_adr_device rt1320_3_group1_adr[] = {
+ {
+ .adr = 0x000330025D132001ull,
+ .num_endpoints = 1,
+ .endpoints = &spk_r_endpoint,
+ .name_prefix = "rt1320-1"
+ }
+};
+
+static const struct snd_soc_acpi_adr_device rt721_0_single_adr[] = {
+ {
+ .adr = 0x000030025d072101ull,
+ .num_endpoints = ARRAY_SIZE(rt_mf_endpoints),
+ .endpoints = rt_mf_endpoints,
+ .name_prefix = "rt721"
+ }
+};
+
static const struct snd_soc_acpi_adr_device rt721_3_single_adr[] = {
{
.adr = 0x000330025d072101ull,
@@ -435,6 +489,15 @@ static const struct snd_soc_acpi_link_adr ptl_cs42l43_l3[] = {
{}
};
+static const struct snd_soc_acpi_link_adr ptl_rt721_l0[] = {
+ {
+ .mask = BIT(0),
+ .num_adr = ARRAY_SIZE(rt721_0_single_adr),
+ .adr_d = rt721_0_single_adr,
+ },
+ {}
+};
+
static const struct snd_soc_acpi_link_adr ptl_rt722_only[] = {
{
.mask = BIT(0),
@@ -537,9 +600,23 @@ static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l3_rt1320_l2[] = {
{}
};
+static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l3_rt1320_l3[] = {
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt712_vb_3_group1_adr),
+ .adr_d = rt712_vb_3_group1_adr,
+ },
+ {
+ .mask = BIT(3),
+ .num_adr = ARRAY_SIZE(rt1320_3_group1_adr),
+ .adr_d = rt1320_3_group1_adr,
+ },
+ {}
+};
+
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
- /* mockup tests need to be first */
+/* Order Priority: mockup > most links > most bit link-mask > alphabetical */
{
.link_mask = GENMASK(3, 0),
.links = sdw_mockup_headset_2amps_mic,
@@ -559,80 +636,104 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
.sof_tplg_filename = "sof-ptl-rt715-rt711-rt1308-mono.tplg",
},
{
+ .link_mask = BIT(0),
+ .links = sdw_mockup_multi_func,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-rt722.tplg", /* Reuse the existing tplg file */
+ },
+ {
+ .link_mask = BIT(1) | BIT(2) | BIT(3),
+ .links = ptl_sdw_rt713_vb_l2_rt1320_l13,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(1) | BIT(2) | BIT(3),
+ .links = ptl_sdw_rt713_vb_l3_rt1320_l12,
+ .drv_name = "sof_sdw",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
.link_mask = BIT(1) | BIT(2) | BIT(3),
.links = ptl_cs42l43_l2_cs35l56x6_l13,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-cs42l43-l2-cs35l56x6-l13.tplg",
},
{
- .link_mask = BIT(0),
- .links = sdw_mockup_multi_func,
+ .link_mask = BIT(1) | BIT(2),
+ .links = ptl_sdw_rt712_vb_l2_rt1320_l1,
.drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-rt722.tplg", /* Reuse the existing tplg file */
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
- .link_mask = BIT(0),
- .links = ptl_rvp,
+ .link_mask = BIT(2) | BIT(3),
+ .links = ptl_sdw_rt712_vb_l3_rt1320_l2,
.drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-rt711.tplg",
+ .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
+ .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
- .link_mask = BIT(3),
- .links = ptl_cs42l43_l3,
+ .link_mask = BIT(0),
+ .links = ptl_rvp,
.drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-cs42l43-l3.tplg",
+ .sof_tplg_filename = "sof-ptl-rt711.tplg",
},
{
- .link_mask = BIT(3),
- .links = ptl_rt721_l3,
+ .link_mask = BIT(0),
+ .links = ptl_rt721_l0,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt721.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(0),
.links = ptl_rt722_only,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt722.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(1),
.links = ptl_rt722_l1,
.drv_name = "sof_sdw",
.sof_tplg_filename = "sof-ptl-rt722.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
.link_mask = BIT(3),
- .links = ptl_rt722_l3,
- .drv_name = "sof_sdw",
- .sof_tplg_filename = "sof-ptl-rt722.tplg",
- },
- {
- .link_mask = BIT(1) | BIT(2),
- .links = ptl_sdw_rt712_vb_l2_rt1320_l1,
+ .links = ptl_cs42l43_l3,
.drv_name = "sof_sdw",
- .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
- .sof_tplg_filename = "sof-ptl-rt712-l2-rt1320-l1.tplg"
+ .sof_tplg_filename = "sof-ptl-cs42l43-l3.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
- .link_mask = BIT(2) | BIT(3),
- .links = ptl_sdw_rt712_vb_l3_rt1320_l2,
+ .link_mask = BIT(3),
+ .links = ptl_sdw_rt712_vb_l3_rt1320_l3,
.drv_name = "sof_sdw",
.machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
- .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l2.tplg"
+ .sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
- .link_mask = BIT(1) | BIT(2) | BIT(3),
- .links = ptl_sdw_rt713_vb_l2_rt1320_l13,
+ .link_mask = BIT(3),
+ .links = ptl_rt721_l3,
.drv_name = "sof_sdw",
- .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
- .sof_tplg_filename = "sof-ptl-rt713-l2-rt1320-l13.tplg"
+ .sof_tplg_filename = "sof-ptl-rt721.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{
- .link_mask = BIT(1) | BIT(2) | BIT(3),
- .links = ptl_sdw_rt713_vb_l3_rt1320_l12,
+ .link_mask = BIT(3),
+ .links = ptl_rt722_l3,
.drv_name = "sof_sdw",
- .machine_check = snd_soc_acpi_intel_sdca_is_device_rt712_vb,
- .sof_tplg_filename = "sof-ptl-rt713-l3-rt1320-l12.tplg"
+ .sof_tplg_filename = "sof-ptl-rt722.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
},
{},
};
diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c
new file mode 100644
index 000000000000..3cc81dcf047e
--- /dev/null
+++ b/sound/soc/intel/common/sof-function-topology-lib.c
@@ -0,0 +1,136 @@
+// 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) 2025 Intel Corporation.
+//
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/firmware.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include "sof-function-topology-lib.h"
+
+enum tplg_device_id {
+ TPLG_DEVICE_SDCA_JACK,
+ TPLG_DEVICE_SDCA_AMP,
+ TPLG_DEVICE_SDCA_MIC,
+ TPLG_DEVICE_INTEL_PCH_DMIC,
+ TPLG_DEVICE_HDMI,
+ TPLG_DEVICE_MAX
+};
+
+#define SDCA_DEVICE_MASK (BIT(TPLG_DEVICE_SDCA_JACK) | BIT(TPLG_DEVICE_SDCA_AMP) | \
+ BIT(TPLG_DEVICE_SDCA_MIC))
+
+#define SOF_INTEL_PLATFORM_NAME_MAX 4
+
+int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
+ const char *prefix, const char ***tplg_files)
+{
+ struct snd_soc_acpi_mach_params mach_params = mach->mach_params;
+ struct snd_soc_dai_link *dai_link;
+ const struct firmware *fw;
+ char platform[SOF_INTEL_PLATFORM_NAME_MAX];
+ unsigned long tplg_mask = 0;
+ int tplg_num = 0;
+ int tplg_dev;
+ int ret;
+ int i;
+
+ ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform);
+ if (ret != 1) {
+ dev_err(card->dev, "Invalid platform name %s of tplg %s\n",
+ platform, mach->sof_tplg_filename);
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ char *tplg_dev_name;
+
+ dev_dbg(card->dev, "dai_link %s id %d\n", dai_link->name, dai_link->id);
+ if (strstr(dai_link->name, "SimpleJack")) {
+ tplg_dev = TPLG_DEVICE_SDCA_JACK;
+ tplg_dev_name = "sdca-jack";
+ } else if (strstr(dai_link->name, "SmartAmp")) {
+ tplg_dev = TPLG_DEVICE_SDCA_AMP;
+ tplg_dev_name = devm_kasprintf(card->dev, GFP_KERNEL,
+ "sdca-%damp", dai_link->num_cpus);
+ if (!tplg_dev_name)
+ return -ENOMEM;
+ } else if (strstr(dai_link->name, "SmartMic")) {
+ tplg_dev = TPLG_DEVICE_SDCA_MIC;
+ tplg_dev_name = "sdca-mic";
+ } else if (strstr(dai_link->name, "dmic")) {
+ switch (mach_params.dmic_num) {
+ case 2:
+ tplg_dev_name = "dmic-2ch";
+ break;
+ case 4:
+ tplg_dev_name = "dmic-4ch";
+ break;
+ default:
+ dev_warn(card->dev,
+ "unsupported number of dmics: %d\n",
+ mach_params.dmic_num);
+ continue;
+ }
+ tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC;
+ } else if (strstr(dai_link->name, "iDisp")) {
+ tplg_dev = TPLG_DEVICE_HDMI;
+ tplg_dev_name = "hdmi-pcm5";
+
+ } else {
+ /* The dai link is not supported by separated tplg yet */
+ dev_dbg(card->dev,
+ "dai_link %s is not supported by separated tplg yet\n",
+ dai_link->name);
+ return 0;
+ }
+ if (tplg_mask & BIT(tplg_dev))
+ continue;
+
+ tplg_mask |= BIT(tplg_dev);
+
+ /*
+ * The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg
+ * where <platform> is only required for the DMIC function as the nhlt blob
+ * is platform dependent.
+ */
+ switch (tplg_dev) {
+ case TPLG_DEVICE_INTEL_PCH_DMIC:
+ (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s/sof-%s-%s-id%d.tplg",
+ prefix, platform,
+ tplg_dev_name, dai_link->id);
+ break;
+ default:
+ (*tplg_files)[tplg_num] = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s/sof-%s-id%d.tplg",
+ prefix, tplg_dev_name,
+ dai_link->id);
+ break;
+ }
+ if (!(*tplg_files)[tplg_num])
+ return -ENOMEM;
+ tplg_num++;
+ }
+
+ dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num);
+
+ /* Check presence of sub-topologies */
+ for (i = 0; i < tplg_num; i++) {
+ ret = firmware_request_nowarn(&fw, (*tplg_files)[i], card->dev);
+ if (!ret) {
+ release_firmware(fw);
+ } else {
+ dev_dbg(card->dev, "Failed to open topology file: %s\n", (*tplg_files)[i]);
+ return 0;
+ }
+ }
+
+ return tplg_num;
+}
+
diff --git a/sound/soc/intel/common/sof-function-topology-lib.h b/sound/soc/intel/common/sof-function-topology-lib.h
new file mode 100644
index 000000000000..e7d0c39d0788
--- /dev/null
+++ b/sound/soc/intel/common/sof-function-topology-lib.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * soc-acpi-intel-get-tplg.h - get-tplg-files ops
+ *
+ * Copyright (c) 2025, Intel Corporation.
+ *
+ */
+
+#ifndef _SND_SOC_ACPI_INTEL_GET_TPLG_H
+#define _SND_SOC_ACPI_INTEL_GET_TPLG_H
+
+int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
+ const char *prefix, const char ***tplg_files);
+
+#endif
diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig
index dd3b4507fbe6..f3ff3fb49239 100644
--- a/sound/soc/jz4740/Kconfig
+++ b/sound/soc/jz4740/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "JZ4740"
+
config SND_JZ4740_SOC_I2S
tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
depends on MIPS || COMPILE_TEST
@@ -8,3 +10,5 @@ config SND_JZ4740_SOC_I2S
help
Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
based boards.
+
+endmenu
diff --git a/sound/soc/kirkwood/Kconfig b/sound/soc/kirkwood/Kconfig
index 5d8a86b26fa2..924072e402c8 100644
--- a/sound/soc/kirkwood/Kconfig
+++ b/sound/soc/kirkwood/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Kirkwood"
+
config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
@@ -16,3 +18,4 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB
Say Y if you want to add support for SoC audio on
the Armada 370 Development Board.
+endmenu
diff --git a/sound/soc/loongson/Kconfig b/sound/soc/loongson/Kconfig
index 2d8291c1443c..2e06670e4d7e 100644
--- a/sound/soc/loongson/Kconfig
+++ b/sound/soc/loongson/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-menu "SoC Audio for Loongson CPUs"
+menu "Loongson"
config SND_SOC_LOONGSON_CARD
tristate "Loongson Sound Card Driver"
@@ -37,3 +37,13 @@ config SND_SOC_LOONGSON_I2S_PLATFORM
The controller work as a platform device, we can found it in
Loongson-2K1000 SoCs.
endmenu
+
+config SND_LOONGSON1_AC97
+ tristate "Loongson1 AC97 Support"
+ depends on LOONGSON1_APB_DMA
+ select SND_SOC_AC97_CODEC
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y or M if you want to add support for codecs attached to
+ the Loongson1 AC97 controller.
diff --git a/sound/soc/loongson/Makefile b/sound/soc/loongson/Makefile
index c0cb1acb36e3..4c6d3130bcee 100644
--- a/sound/soc/loongson/Makefile
+++ b/sound/soc/loongson/Makefile
@@ -8,6 +8,8 @@ obj-$(CONFIG_SND_SOC_LOONGSON_I2S_PLATFORM) += snd-soc-loongson-i2s-plat.o snd-s
snd-soc-loongson-i2s-y := loongson_i2s.o
+obj-$(CONFIG_SND_LOONGSON1_AC97) += loongson1_ac97.o
+
#Machine Support
snd-soc-loongson-card-y := loongson_card.o
obj-$(CONFIG_SND_SOC_LOONGSON_CARD) += snd-soc-loongson-card.o
diff --git a/sound/soc/loongson/loongson1_ac97.c b/sound/soc/loongson/loongson1_ac97.c
new file mode 100644
index 000000000000..84901900ad43
--- /dev/null
+++ b/sound/soc/loongson/loongson1_ac97.c
@@ -0,0 +1,398 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * AC97 Controller Driver for Loongson-1 SoC
+ *
+ * Copyright (C) 2025 Keguang Zhang <keguang.zhang@gmail.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/dma-mapping.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+/* Loongson-1 AC97 Controller Registers */
+#define AC97_CSR 0x0
+#define AC97_OCC0 0x4
+#define AC97_ICC 0x10
+#define AC97_CRAC 0x18
+#define AC97_INTRAW 0x54
+#define AC97_INTM 0x58
+#define AC97_INT_CW_CLR 0x68
+#define AC97_INT_CR_CLR 0x6c
+
+/* Control Status Register Bits (CSR) */
+#define CSR_RESUME BIT(1)
+#define CSR_RST_FORCE BIT(0)
+
+/* MIC Channel Configuration Bits */
+#define M_DMA_EN BIT(22)
+#define M_FIFO_THRES GENMASK(21, 20)
+#define M_FIFO_THRES_FULL FIELD_PREP(M_FIFO_THRES, 3)
+#define M_FIFO_THRES_HALF FIELD_PREP(M_FIFO_THRES, 1)
+#define M_FIFO_THRES_QUARTER FIELD_PREP(M_FIFO_THRES, 0)
+#define M_SW GENMASK(19, 18)
+#define M_SW_16_BITS FIELD_PREP(M_SW, 2)
+#define M_SW_8_BITS FIELD_PREP(M_SW, 0)
+#define M_VSR BIT(17)
+#define M_CH_EN BIT(16)
+/* Right Channel Configuration Bits */
+#define R_DMA_EN BIT(14)
+#define R_FIFO_THRES GENMASK(13, 12)
+#define R_FIFO_THRES_EMPTY FIELD_PREP(R_FIFO_THRES, 3)
+#define R_FIFO_THRES_HALF FIELD_PREP(R_FIFO_THRES, 1)
+#define R_FIFO_THRES_QUARTER FIELD_PREP(R_FIFO_THRES, 0)
+#define R_SW GENMASK(11, 10)
+#define R_SW_16_BITS FIELD_PREP(R_SW, 2)
+#define R_SW_8_BITS FIELD_PREP(R_SW, 0)
+#define R_VSR BIT(9)
+#define R_CH_EN BIT(8)
+/* Left Channel Configuration Bits */
+#define L_DMA_EN BIT(6)
+#define L_FIFO_THRES GENMASK(5, 4)
+#define L_FIFO_THRES_EMPTY FIELD_PREP(L_FIFO_THRES, 3)
+#define L_FIFO_THRES_HALF FIELD_PREP(L_FIFO_THRES, 1)
+#define L_FIFO_THRES_QUARTER FIELD_PREP(L_FIFO_THRES, 0)
+#define L_SW GENMASK(3, 2)
+#define L_SW_16_BITS FIELD_PREP(L_SW, 2)
+#define L_SW_8_BITS FIELD_PREP(L_SW, 0)
+#define L_VSR BIT(1)
+#define L_CH_EN BIT(0)
+
+/* Codec Register Access Command Bits (CRAC) */
+#define CODEC_WR BIT(31)
+#define CODEC_ADR GENMASK(22, 16)
+#define CODEC_DAT GENMASK(15, 0)
+
+/* Interrupt Register (INTRAW) */
+#define CW_DONE BIT(1)
+#define CR_DONE BIT(0)
+
+#define LS1X_AC97_DMA_TX_EN BIT(31)
+#define LS1X_AC97_DMA_STEREO BIT(30)
+#define LS1X_AC97_DMA_TX_BYTES GENMASK(29, 28)
+#define LS1X_AC97_DMA_TX_4_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 2)
+#define LS1X_AC97_DMA_TX_2_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 1)
+#define LS1X_AC97_DMA_TX_1_BYTE FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 0)
+#define LS1X_AC97_DMA_DADDR_MASK GENMASK(27, 0)
+
+#define LS1X_AC97_DMA_FIFO_SIZE 128
+
+#define LS1X_AC97_TIMEOUT 3000
+
+struct ls1x_ac97 {
+ void __iomem *reg_base;
+ struct regmap *regmap;
+ dma_addr_t tx_dma_base;
+ dma_addr_t rx_dma_base;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+};
+
+static struct ls1x_ac97 *ls1x_ac97;
+
+static const struct regmap_config ls1x_ac97_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+};
+
+static void ls1x_ac97_reset(struct snd_ac97 *ac97)
+{
+ int val;
+
+ regmap_write(ls1x_ac97->regmap, AC97_CSR, CSR_RST_FORCE);
+ regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
+ !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
+}
+
+static void ls1x_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
+{
+ int tmp, ret;
+
+ tmp = FIELD_PREP(CODEC_ADR, reg) | FIELD_PREP(CODEC_DAT, val);
+ regmap_write(ls1x_ac97->regmap, AC97_CRAC, tmp);
+ ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, tmp,
+ (tmp & CW_DONE), 0, LS1X_AC97_TIMEOUT);
+ if (ret)
+ pr_err("timeout on AC97 write! %d\n", ret);
+
+ regmap_read(ls1x_ac97->regmap, AC97_INT_CW_CLR, &ret);
+}
+
+static unsigned short ls1x_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
+{
+ int val, ret;
+
+ val = CODEC_WR | FIELD_PREP(CODEC_ADR, reg);
+ regmap_write(ls1x_ac97->regmap, AC97_CRAC, val);
+ ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, val,
+ (val & CR_DONE), 0, LS1X_AC97_TIMEOUT);
+ if (ret) {
+ pr_err("timeout on AC97 read! %d\n", ret);
+ return ret;
+ }
+
+ regmap_read(ls1x_ac97->regmap, AC97_INT_CR_CLR, &ret);
+ regmap_read(ls1x_ac97->regmap, AC97_CRAC, &ret);
+
+ return (ret & CODEC_DAT);
+}
+
+static void ls1x_ac97_init(struct snd_ac97 *ac97)
+{
+ writel(0, ls1x_ac97->reg_base + AC97_INTRAW);
+ writel(0, ls1x_ac97->reg_base + AC97_INTM);
+
+ /* Config output channels */
+ regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0,
+ R_DMA_EN | R_FIFO_THRES | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES | L_CH_EN,
+ R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
+
+ /* Config inputs channel */
+ regmap_update_bits(ls1x_ac97->regmap, AC97_ICC,
+ M_DMA_EN | M_FIFO_THRES | M_CH_EN |
+ R_DMA_EN | R_FIFO_THRES | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES | L_CH_EN,
+ M_DMA_EN | M_FIFO_THRES_FULL | M_CH_EN |
+ R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN |
+ L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN);
+
+ if (ac97->ext_id & AC97_EI_VRA) {
+ regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0, R_VSR | L_VSR, R_VSR | L_VSR);
+ regmap_update_bits(ls1x_ac97->regmap, AC97_ICC, M_VSR, M_VSR);
+ }
+}
+
+static struct snd_ac97_bus_ops ls1x_ac97_ops = {
+ .reset = ls1x_ac97_reset,
+ .write = ls1x_ac97_write,
+ .read = ls1x_ac97_read,
+ .init = ls1x_ac97_init,
+};
+
+static int ls1x_ac97_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
+ struct snd_dmaengine_dai_dma_data *dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ switch (params_channels(params)) {
+ case 1:
+ dma_data->addr &= ~LS1X_AC97_DMA_STEREO;
+ break;
+ case 2:
+ dma_data->addr |= LS1X_AC97_DMA_STEREO;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "unsupported channels! %d\n", params_channels(params));
+ return -EINVAL;
+ }
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_U8:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(ac97->regmap, AC97_OCC0,
+ R_SW | L_SW,
+ R_SW_8_BITS | L_SW_8_BITS);
+ else
+ regmap_update_bits(ac97->regmap, AC97_ICC,
+ M_SW | R_SW | L_SW,
+ M_SW_8_BITS | R_SW_8_BITS | L_SW_8_BITS);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_BE:
+ case SNDRV_PCM_FORMAT_U16_BE:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_update_bits(ac97->regmap, AC97_OCC0,
+ R_SW | L_SW,
+ R_SW_16_BITS | L_SW_16_BITS);
+ else
+ regmap_update_bits(ac97->regmap, AC97_ICC,
+ M_SW | R_SW | L_SW,
+ M_SW_16_BITS | R_SW_16_BITS | L_SW_16_BITS);
+ break;
+ default:
+ dev_err(cpu_dai->dev, "unsupported format! %d\n", params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ls1x_ac97_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev);
+
+ ac97->capture_dma_data.addr = ac97->rx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
+ ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ ac97->capture_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
+
+ ac97->playback_dma_data.addr = ac97->tx_dma_base & LS1X_AC97_DMA_DADDR_MASK;
+ ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_4_BYTES;
+ ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_EN;
+ ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ ac97->playback_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE;
+
+ snd_soc_dai_init_dma_data(cpu_dai, &ac97->playback_dma_data, &ac97->capture_dma_data);
+ snd_soc_dai_set_drvdata(cpu_dai, ac97);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops ls1x_ac97_dai_ops = {
+ .probe = ls1x_ac97_dai_probe,
+ .hw_params = ls1x_ac97_hw_params,
+};
+
+#define LS1X_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |\
+ SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
+ SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE)
+
+static struct snd_soc_dai_driver ls1x_ac97_dai[] = {
+ {
+ .name = "ls1x-ac97",
+ .playback = {
+ .stream_name = "AC97 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = LS1X_AC97_FMTS,
+ },
+ .capture = {
+ .stream_name = "AC97 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_48000,
+ .formats = LS1X_AC97_FMTS,
+ },
+ .ops = &ls1x_ac97_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver ls1x_ac97_component = {
+ .name = KBUILD_MODNAME,
+ .legacy_dai_naming = 1,
+};
+
+static int ls1x_ac97_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct ls1x_ac97 *ac97;
+ struct resource *res;
+ int ret;
+
+ ac97 = devm_kzalloc(dev, sizeof(struct ls1x_ac97), GFP_KERNEL);
+ if (!ac97)
+ return -ENOMEM;
+ ls1x_ac97 = ac97;
+ platform_set_drvdata(pdev, ac97);
+
+ ac97->reg_base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(ac97->reg_base))
+ return PTR_ERR(ac97->reg_base);
+
+ ac97->regmap = devm_regmap_init_mmio(dev, ac97->reg_base, &ls1x_ac97_regmap_config);
+ if (IS_ERR(ac97->regmap))
+ return dev_err_probe(dev, PTR_ERR(ac97->regmap), "devm_regmap_init_mmio failed\n");
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-tx");
+ if (!res)
+ return dev_err_probe(dev, -EINVAL, "Missing 'audio-tx' in reg-names property\n");
+
+ ac97->tx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
+ DMA_TO_DEVICE, 0);
+ if (dma_mapping_error(dev, ac97->tx_dma_base))
+ return -ENXIO;
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-rx");
+ if (!res)
+ return dev_err_probe(dev, -EINVAL, "Missing 'audio-rx' in reg-names property\n");
+
+ ac97->rx_dma_base = dma_map_resource(dev, res->start, resource_size(res),
+ DMA_FROM_DEVICE, 0);
+ if (dma_mapping_error(dev, ac97->rx_dma_base))
+ return -ENXIO;
+
+ ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
+ if (ret)
+ dev_err_probe(dev, ret, "failed to register PCM\n");
+
+ ret = devm_snd_soc_register_component(dev, &ls1x_ac97_component,
+ ls1x_ac97_dai, ARRAY_SIZE(ls1x_ac97_dai));
+ if (ret)
+ dev_err_probe(dev, ret, "failed to register DAI\n");
+
+ return snd_soc_set_ac97_ops(&ls1x_ac97_ops);
+}
+
+static void ls1x_ac97_remove(struct platform_device *pdev)
+{
+ ls1x_ac97 = NULL;
+ snd_soc_set_ac97_ops(NULL);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ls1x_ac97_suspend(struct device *dev)
+{
+ int val;
+
+ regmap_clear_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_clear_bits(ls1x_ac97->regmap, AC97_ICC,
+ M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
+
+ return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
+ (val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
+}
+
+static int ls1x_ac97_resume(struct device *dev)
+{
+ int val;
+
+ regmap_set_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_set_bits(ls1x_ac97->regmap, AC97_ICC,
+ M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN);
+ regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME);
+
+ return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val,
+ !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT);
+}
+#endif
+
+static const struct dev_pm_ops ls1x_ac97_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(ls1x_ac97_suspend, ls1x_ac97_resume)
+};
+
+static const struct of_device_id ls1x_ac97_match[] = {
+ { .compatible = "loongson,ls1b-ac97" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ls1x_ac97_match);
+
+static struct platform_driver ls1x_ac97_driver = {
+ .probe = ls1x_ac97_probe,
+ .remove = ls1x_ac97_remove,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ls1x_ac97_match,
+ .pm = &ls1x_ac97_pm_ops,
+ },
+};
+
+module_platform_driver(ls1x_ac97_driver);
+
+MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>");
+MODULE_DESCRIPTION("Loongson-1 AC97 Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c
index e8852a30f213..e336656e13eb 100644
--- a/sound/soc/loongson/loongson_i2s.c
+++ b/sound/soc/loongson/loongson_i2s.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
+#include <linux/export.h>
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <sound/soc.h>
diff --git a/sound/soc/loongson/loongson_i2s_pci.c b/sound/soc/loongson/loongson_i2s_pci.c
index d2d0e5d8cac9..1ea5501a97f8 100644
--- a/sound/soc/loongson/loongson_i2s_pci.c
+++ b/sound/soc/loongson/loongson_i2s_pci.c
@@ -16,6 +16,8 @@
#include "loongson_i2s.h"
#include "loongson_dma.h"
+#define DRIVER_NAME "loongson-i2s-pci"
+
static bool loongson_i2s_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -92,13 +94,12 @@ static int loongson_i2s_pci_probe(struct pci_dev *pdev,
i2s->dev = dev;
pci_set_drvdata(pdev, i2s);
- ret = pcim_iomap_regions(pdev, 1 << 0, dev_name(dev));
- if (ret < 0) {
- dev_err(dev, "iomap_regions failed\n");
- return ret;
+ i2s->reg_base = pcim_iomap_region(pdev, 0, DRIVER_NAME);
+ if (IS_ERR(i2s->reg_base)) {
+ dev_err(dev, "iomap_region failed\n");
+ return PTR_ERR(i2s->reg_base);
}
- i2s->reg_base = pcim_iomap_table(pdev)[0];
i2s->regmap = devm_regmap_init_mmio(dev, i2s->reg_base,
&loongson_i2s_regmap_config);
if (IS_ERR(i2s->regmap))
@@ -147,7 +148,7 @@ static const struct pci_device_id loongson_i2s_ids[] = {
MODULE_DEVICE_TABLE(pci, loongson_i2s_ids);
static struct pci_driver loongson_i2s_driver = {
- .name = "loongson-i2s-pci",
+ .name = DRIVER_NAME,
.id_table = loongson_i2s_ids,
.probe = loongson_i2s_pci_probe,
.driver = {
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 90e367586493..10ca8bccabdd 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Mediatek"
+
config SND_SOC_MEDIATEK
tristate
select REGMAP_MMIO
@@ -319,3 +321,5 @@ config SND_SOC_MT8365_MT6357
boards with the MT6357 PMIC codec.
Select Y if you have such device.
If unsure select "N".
+
+endmenu
diff --git a/sound/soc/mediatek/common/mtk-afe-platform-driver.c b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
index 6b6330583941..70fd05d5ff48 100644
--- a/sound/soc/mediatek/common/mtk-afe-platform-driver.c
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -120,7 +120,9 @@ int mtk_afe_pcm_new(struct snd_soc_component *component,
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
size = afe->mtk_afe_hardware->buffer_bytes_max;
- snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, afe->dev, 0, size);
+ snd_pcm_set_managed_buffer_all(pcm, SNDRV_DMA_TYPE_DEV, afe->dev,
+ afe->preallocate_buffers ? size : 0,
+ size);
return 0;
}
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
index f51578b6c50a..a406f2e3e7a8 100644
--- a/sound/soc/mediatek/common/mtk-base-afe.h
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -117,6 +117,7 @@ struct mtk_base_afe {
struct mtk_base_afe_irq *irqs;
int irqs_size;
int memif_32bit_supported;
+ bool preallocate_buffers;
struct list_head sub_dais;
struct snd_soc_dai_driver *dai_drivers;
diff --git a/sound/soc/mediatek/common/mtk-soundcard-driver.c b/sound/soc/mediatek/common/mtk-soundcard-driver.c
index 713a368f79cf..95a083939f3e 100644
--- a/sound/soc/mediatek/common/mtk-soundcard-driver.c
+++ b/sound/soc/mediatek/common/mtk-soundcard-driver.c
@@ -262,9 +262,13 @@ int mtk_soundcard_common_probe(struct platform_device *pdev)
soc_card_data->accdet = accdet_comp;
else
dev_err(&pdev->dev, "No sound component found from mediatek,accdet property\n");
+
+ put_device(&accdet_pdev->dev);
} else {
dev_err(&pdev->dev, "No device found from mediatek,accdet property\n");
}
+
+ of_node_put(accdet_node);
}
platform_node = of_parse_phandle(pdev->dev.of_node, "mediatek,platform", 0);
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 04ed0cfec174..c0fa623e0b17 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -13,6 +13,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
@@ -1053,22 +1054,28 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
struct mtk_base_afe *afe;
struct mt8173_afe_private *afe_priv;
struct snd_soc_component *comp_pcm, *comp_hdmi;
+ struct device *dev = &pdev->dev;
- ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(33));
if (ret)
return ret;
- afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
if (!afe)
return -ENOMEM;
- afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
- GFP_KERNEL);
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL);
afe_priv = afe->platform_priv;
if (!afe_priv)
return -ENOMEM;
- afe->dev = &pdev->dev;
+ afe->dev = dev;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
irq_id = platform_get_irq(pdev, 0);
if (irq_id <= 0)
@@ -1078,27 +1085,27 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
if (IS_ERR(afe->base_addr))
return PTR_ERR(afe->base_addr);
- afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
- &mt8173_afe_regmap_config);
+ afe->regmap = devm_regmap_init_mmio(dev, afe->base_addr,
+ &mt8173_afe_regmap_config);
if (IS_ERR(afe->regmap))
return PTR_ERR(afe->regmap);
/* initial audio related clock */
ret = mt8173_afe_init_audio_clk(afe);
if (ret) {
- dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n");
+ dev_err(dev, "mt8173_afe_init_audio_clk fail\n");
return ret;
}
/* memif % irq initialize*/
afe->memif_size = MT8173_AFE_MEMIF_NUM;
- afe->memif = devm_kcalloc(afe->dev, afe->memif_size,
+ afe->memif = devm_kcalloc(dev, afe->memif_size,
sizeof(*afe->memif), GFP_KERNEL);
if (!afe->memif)
return -ENOMEM;
afe->irqs_size = MT8173_AFE_IRQ_NUM;
- afe->irqs = devm_kcalloc(afe->dev, afe->irqs_size,
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size,
sizeof(*afe->irqs), GFP_KERNEL);
if (!afe->irqs)
return -ENOMEM;
@@ -1117,9 +1124,9 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, afe);
- pm_runtime_enable(&pdev->dev);
- if (!pm_runtime_enabled(&pdev->dev)) {
- ret = mt8173_afe_runtime_resume(&pdev->dev);
+ pm_runtime_enable(dev);
+ if (!pm_runtime_enabled(dev)) {
+ ret = mt8173_afe_runtime_resume(dev);
if (ret)
goto err_pm_disable;
}
@@ -1129,13 +1136,12 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
afe->runtime_resume = mt8173_afe_runtime_resume;
afe->runtime_suspend = mt8173_afe_runtime_suspend;
- ret = devm_snd_soc_register_component(&pdev->dev,
- &mtk_afe_pcm_platform,
- NULL, 0);
+ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
+ NULL, 0);
if (ret)
goto err_pm_disable;
- comp_pcm = devm_kzalloc(&pdev->dev, sizeof(*comp_pcm), GFP_KERNEL);
+ comp_pcm = devm_kzalloc(dev, sizeof(*comp_pcm), GFP_KERNEL);
if (!comp_pcm) {
ret = -ENOMEM;
goto err_pm_disable;
@@ -1143,7 +1149,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
ret = snd_soc_component_initialize(comp_pcm,
&mt8173_afe_pcm_dai_component,
- &pdev->dev);
+ dev);
if (ret)
goto err_pm_disable;
@@ -1157,7 +1163,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret)
goto err_pm_disable;
- comp_hdmi = devm_kzalloc(&pdev->dev, sizeof(*comp_hdmi), GFP_KERNEL);
+ comp_hdmi = devm_kzalloc(dev, sizeof(*comp_hdmi), GFP_KERNEL);
if (!comp_hdmi) {
ret = -ENOMEM;
goto err_cleanup_components;
@@ -1165,7 +1171,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
ret = snd_soc_component_initialize(comp_hdmi,
&mt8173_afe_hdmi_dai_component,
- &pdev->dev);
+ dev);
if (ret)
goto err_cleanup_components;
@@ -1179,30 +1185,32 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret)
goto err_cleanup_components;
- ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
+ ret = devm_request_irq(dev, irq_id, mt8173_afe_irq_handler,
0, "Afe_ISR_Handle", (void *)afe);
if (ret) {
- dev_err(afe->dev, "could not request_irq\n");
+ dev_err(dev, "could not request_irq\n");
goto err_cleanup_components;
}
- dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
+ dev_info(dev, "MT8173 AFE driver initialized.\n");
return 0;
err_cleanup_components:
- snd_soc_unregister_component(&pdev->dev);
+ snd_soc_unregister_component(dev);
err_pm_disable:
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_disable(dev);
return ret;
}
static void mt8173_afe_pcm_dev_remove(struct platform_device *pdev)
{
- snd_soc_unregister_component(&pdev->dev);
+ struct device *dev = &pdev->dev;
+
+ snd_soc_unregister_component(dev);
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- mt8173_afe_runtime_suspend(&pdev->dev);
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+ mt8173_afe_runtime_suspend(dev);
}
static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
index d083b4bf0f95..a7fef772760a 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -6,10 +6,12 @@
// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
#include <linux/delay.h>
+#include <linux/dma-mapping.h>
#include <linux/module.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
@@ -424,342 +426,106 @@ static const struct snd_soc_component_driver mt8183_afe_pcm_dai_component = {
.name = "mt8183-afe-pcm-dai",
};
+#define MT8183_MEMIF_BASE(_id, _en_reg, _fs_reg, _mono_reg) \
+ [MT8183_MEMIF_##_id] = { \
+ .name = #_id, \
+ .id = MT8183_MEMIF_##_id, \
+ .reg_ofs_base = AFE_##_id##_BASE, \
+ .reg_ofs_cur = AFE_##_id##_CUR, \
+ .reg_ofs_end = AFE_##_id##_END, \
+ .reg_ofs_base_msb = AFE_##_id##_BASE_MSB, \
+ .reg_ofs_cur_msb = AFE_##_id##_CUR_MSB, \
+ .reg_ofs_end_msb = AFE_##_id##_END_MSB, \
+ .fs_reg = (_fs_reg), \
+ .fs_shift = _id##_MODE_SFT, \
+ .fs_maskbit = _id##_MODE_MASK, \
+ .mono_reg = (_mono_reg), \
+ .mono_shift = _id##_DATA_SFT, \
+ .enable_reg = (_en_reg), \
+ .enable_shift = _id##_ON_SFT, \
+ .hd_reg = AFE_MEMIF_HD_MODE, \
+ .hd_align_reg = AFE_MEMIF_HDALIGN, \
+ .hd_shift = _id##_HD_SFT, \
+ .hd_align_mshift = _id##_HD_ALIGN_SFT, \
+ .agent_disable_reg = -1, \
+ .agent_disable_shift = -1, \
+ .msb_reg = -1, \
+ .msb_shift = -1, \
+ }
+
+#define MT8183_MEMIF(_id, _fs_reg, _mono_reg) \
+ MT8183_MEMIF_BASE(_id, AFE_DAC_CON0, _fs_reg, _mono_reg)
+
+/* For convenience with macros: missing register fields */
+#define MOD_DAI_DATA_SFT -1
+#define HDMI_MODE_SFT -1
+#define HDMI_MODE_MASK -1
+#define HDMI_DATA_SFT -1
+#define HDMI_ON_SFT -1
+
+/* For convenience with macros: register name differences */
+#define AFE_VUL12_BASE AFE_VUL_D2_BASE
+#define AFE_VUL12_CUR AFE_VUL_D2_CUR
+#define AFE_VUL12_END AFE_VUL_D2_END
+#define AFE_VUL12_BASE_MSB AFE_VUL_D2_BASE_MSB
+#define AFE_VUL12_CUR_MSB AFE_VUL_D2_CUR_MSB
+#define AFE_VUL12_END_MSB AFE_VUL_D2_END_MSB
+#define AWB2_HD_ALIGN_SFT AWB2_ALIGN_SFT
+#define VUL12_DATA_SFT VUL12_MONO_SFT
+#define AFE_HDMI_BASE AFE_HDMI_OUT_BASE
+#define AFE_HDMI_CUR AFE_HDMI_OUT_CUR
+#define AFE_HDMI_END AFE_HDMI_OUT_END
+#define AFE_HDMI_BASE_MSB AFE_HDMI_OUT_BASE_MSB
+#define AFE_HDMI_CUR_MSB AFE_HDMI_OUT_CUR_MSB
+#define AFE_HDMI_END_MSB AFE_HDMI_OUT_END_MSB
+
static const struct mtk_base_memif_data memif_data[MT8183_MEMIF_NUM] = {
- [MT8183_MEMIF_DL1] = {
- .name = "DL1",
- .id = MT8183_MEMIF_DL1,
- .reg_ofs_base = AFE_DL1_BASE,
- .reg_ofs_cur = AFE_DL1_CUR,
- .fs_reg = AFE_DAC_CON1,
- .fs_shift = DL1_MODE_SFT,
- .fs_maskbit = DL1_MODE_MASK,
- .mono_reg = AFE_DAC_CON1,
- .mono_shift = DL1_DATA_SFT,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = DL1_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = DL1_HD_SFT,
- .hd_align_mshift = DL1_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_DL2] = {
- .name = "DL2",
- .id = MT8183_MEMIF_DL2,
- .reg_ofs_base = AFE_DL2_BASE,
- .reg_ofs_cur = AFE_DL2_CUR,
- .fs_reg = AFE_DAC_CON1,
- .fs_shift = DL2_MODE_SFT,
- .fs_maskbit = DL2_MODE_MASK,
- .mono_reg = AFE_DAC_CON1,
- .mono_shift = DL2_DATA_SFT,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = DL2_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = DL2_HD_SFT,
- .hd_align_mshift = DL2_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_DL3] = {
- .name = "DL3",
- .id = MT8183_MEMIF_DL3,
- .reg_ofs_base = AFE_DL3_BASE,
- .reg_ofs_cur = AFE_DL3_CUR,
- .fs_reg = AFE_DAC_CON2,
- .fs_shift = DL3_MODE_SFT,
- .fs_maskbit = DL3_MODE_MASK,
- .mono_reg = AFE_DAC_CON1,
- .mono_shift = DL3_DATA_SFT,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = DL3_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = DL3_HD_SFT,
- .hd_align_mshift = DL3_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_VUL2] = {
- .name = "VUL2",
- .id = MT8183_MEMIF_VUL2,
- .reg_ofs_base = AFE_VUL2_BASE,
- .reg_ofs_cur = AFE_VUL2_CUR,
- .fs_reg = AFE_DAC_CON2,
- .fs_shift = VUL2_MODE_SFT,
- .fs_maskbit = VUL2_MODE_MASK,
- .mono_reg = AFE_DAC_CON2,
- .mono_shift = VUL2_DATA_SFT,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = VUL2_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = VUL2_HD_SFT,
- .hd_align_mshift = VUL2_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_AWB] = {
- .name = "AWB",
- .id = MT8183_MEMIF_AWB,
- .reg_ofs_base = AFE_AWB_BASE,
- .reg_ofs_cur = AFE_AWB_CUR,
- .fs_reg = AFE_DAC_CON1,
- .fs_shift = AWB_MODE_SFT,
- .fs_maskbit = AWB_MODE_MASK,
- .mono_reg = AFE_DAC_CON1,
- .mono_shift = AWB_DATA_SFT,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = AWB_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = AWB_HD_SFT,
- .hd_align_mshift = AWB_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_AWB2] = {
- .name = "AWB2",
- .id = MT8183_MEMIF_AWB2,
- .reg_ofs_base = AFE_AWB2_BASE,
- .reg_ofs_cur = AFE_AWB2_CUR,
- .fs_reg = AFE_DAC_CON2,
- .fs_shift = AWB2_MODE_SFT,
- .fs_maskbit = AWB2_MODE_MASK,
- .mono_reg = AFE_DAC_CON2,
- .mono_shift = AWB2_DATA_SFT,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = AWB2_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = AWB2_HD_SFT,
- .hd_align_mshift = AWB2_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_VUL12] = {
- .name = "VUL12",
- .id = MT8183_MEMIF_VUL12,
- .reg_ofs_base = AFE_VUL_D2_BASE,
- .reg_ofs_cur = AFE_VUL_D2_CUR,
- .fs_reg = AFE_DAC_CON0,
- .fs_shift = VUL12_MODE_SFT,
- .fs_maskbit = VUL12_MODE_MASK,
- .mono_reg = AFE_DAC_CON0,
- .mono_shift = VUL12_MONO_SFT,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = VUL12_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = VUL12_HD_SFT,
- .hd_align_mshift = VUL12_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_MOD_DAI] = {
- .name = "MOD_DAI",
- .id = MT8183_MEMIF_MOD_DAI,
- .reg_ofs_base = AFE_MOD_DAI_BASE,
- .reg_ofs_cur = AFE_MOD_DAI_CUR,
- .fs_reg = AFE_DAC_CON1,
- .fs_shift = MOD_DAI_MODE_SFT,
- .fs_maskbit = MOD_DAI_MODE_MASK,
- .mono_reg = -1,
- .mono_shift = 0,
- .enable_reg = AFE_DAC_CON0,
- .enable_shift = MOD_DAI_ON_SFT,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = MOD_DAI_HD_SFT,
- .hd_align_mshift = MOD_DAI_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
- [MT8183_MEMIF_HDMI] = {
- .name = "HDMI",
- .id = MT8183_MEMIF_HDMI,
- .reg_ofs_base = AFE_HDMI_OUT_BASE,
- .reg_ofs_cur = AFE_HDMI_OUT_CUR,
- .fs_reg = -1,
- .fs_shift = -1,
- .fs_maskbit = -1,
- .mono_reg = -1,
- .mono_shift = -1,
- .enable_reg = -1, /* control in tdm for sync start */
- .enable_shift = -1,
- .hd_reg = AFE_MEMIF_HD_MODE,
- .hd_align_reg = AFE_MEMIF_HDALIGN,
- .hd_shift = HDMI_HD_SFT,
- .hd_align_mshift = HDMI_HD_ALIGN_SFT,
- .agent_disable_reg = -1,
- .agent_disable_shift = -1,
- .msb_reg = -1,
- .msb_shift = -1,
- },
+ MT8183_MEMIF(DL1, AFE_DAC_CON1, AFE_DAC_CON1),
+ MT8183_MEMIF(DL2, AFE_DAC_CON1, AFE_DAC_CON1),
+ MT8183_MEMIF(DL3, AFE_DAC_CON2, AFE_DAC_CON1),
+ MT8183_MEMIF(VUL2, AFE_DAC_CON2, AFE_DAC_CON2),
+ MT8183_MEMIF(AWB, AFE_DAC_CON1, AFE_DAC_CON1),
+ MT8183_MEMIF(AWB2, AFE_DAC_CON2, AFE_DAC_CON2),
+ MT8183_MEMIF(VUL12, AFE_DAC_CON0, AFE_DAC_CON0),
+ MT8183_MEMIF(MOD_DAI, AFE_DAC_CON1, -1),
+ /* enable control in tdm for sync start */
+ MT8183_MEMIF_BASE(HDMI, -1, -1, -1),
};
+#define MT8183_AFE_IRQ_BASE(_id, _fs_reg, _fs_shift, _fs_maskbit) \
+ [MT8183_IRQ_##_id] = { \
+ .id = MT8183_IRQ_##_id, \
+ .irq_cnt_reg = AFE_IRQ_MCU_CNT##_id, \
+ .irq_cnt_shift = 0, \
+ .irq_cnt_maskbit = 0x3ffff, \
+ .irq_fs_reg = _fs_reg, \
+ .irq_fs_shift = _fs_shift, \
+ .irq_fs_maskbit = _fs_maskbit, \
+ .irq_en_reg = AFE_IRQ_MCU_CON0, \
+ .irq_en_shift = IRQ##_id##_MCU_ON_SFT, \
+ .irq_clr_reg = AFE_IRQ_MCU_CLR, \
+ .irq_clr_shift = IRQ##_id##_MCU_CLR_SFT, \
+ }
+
+#define MT8183_AFE_IRQ(_id) \
+ MT8183_AFE_IRQ_BASE(_id, AFE_IRQ_MCU_CON1 + _id / 8 * 4, \
+ IRQ##_id##_MCU_MODE_SFT, \
+ IRQ##_id##_MCU_MODE_MASK)
+
+#define MT8183_AFE_IRQ_NOFS(_id) MT8183_AFE_IRQ_BASE(_id, -1, -1, -1)
+
static const struct mtk_base_irq_data irq_data[MT8183_IRQ_NUM] = {
- [MT8183_IRQ_0] = {
- .id = MT8183_IRQ_0,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT0,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ0_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ0_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ0_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ0_MCU_CLR_SFT,
- },
- [MT8183_IRQ_1] = {
- .id = MT8183_IRQ_1,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT1,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ1_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ1_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ1_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ1_MCU_CLR_SFT,
- },
- [MT8183_IRQ_2] = {
- .id = MT8183_IRQ_2,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT2,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ2_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ2_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ2_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ2_MCU_CLR_SFT,
- },
- [MT8183_IRQ_3] = {
- .id = MT8183_IRQ_3,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT3,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ3_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ3_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ3_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ3_MCU_CLR_SFT,
- },
- [MT8183_IRQ_4] = {
- .id = MT8183_IRQ_4,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT4,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ4_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ4_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ4_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ4_MCU_CLR_SFT,
- },
- [MT8183_IRQ_5] = {
- .id = MT8183_IRQ_5,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT5,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ5_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ5_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ5_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ5_MCU_CLR_SFT,
- },
- [MT8183_IRQ_6] = {
- .id = MT8183_IRQ_6,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT6,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ6_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ6_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ6_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ6_MCU_CLR_SFT,
- },
- [MT8183_IRQ_7] = {
- .id = MT8183_IRQ_7,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT7,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON1,
- .irq_fs_shift = IRQ7_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ7_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ7_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ7_MCU_CLR_SFT,
- },
- [MT8183_IRQ_8] = {
- .id = MT8183_IRQ_8,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT8,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = -1,
- .irq_fs_shift = -1,
- .irq_fs_maskbit = -1,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ8_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ8_MCU_CLR_SFT,
- },
- [MT8183_IRQ_11] = {
- .id = MT8183_IRQ_11,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT11,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON2,
- .irq_fs_shift = IRQ11_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ11_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ11_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ11_MCU_CLR_SFT,
- },
- [MT8183_IRQ_12] = {
- .id = MT8183_IRQ_12,
- .irq_cnt_reg = AFE_IRQ_MCU_CNT12,
- .irq_cnt_shift = 0,
- .irq_cnt_maskbit = 0x3ffff,
- .irq_fs_reg = AFE_IRQ_MCU_CON2,
- .irq_fs_shift = IRQ12_MCU_MODE_SFT,
- .irq_fs_maskbit = IRQ12_MCU_MODE_MASK,
- .irq_en_reg = AFE_IRQ_MCU_CON0,
- .irq_en_shift = IRQ12_MCU_ON_SFT,
- .irq_clr_reg = AFE_IRQ_MCU_CLR,
- .irq_clr_shift = IRQ12_MCU_CLR_SFT,
- },
+ MT8183_AFE_IRQ(0),
+ MT8183_AFE_IRQ(1),
+ MT8183_AFE_IRQ(2),
+ MT8183_AFE_IRQ(3),
+ MT8183_AFE_IRQ(4),
+ MT8183_AFE_IRQ(5),
+ MT8183_AFE_IRQ(6),
+ MT8183_AFE_IRQ(7),
+ MT8183_AFE_IRQ_NOFS(8),
+ MT8183_AFE_IRQ(11),
+ MT8183_AFE_IRQ(12),
};
static bool mt8183_is_volatile_reg(struct device *dev, unsigned int reg)
@@ -767,86 +533,46 @@ static bool mt8183_is_volatile_reg(struct device *dev, unsigned int reg)
/* these auto-gen reg has read-only bit, so put it as volatile */
/* volatile reg cannot be cached, so cannot be set when power off */
switch (reg) {
- case AUDIO_TOP_CON0: /* reg bit controlled by CCF */
- case AUDIO_TOP_CON1: /* reg bit controlled by CCF */
+ case AUDIO_TOP_CON0 ... AUDIO_TOP_CON1: /* reg bit controlled by CCF */
case AUDIO_TOP_CON3:
- case AFE_DL1_CUR:
- case AFE_DL1_END:
- case AFE_DL2_CUR:
- case AFE_DL2_END:
- case AFE_AWB_END:
- case AFE_AWB_CUR:
- case AFE_VUL_END:
- case AFE_VUL_CUR:
- case AFE_MEMIF_MON0:
- case AFE_MEMIF_MON1:
- case AFE_MEMIF_MON2:
- case AFE_MEMIF_MON3:
- case AFE_MEMIF_MON4:
- case AFE_MEMIF_MON5:
- case AFE_MEMIF_MON6:
- case AFE_MEMIF_MON7:
- case AFE_MEMIF_MON8:
- case AFE_MEMIF_MON9:
- case AFE_ADDA_SRC_DEBUG_MON0:
- case AFE_ADDA_SRC_DEBUG_MON1:
- case AFE_ADDA_UL_SRC_MON0:
- case AFE_ADDA_UL_SRC_MON1:
+ case AFE_DL1_CUR ... AFE_DL1_END:
+ case AFE_DL2_CUR ... AFE_DL2_END:
+ case AFE_AWB_END ... AFE_AWB_CUR:
+ case AFE_VUL_END ... AFE_VUL_CUR:
+ case AFE_MEMIF_MON0 ... AFE_MEMIF_MON9:
+ case AFE_ADDA_SRC_DEBUG_MON0 ... AFE_ADDA_SRC_DEBUG_MON1:
+ case AFE_ADDA_UL_SRC_MON0 ... AFE_ADDA_UL_SRC_MON1:
case AFE_SIDETONE_MON:
- case AFE_SIDETONE_CON0:
- case AFE_SIDETONE_COEFF:
+ case AFE_SIDETONE_CON0 ... AFE_SIDETONE_COEFF:
case AFE_BUS_MON0:
- case AFE_MRGIF_MON0:
- case AFE_MRGIF_MON1:
- case AFE_MRGIF_MON2:
- case AFE_I2S_MON:
+ case AFE_MRGIF_MON0 ... AFE_I2S_MON:
case AFE_DAC_MON:
- case AFE_VUL2_END:
- case AFE_VUL2_CUR:
- case AFE_IRQ0_MCU_CNT_MON:
- case AFE_IRQ6_MCU_CNT_MON:
- case AFE_MOD_DAI_END:
- case AFE_MOD_DAI_CUR:
- case AFE_VUL_D2_END:
- case AFE_VUL_D2_CUR:
- case AFE_DL3_CUR:
- case AFE_DL3_END:
+ case AFE_VUL2_END ... AFE_VUL2_CUR:
+ case AFE_IRQ0_MCU_CNT_MON ... AFE_IRQ6_MCU_CNT_MON:
+ case AFE_MOD_DAI_END ... AFE_MOD_DAI_CUR:
+ case AFE_VUL_D2_END ... AFE_VUL_D2_CUR:
+ case AFE_DL3_CUR ... AFE_DL3_END:
case AFE_HDMI_OUT_CON0:
- case AFE_HDMI_OUT_CUR:
- case AFE_HDMI_OUT_END:
- case AFE_IRQ3_MCU_CNT_MON:
- case AFE_IRQ4_MCU_CNT_MON:
- case AFE_IRQ_MCU_STATUS:
- case AFE_IRQ_MCU_CLR:
+ case AFE_HDMI_OUT_CUR ... AFE_HDMI_OUT_END:
+ case AFE_IRQ3_MCU_CNT_MON... AFE_IRQ4_MCU_CNT_MON:
+ case AFE_IRQ_MCU_STATUS ... AFE_IRQ_MCU_CLR:
case AFE_IRQ_MCU_MON2:
- case AFE_IRQ1_MCU_CNT_MON:
- case AFE_IRQ2_MCU_CNT_MON:
- case AFE_IRQ1_MCU_EN_CNT_MON:
- case AFE_IRQ5_MCU_CNT_MON:
+ case AFE_IRQ1_MCU_CNT_MON ... AFE_IRQ5_MCU_CNT_MON:
case AFE_IRQ7_MCU_CNT_MON:
case AFE_GAIN1_CUR:
case AFE_GAIN2_CUR:
case AFE_SRAM_DELSEL_CON0:
- case AFE_SRAM_DELSEL_CON2:
- case AFE_SRAM_DELSEL_CON3:
- case AFE_ASRC_2CH_CON12:
- case AFE_ASRC_2CH_CON13:
+ case AFE_SRAM_DELSEL_CON2 ... AFE_SRAM_DELSEL_CON3:
+ case AFE_ASRC_2CH_CON12 ... AFE_ASRC_2CH_CON13:
case PCM_INTF_CON2:
- case FPGA_CFG0:
- case FPGA_CFG1:
- case FPGA_CFG2:
- case FPGA_CFG3:
- case AUDIO_TOP_DBG_MON0:
- case AUDIO_TOP_DBG_MON1:
- case AFE_IRQ8_MCU_CNT_MON:
- case AFE_IRQ11_MCU_CNT_MON:
- case AFE_IRQ12_MCU_CNT_MON:
+ case FPGA_CFG0 ... FPGA_CFG1:
+ case FPGA_CFG2 ... FPGA_CFG3:
+ case AUDIO_TOP_DBG_MON0 ... AUDIO_TOP_DBG_MON1:
+ case AFE_IRQ8_MCU_CNT_MON ... AFE_IRQ12_MCU_CNT_MON:
case AFE_CBIP_MON0:
- case AFE_CBIP_SLV_MUX_MON0:
- case AFE_CBIP_SLV_DECODER_MON0:
+ case AFE_CBIP_SLV_MUX_MON0 ... AFE_CBIP_SLV_DECODER_MON0:
case AFE_ADDA6_SRC_DEBUG_MON0:
- case AFE_ADD6A_UL_SRC_MON0:
- case AFE_ADDA6_UL_SRC_MON1:
+ case AFE_ADD6A_UL_SRC_MON0... AFE_ADDA6_UL_SRC_MON1:
case AFE_DL1_CUR_MSB:
case AFE_DL2_CUR_MSB:
case AFE_AWB_CUR_MSB:
@@ -856,55 +582,23 @@ static bool mt8183_is_volatile_reg(struct device *dev, unsigned int reg)
case AFE_VUL_D2_CUR_MSB:
case AFE_DL3_CUR_MSB:
case AFE_HDMI_OUT_CUR_MSB:
- case AFE_AWB2_END:
- case AFE_AWB2_CUR:
+ case AFE_AWB2_END ... AFE_AWB2_CUR:
case AFE_AWB2_CUR_MSB:
- case AFE_ADDA_DL_SDM_FIFO_MON:
- case AFE_ADDA_DL_SRC_LCH_MON:
- case AFE_ADDA_DL_SRC_RCH_MON:
- case AFE_ADDA_DL_SDM_OUT_MON:
- case AFE_CONNSYS_I2S_MON:
- case AFE_ASRC_2CH_CON0:
- case AFE_ASRC_2CH_CON2:
- case AFE_ASRC_2CH_CON3:
- case AFE_ASRC_2CH_CON4:
- case AFE_ASRC_2CH_CON5:
- case AFE_ASRC_2CH_CON7:
- case AFE_ASRC_2CH_CON8:
- case AFE_MEMIF_MON12:
- case AFE_MEMIF_MON13:
- case AFE_MEMIF_MON14:
- case AFE_MEMIF_MON15:
- case AFE_MEMIF_MON16:
- case AFE_MEMIF_MON17:
- case AFE_MEMIF_MON18:
- case AFE_MEMIF_MON19:
- case AFE_MEMIF_MON20:
- case AFE_MEMIF_MON21:
- case AFE_MEMIF_MON22:
- case AFE_MEMIF_MON23:
- case AFE_MEMIF_MON24:
- case AFE_ADDA_MTKAIF_MON0:
- case AFE_ADDA_MTKAIF_MON1:
+ case AFE_ADDA_DL_SDM_FIFO_MON ... AFE_ADDA_DL_SDM_OUT_MON:
+ case AFE_CONNSYS_I2S_MON ... AFE_ASRC_2CH_CON0:
+ case AFE_ASRC_2CH_CON2 ... AFE_ASRC_2CH_CON5:
+ case AFE_ASRC_2CH_CON7 ... AFE_ASRC_2CH_CON8:
+ case AFE_MEMIF_MON12 ... AFE_MEMIF_MON24:
+ case AFE_ADDA_MTKAIF_MON0 ... AFE_ADDA_MTKAIF_MON1:
case AFE_AUD_PAD_TOP:
case AFE_GENERAL1_ASRC_2CH_CON0:
- case AFE_GENERAL1_ASRC_2CH_CON2:
- case AFE_GENERAL1_ASRC_2CH_CON3:
- case AFE_GENERAL1_ASRC_2CH_CON4:
- case AFE_GENERAL1_ASRC_2CH_CON5:
- case AFE_GENERAL1_ASRC_2CH_CON7:
- case AFE_GENERAL1_ASRC_2CH_CON8:
- case AFE_GENERAL1_ASRC_2CH_CON12:
- case AFE_GENERAL1_ASRC_2CH_CON13:
+ case AFE_GENERAL1_ASRC_2CH_CON2 ... AFE_GENERAL1_ASRC_2CH_CON5:
+ case AFE_GENERAL1_ASRC_2CH_CON7 ... AFE_GENERAL1_ASRC_2CH_CON8:
+ case AFE_GENERAL1_ASRC_2CH_CON12 ... AFE_GENERAL1_ASRC_2CH_CON13:
case AFE_GENERAL2_ASRC_2CH_CON0:
- case AFE_GENERAL2_ASRC_2CH_CON2:
- case AFE_GENERAL2_ASRC_2CH_CON3:
- case AFE_GENERAL2_ASRC_2CH_CON4:
- case AFE_GENERAL2_ASRC_2CH_CON5:
- case AFE_GENERAL2_ASRC_2CH_CON7:
- case AFE_GENERAL2_ASRC_2CH_CON8:
- case AFE_GENERAL2_ASRC_2CH_CON12:
- case AFE_GENERAL2_ASRC_2CH_CON13:
+ case AFE_GENERAL2_ASRC_2CH_CON2 ... AFE_GENERAL2_ASRC_2CH_CON5:
+ case AFE_GENERAL2_ASRC_2CH_CON7 ... AFE_GENERAL2_ASRC_2CH_CON8:
+ case AFE_GENERAL2_ASRC_2CH_CON12 ... AFE_GENERAL2_ASRC_2CH_CON13:
return true;
default:
return false;
@@ -1076,23 +770,31 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
struct mt8183_afe_private *afe_priv;
- struct device *dev;
+ struct device *dev = &pdev->dev;
struct reset_control *rstc;
int i, irq_id, ret;
- afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
+ ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(34));
+ if (ret)
+ return ret;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
if (!afe)
return -ENOMEM;
platform_set_drvdata(pdev, afe);
- afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
- GFP_KERNEL);
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv), GFP_KERNEL);
if (!afe->platform_priv)
return -ENOMEM;
afe_priv = afe->platform_priv;
- afe->dev = &pdev->dev;
- dev = afe->dev;
+ afe->dev = dev;
+
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
/* initial audio related clock */
ret = mt8183_init_clock(afe);
@@ -1131,7 +833,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
/* enable clock for regcache get default value from hw */
afe_priv->pm_runtime_bypass_reg_ctl = true;
- pm_runtime_get_sync(&pdev->dev);
+ pm_runtime_get_sync(dev);
ret = regmap_reinit_cache(afe->regmap, &mt8183_afe_regmap_config);
if (ret) {
@@ -1139,7 +841,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
goto err_pm_disable;
}
- pm_runtime_put_sync(&pdev->dev);
+ pm_runtime_put_sync(dev);
afe_priv->pm_runtime_bypass_reg_ctl = false;
regcache_cache_only(afe->regmap, true);
@@ -1197,7 +899,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
ret = dai_register_cbs[i](afe);
if (ret) {
- dev_warn(afe->dev, "dai register i %d fail, ret %d\n",
+ dev_warn(dev, "dai register i %d fail, ret %d\n",
i, ret);
goto err_pm_disable;
}
@@ -1206,8 +908,7 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
/* init dai_driver and component_driver */
ret = mtk_afe_combine_sub_dai(afe);
if (ret) {
- dev_warn(afe->dev, "mtk_afe_combine_sub_dai fail, ret %d\n",
- ret);
+ dev_warn(dev, "mtk_afe_combine_sub_dai fail, ret %d\n", ret);
goto err_pm_disable;
}
@@ -1219,16 +920,14 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
afe->runtime_suspend = mt8183_afe_runtime_suspend;
/* register component */
- ret = devm_snd_soc_register_component(&pdev->dev,
- &mtk_afe_pcm_platform,
+ ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
NULL, 0);
if (ret) {
dev_warn(dev, "err_platform\n");
goto err_pm_disable;
}
- ret = devm_snd_soc_register_component(afe->dev,
- &mt8183_afe_pcm_dai_component,
+ ret = devm_snd_soc_register_component(dev, &mt8183_afe_pcm_dai_component,
afe->dai_drivers,
afe->num_dai_drivers);
if (ret) {
@@ -1239,15 +938,17 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
return ret;
err_pm_disable:
- pm_runtime_disable(&pdev->dev);
+ pm_runtime_disable(dev);
return ret;
}
static void mt8183_afe_pcm_dev_remove(struct platform_device *pdev)
{
- pm_runtime_disable(&pdev->dev);
- if (!pm_runtime_status_suspended(&pdev->dev))
- mt8183_afe_runtime_suspend(&pdev->dev);
+ struct device *dev = &pdev->dev;
+
+ pm_runtime_disable(dev);
+ if (!pm_runtime_status_suspended(dev))
+ mt8183_afe_runtime_suspend(dev);
}
static const struct of_device_id mt8183_afe_pcm_dt_match[] = {
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
index db7c93401bee..c73b4664e53e 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/soc.h>
@@ -2835,6 +2836,12 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev)
afe_priv = afe->platform_priv;
afe->dev = &pdev->dev;
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
+
afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(afe->base_addr))
return PTR_ERR(afe->base_addr);
diff --git a/sound/soc/mediatek/mt8188/mt8188-mt6359.c b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
index 20dc9470ba76..ea814a0f726d 100644
--- a/sound/soc/mediatek/mt8188/mt8188-mt6359.c
+++ b/sound/soc/mediatek/mt8188/mt8188-mt6359.c
@@ -408,7 +408,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
}
if (pin_w)
- dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_PRE_PMU);
+ snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_PRE_PMU);
else
dev_dbg(afe->dev, "%s(), no pinmux widget, please check if default on\n", __func__);
@@ -510,7 +510,7 @@ static int mt8188_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
param->mtkaif_phase_cycle[i] = mtkaif_phase_cycle[i];
if (pin_w)
- dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_POST_PMD);
+ snd_soc_dapm_pinctrl_event(pin_w, NULL, SND_SOC_DAPM_POST_PMD);
dev_dbg(afe->dev, "%s(), end, calibration ok %d\n",
__func__, param->mtkaif_calibration_ok);
@@ -1333,11 +1333,11 @@ static int mt8188_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data,
for_each_card_prelinks(card, i, dai_link) {
if (strcmp(dai_link->name, "DPTX_BE") == 0) {
if (dai_link->num_codecs &&
- strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
dai_link->init = mt8188_dptx_codec_init;
} else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
if (dai_link->num_codecs &&
- strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
dai_link->init = mt8188_hdmi_codec_init;
} else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
strcmp(dai_link->name, "UL_SRC_BE") == 0) {
@@ -1387,7 +1387,7 @@ static int mt8188_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data,
init_es8326 = true;
}
} else {
- if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) {
+ if (!snd_soc_dlc_is_dummy(dai_link->codecs)) {
if (!init_dumb) {
dai_link->init = mt8188_dumb_amp_init;
init_dumb = true;
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
index fd6af74d7995..3d32fe46118e 100644
--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
@@ -12,6 +12,7 @@
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/of_reserved_mem.h>
#include <linux/pm_runtime.h>
#include <linux/reset.h>
#include <sound/soc.h>
@@ -2179,6 +2180,12 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
afe->dev = dev;
+ ret = of_reserved_mem_device_init(dev);
+ if (ret) {
+ dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
+ afe->preallocate_buffers = true;
+ }
+
/* init audio related clock */
ret = mt8192_init_clock(afe);
if (ret) {
diff --git a/sound/soc/mediatek/mt8195/mt8195-mt6359.c b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
index df29a9fa5aee..e57391c213e7 100644
--- a/sound/soc/mediatek/mt8195/mt8195-mt6359.c
+++ b/sound/soc/mediatek/mt8195/mt8195-mt6359.c
@@ -92,10 +92,6 @@ static const struct snd_soc_dapm_widget mt8195_mt6359_widgets[] = {
};
static const struct snd_soc_dapm_route mt8195_mt6359_routes[] = {
- /* headset */
- { "Headphone", NULL, "HPOL" },
- { "Headphone", NULL, "HPOR" },
- { "IN1P", NULL, "Headset Mic" },
/* SOF Uplink */
{SOF_DMA_UL4, NULL, "O034"},
{SOF_DMA_UL4, NULL, "O035"},
@@ -131,6 +127,13 @@ static const struct snd_kcontrol_new mt8195_speaker_controls[] = {
SOC_DAPM_PIN_SWITCH("Ext Spk"),
};
+static const struct snd_soc_dapm_route mt8195_rt5682_routes[] = {
+ /* headset */
+ { "Headphone", NULL, "HPOL" },
+ { "Headphone", NULL, "HPOR" },
+ { "IN1P", NULL, "Headset Mic" },
+};
+
static const struct snd_soc_dapm_route mt8195_rt1011_routes[] = {
{ "Left Spk", NULL, "Left SPO" },
{ "Right Spk", NULL, "Right SPO" },
@@ -447,6 +450,7 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
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;
+ struct snd_soc_card *card = rtd->card;
int ret;
priv->i2so1_mclk = afe_priv->clk[MT8195_CLK_TOP_APLL12_DIV2];
@@ -473,7 +477,12 @@ static int mt8195_rt5682_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
- return 0;
+ ret = snd_soc_dapm_add_routes(&card->dapm, mt8195_rt5682_routes,
+ ARRAY_SIZE(mt8195_rt5682_routes));
+ if (ret)
+ dev_err(rtd->dev, "unable to add dapm routes, ret %d\n", ret);
+
+ return ret;
};
static int mt8195_rt1011_etdm_hw_params(struct snd_pcm_substream *substream,
@@ -822,12 +831,12 @@ SND_SOC_DAILINK_DEFS(ETDM1_IN_BE,
SND_SOC_DAILINK_DEFS(ETDM2_IN_BE,
DAILINK_COMP_ARRAY(COMP_CPU("ETDM2_IN")),
- DAILINK_COMP_ARRAY(COMP_EMPTY()),
+ 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_EMPTY()),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
DAILINK_COMP_ARRAY(COMP_EMPTY()));
SND_SOC_DAILINK_DEFS(ETDM2_OUT_BE,
@@ -1379,11 +1388,11 @@ static int mt8195_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data,
for_each_card_prelinks(card, i, dai_link) {
if (strcmp(dai_link->name, "DPTX_BE") == 0) {
if (dai_link->num_codecs &&
- strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
dai_link->init = mt8195_dptx_codec_init;
} else if (strcmp(dai_link->name, "ETDM3_OUT_BE") == 0) {
if (dai_link->num_codecs &&
- strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai"))
+ !snd_soc_dlc_is_dummy(dai_link->codecs))
dai_link->init = mt8195_hdmi_codec_init;
} else if (strcmp(dai_link->name, "DL_SRC_BE") == 0 ||
strcmp(dai_link->name, "UL_SRC1_BE") == 0 ||
@@ -1423,7 +1432,7 @@ static int mt8195_mt6359_soc_card_probe(struct mtk_soc_card_data *soc_card_data,
codec_init |= RT5682_CODEC_INIT;
}
} else {
- if (strcmp(dai_link->codecs->dai_name, "snd-soc-dummy-dai")) {
+ if (!snd_soc_dlc_is_dummy(dai_link->codecs)) {
if (!(codec_init & DUMB_CODEC_INIT)) {
dai_link->init = mt8195_dumb_amp_init;
codec_init |= DUMB_CODEC_INIT;
@@ -1515,6 +1524,18 @@ static const struct mtk_soundcard_pdata mt8195_mt6359_max98390_rt5682_card = {
.soc_probe = mt8195_mt6359_soc_card_probe
};
+static const struct mtk_soundcard_pdata mt8195_mt6359_card = {
+ .card_name = "mt8195_mt6359",
+ .card_data = &(struct mtk_platform_card_data) {
+ .card = &mt8195_mt6359_soc_card,
+ .num_jacks = MT8195_JACK_MAX,
+ .pcm_constraints = mt8195_pcm_constraints,
+ .num_pcm_constraints = ARRAY_SIZE(mt8195_pcm_constraints),
+ },
+ .sof_priv = &mt8195_sof_priv,
+ .soc_probe = mt8195_mt6359_soc_card_probe
+};
+
static const struct of_device_id mt8195_mt6359_dt_match[] = {
{
.compatible = "mediatek,mt8195_mt6359_rt1019_rt5682",
@@ -1528,6 +1549,10 @@ static const struct of_device_id mt8195_mt6359_dt_match[] = {
.compatible = "mediatek,mt8195_mt6359_max98390_rt5682",
.data = &mt8195_mt6359_max98390_rt5682_card,
},
+ {
+ .compatible = "mediatek,mt8195_mt6359",
+ .data = &mt8195_mt6359_card,
+ },
{},
};
MODULE_DEVICE_TABLE(of, mt8195_mt6359_dt_match);
diff --git a/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c
index cae51756cead..cb9beb172ed5 100644
--- a/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c
+++ b/sound/soc/mediatek/mt8365/mt8365-dai-i2s.c
@@ -812,11 +812,10 @@ static const struct snd_soc_dapm_route mtk_dai_i2s_routes[] = {
static int mt8365_dai_i2s_set_priv(struct mtk_base_afe *afe)
{
int i, ret;
- struct mt8365_afe_private *afe_priv = afe->platform_priv;
for (i = 0; i < DAI_I2S_NUM; i++) {
ret = mt8365_dai_set_priv(afe, mt8365_i2s_priv[i].id,
- sizeof(*afe_priv),
+ sizeof(mt8365_i2s_priv[i]),
&mt8365_i2s_priv[i]);
if (ret)
return ret;
diff --git a/sound/soc/meson/Kconfig b/sound/soc/meson/Kconfig
index 6458d5dc4902..d9a730994a2a 100644
--- a/sound/soc/meson/Kconfig
+++ b/sound/soc/meson/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-menu "ASoC support for Amlogic platforms"
+menu "Amlogic"
depends on ARCH_MESON || (COMPILE_TEST && COMMON_CLK)
config SND_MESON_AIU
diff --git a/sound/soc/meson/axg-card.c b/sound/soc/meson/axg-card.c
index a2dfccb7990f..b4dca80e15e4 100644
--- a/sound/soc/meson/axg-card.c
+++ b/sound/soc/meson/axg-card.c
@@ -222,7 +222,6 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
struct axg_dai_link_tdm_data *be)
{
struct axg_dai_link_tdm_mask *codec_mask;
- struct device_node *np;
codec_mask = devm_kcalloc(card->dev, link->num_codecs,
sizeof(*codec_mask), GFP_KERNEL);
@@ -231,7 +230,7 @@ static int axg_card_parse_codecs_masks(struct snd_soc_card *card,
be->codec_masks = codec_mask;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask",
&codec_mask->rx);
snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask",
diff --git a/sound/soc/meson/meson-card-utils.c b/sound/soc/meson/meson-card-utils.c
index cfc7f6e41ab5..cdb759b466ad 100644
--- a/sound/soc/meson/meson-card-utils.c
+++ b/sound/soc/meson/meson-card-utils.c
@@ -137,7 +137,6 @@ int meson_card_set_be_link(struct snd_soc_card *card,
struct device_node *node)
{
struct snd_soc_dai_link_component *codec;
- struct device_node *np;
int ret, num_codecs;
num_codecs = of_get_child_count(node);
@@ -154,19 +153,17 @@ int meson_card_set_be_link(struct snd_soc_card *card,
link->codecs = codec;
link->num_codecs = num_codecs;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
ret = meson_card_parse_dai(card, np, codec);
- if (ret) {
- of_node_put(np);
+ if (ret)
return ret;
- }
codec++;
}
ret = meson_card_set_link_name(card, link, node, "be");
if (ret)
- dev_err(card->dev, "error setting %pOFn link name\n", np);
+ dev_err(card->dev, "error setting %pOFn link name\n", node);
return ret;
}
@@ -198,7 +195,6 @@ static int meson_card_add_links(struct snd_soc_card *card)
{
struct meson_card *priv = snd_soc_card_get_drvdata(card);
struct device_node *node = card->dev->of_node;
- struct device_node *np;
int num, i, ret;
num = of_get_child_count(node);
@@ -212,12 +208,10 @@ static int meson_card_add_links(struct snd_soc_card *card)
return ret;
i = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
ret = priv->match_data->add_link(card, np, &i);
- if (ret) {
- of_node_put(np);
+ if (ret)
return ret;
- }
i++;
}
@@ -231,7 +225,7 @@ static int meson_card_parse_of_optional(struct snd_soc_card *card,
const char *p))
{
/* If property is not provided, don't fail ... */
- if (!of_property_read_bool(card->dev->of_node, propname))
+ if (!of_property_present(card->dev->of_node, propname))
return 0;
/* ... but do fail if it is provided and the parsing fails */
diff --git a/sound/soc/mxs/Kconfig b/sound/soc/mxs/Kconfig
index 402ef1ee7a32..2fb1ca711f71 100644
--- a/sound/soc/mxs/Kconfig
+++ b/sound/soc/mxs/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_MXS_SOC
- tristate "SoC Audio for Freescale MXS CPUs"
+ tristate "Freescale MXS"
depends on ARCH_MXS || COMPILE_TEST
depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig
index e05d6ce4c8fa..e026f9912a6d 100644
--- a/sound/soc/pxa/Kconfig
+++ b/sound/soc/pxa/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "PXA"
+
config SND_PXA2XX_SOC
tristate "SoC Audio for the Intel PXA2xx chip"
depends on ARCH_PXA || COMPILE_TEST
@@ -52,3 +54,5 @@ config SND_PXA910_SOC
help
Say Y if you want to add support for SoC audio on the
Marvell PXA910 reference platform.
+
+endmenu
diff --git a/sound/soc/qcom/Kconfig b/sound/soc/qcom/Kconfig
index ca7a30ebd26a..e6e24f3b9922 100644
--- a/sound/soc/qcom/Kconfig
+++ b/sound/soc/qcom/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_SOC_QCOM
- tristate "ASoC support for QCOM platforms"
+ tristate "Qualcomm"
depends on ARCH_QCOM || COMPILE_TEST
help
Say Y or M if you want to add support to use audio devices
@@ -118,6 +118,22 @@ config SND_SOC_QDSP6_PRM
tristate
select SND_SOC_QDSP6_PRM_LPASS_CLOCKS
+config SND_SOC_QCOM_OFFLOAD_UTILS
+ tristate
+
+config SND_SOC_QDSP6_USB
+ tristate "SoC ALSA USB offloading backing for QDSP6"
+ depends on SND_SOC_USB
+ select AUXILIARY_BUS
+ select SND_SOC_QCOM_OFFLOAD_UTILS
+
+ help
+ Adds support for USB offloading for QDSP6 ASoC
+ based platform sound cards. This will enable the
+ Q6USB DPCM backend DAI link, which will interact
+ with the SoC USB framework to initialize a session
+ with active USB SND devices.
+
config SND_SOC_QDSP6
tristate "SoC ALSA audio driver for QDSP6"
depends on QCOM_APR
@@ -170,6 +186,7 @@ config SND_SOC_SM8250
tristate "SoC Machine driver for SM8250 boards"
depends on QCOM_APR && SOUNDWIRE
depends on COMMON_CLK
+ depends on SND_SOC_QCOM_OFFLOAD_UTILS || !SND_SOC_QCOM_OFFLOAD_UTILS
select SND_SOC_QDSP6
select SND_SOC_QCOM_COMMON
select SND_SOC_QCOM_SDW
diff --git a/sound/soc/qcom/Makefile b/sound/soc/qcom/Makefile
index 16db7b53ddac..985ce2ae286b 100644
--- a/sound/soc/qcom/Makefile
+++ b/sound/soc/qcom/Makefile
@@ -30,6 +30,7 @@ snd-soc-sc8280xp-y := sc8280xp.o
snd-soc-qcom-common-y := common.o
snd-soc-qcom-sdw-y := sdw.o
snd-soc-x1e80100-y := x1e80100.o
+snd-soc-qcom-offload-utils-objs := usb_offload_utils.o
obj-$(CONFIG_SND_SOC_STORM) += snd-soc-storm.o
obj-$(CONFIG_SND_SOC_APQ8016_SBC) += snd-soc-apq8016-sbc.o
@@ -42,6 +43,7 @@ obj-$(CONFIG_SND_SOC_SM8250) += snd-soc-sm8250.o
obj-$(CONFIG_SND_SOC_QCOM_COMMON) += snd-soc-qcom-common.o
obj-$(CONFIG_SND_SOC_QCOM_SDW) += snd-soc-qcom-sdw.o
obj-$(CONFIG_SND_SOC_X1E80100) += snd-soc-x1e80100.o
+obj-$(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) += snd-soc-qcom-offload-utils.o
#DSP lib
obj-$(CONFIG_SND_SOC_QDSP6) += qdsp6/
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index 9946f12254b3..b456e096f138 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -202,7 +202,6 @@ static int lpass_platform_pcmops_open(struct snd_soc_component *component,
struct regmap *map;
unsigned int dai_id = cpu_dai->driver->id;
- component->id = dai_id;
data = kzalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
@@ -1190,13 +1189,14 @@ static int lpass_platform_pcmops_suspend(struct snd_soc_component *component)
{
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct regmap *map;
- unsigned int dai_id = component->id;
- if (dai_id == LPASS_DP_RX)
+ if (drvdata->hdmi_port_enable) {
map = drvdata->hdmiif_map;
- else
- map = drvdata->lpaif_map;
+ regcache_cache_only(map, true);
+ regcache_mark_dirty(map);
+ }
+ map = drvdata->lpaif_map;
regcache_cache_only(map, true);
regcache_mark_dirty(map);
@@ -1207,14 +1207,19 @@ static int lpass_platform_pcmops_resume(struct snd_soc_component *component)
{
struct lpass_data *drvdata = snd_soc_component_get_drvdata(component);
struct regmap *map;
- unsigned int dai_id = component->id;
+ int ret;
- if (dai_id == LPASS_DP_RX)
+ if (drvdata->hdmi_port_enable) {
map = drvdata->hdmiif_map;
- else
- map = drvdata->lpaif_map;
+ regcache_cache_only(map, false);
+ ret = regcache_sync(map);
+ if (ret)
+ return ret;
+ }
+ map = drvdata->lpaif_map;
regcache_cache_only(map, false);
+
return regcache_sync(map);
}
@@ -1224,7 +1229,9 @@ static int lpass_platform_copy(struct snd_soc_component *component,
unsigned long bytes)
{
struct snd_pcm_runtime *rt = substream->runtime;
- unsigned int dai_id = component->id;
+ struct snd_soc_pcm_runtime *soc_runtime = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(soc_runtime, 0);
+ unsigned int dai_id = cpu_dai->driver->id;
int ret = 0;
void __iomem *dma_buf = (void __iomem *) (rt->dma_area + pos +
diff --git a/sound/soc/qcom/qdsp6/Makefile b/sound/soc/qcom/qdsp6/Makefile
index 26b7c55c9c11..67267304e7e9 100644
--- a/sound/soc/qcom/qdsp6/Makefile
+++ b/sound/soc/qcom/qdsp6/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SND_SOC_QDSP6_APM_DAI) += q6apm-dai.o
obj-$(CONFIG_SND_SOC_QDSP6_APM_LPASS_DAI) += q6apm-lpass-dais.o
obj-$(CONFIG_SND_SOC_QDSP6_PRM) += q6prm.o
obj-$(CONFIG_SND_SOC_QDSP6_PRM_LPASS_CLOCKS) += q6prm-clocks.o
+obj-$(CONFIG_SND_SOC_QDSP6_USB) += q6usb.o
diff --git a/sound/soc/qcom/qdsp6/q6afe-dai.c b/sound/soc/qcom/qdsp6/q6afe-dai.c
index 7d9628cda875..0f47aadaabe1 100644
--- a/sound/soc/qcom/qdsp6/q6afe-dai.c
+++ b/sound/soc/qcom/qdsp6/q6afe-dai.c
@@ -92,6 +92,39 @@ static int q6hdmi_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static int q6afe_usb_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6afe_dai_data *dai_data = dev_get_drvdata(dai->dev);
+ int channels = params_channels(params);
+ int rate = params_rate(params);
+ struct q6afe_usb_cfg *usb = &dai_data->port_config[dai->id].usb_audio;
+
+ usb->sample_rate = rate;
+ usb->num_channels = channels;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U16_LE:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ usb->bit_width = 16;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ usb->bit_width = 24;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ usb->bit_width = 32;
+ break;
+ default:
+ dev_err(dai->dev, "%s: invalid format %d\n",
+ __func__, params_format(params));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int q6i2s_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
@@ -394,6 +427,10 @@ static int q6afe_dai_prepare(struct snd_pcm_substream *substream,
q6afe_cdc_dma_port_prepare(dai_data->port[dai->id],
&dai_data->port_config[dai->id].dma_cfg);
break;
+ case USB_RX:
+ q6afe_usb_port_prepare(dai_data->port[dai->id],
+ &dai_data->port_config[dai->id].usb_audio);
+ break;
default:
return -EINVAL;
}
@@ -622,6 +659,9 @@ static const struct snd_soc_dapm_route q6afe_dapm_routes[] = {
{"TX_CODEC_DMA_TX_5", NULL, "TX_CODEC_DMA_TX_5 Capture"},
{"RX_CODEC_DMA_RX_6 Playback", NULL, "RX_CODEC_DMA_RX_6"},
{"RX_CODEC_DMA_RX_7 Playback", NULL, "RX_CODEC_DMA_RX_7"},
+
+ /* USB playback AFE port receives data for playback, hence use the RX port */
+ {"USB Playback", NULL, "USB_RX"},
};
static int msm_dai_q6_dai_probe(struct snd_soc_dai *dai)
@@ -649,6 +689,23 @@ static int msm_dai_q6_dai_remove(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops q6afe_usb_ops = {
+ .probe = msm_dai_q6_dai_probe,
+ .prepare = q6afe_dai_prepare,
+ .hw_params = q6afe_usb_hw_params,
+ /*
+ * Shutdown callback required to stop the USB AFE port, which is enabled
+ * by the prepare() stage. This stops the audio traffic on the USB AFE
+ * port on the Q6DSP.
+ */
+ .shutdown = q6afe_dai_shutdown,
+ /*
+ * Startup callback not needed, as AFE port start command passes the PCM
+ * parameters within the AFE command, which is provided by the PCM core
+ * during the prepare() stage.
+ */
+};
+
static const struct snd_soc_dai_ops q6hdmi_ops = {
.probe = msm_dai_q6_dai_probe,
.remove = msm_dai_q6_dai_remove,
@@ -947,6 +1004,8 @@ static const struct snd_soc_dapm_widget q6afe_dai_widgets[] = {
0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("RX_CODEC_DMA_RX_7", "NULL",
0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("USB_RX", NULL, 0, SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_component_driver q6afe_dai_component = {
@@ -1061,6 +1120,7 @@ static int q6afe_dai_dev_probe(struct platform_device *pdev)
cfg.q6i2s_ops = &q6i2s_ops;
cfg.q6tdm_ops = &q6tdm_ops;
cfg.q6dma_ops = &q6dma_ops;
+ cfg.q6usb_ops = &q6afe_usb_ops;
dais = q6dsp_audio_ports_set_config(dev, &cfg, &num_dais);
return devm_snd_soc_register_component(dev, &q6afe_dai_component, dais, num_dais);
diff --git a/sound/soc/qcom/qdsp6/q6afe.c b/sound/soc/qcom/qdsp6/q6afe.c
index ef7557be5d66..7b59d514b432 100644
--- a/sound/soc/qcom/qdsp6/q6afe.c
+++ b/sound/soc/qcom/qdsp6/q6afe.c
@@ -35,6 +35,8 @@
#define AFE_MODULE_TDM 0x0001028A
#define AFE_PARAM_ID_CDC_SLIMBUS_SLAVE_CFG 0x00010235
+#define AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS 0x000102A5
+#define AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT 0x000102AA
#define AFE_PARAM_ID_LPAIF_CLK_CONFIG 0x00010238
#define AFE_PARAM_ID_INT_DIGITAL_CDC_CLK_CONFIG 0x00010239
@@ -44,6 +46,7 @@
#define AFE_PARAM_ID_TDM_CONFIG 0x0001029D
#define AFE_PARAM_ID_PORT_SLOT_MAPPING_CONFIG 0x00010297
#define AFE_PARAM_ID_CODEC_DMA_CONFIG 0x000102B8
+#define AFE_PARAM_ID_USB_AUDIO_CONFIG 0x000102A4
#define AFE_CMD_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f4
#define AFE_CMD_RSP_REMOTE_LPASS_CORE_HW_VOTE_REQUEST 0x000100f5
#define AFE_CMD_REMOTE_LPASS_CORE_HW_DEVOTE_REQUEST 0x000100f6
@@ -72,12 +75,16 @@
#define AFE_PORT_CONFIG_I2S_WS_SRC_INTERNAL 0x1
#define AFE_LINEAR_PCM_DATA 0x0
+#define AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG 0x1
/* Port IDs */
#define AFE_API_VERSION_HDMI_CONFIG 0x1
#define AFE_PORT_ID_MULTICHAN_HDMI_RX 0x100E
#define AFE_PORT_ID_HDMI_OVER_DP_RX 0x6020
+/* USB AFE port */
+#define AFE_PORT_ID_USB_RX 0x7000
+
#define AFE_API_VERSION_SLIMBUS_CONFIG 0x1
/* Clock set API version */
#define AFE_API_VERSION_CLOCK_SET 1
@@ -359,7 +366,7 @@
#define AFE_API_VERSION_SLOT_MAPPING_CONFIG 1
#define AFE_API_VERSION_CODEC_DMA_CONFIG 1
-#define TIMEOUT_MS 1000
+#define TIMEOUT_MS 3000
#define AFE_CMD_RESP_AVAIL 0
#define AFE_CMD_RESP_NONE 1
#define AFE_CLK_TOKEN 1024
@@ -513,12 +520,96 @@ struct afe_param_id_cdc_dma_cfg {
u16 active_channels_mask;
} __packed;
+struct afe_param_id_usb_cfg {
+/* Minor version used for tracking USB audio device configuration.
+ * Supported values: AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ */
+ u32 cfg_minor_version;
+/* Sampling rate of the port.
+ * Supported values:
+ * - AFE_PORT_SAMPLE_RATE_8K
+ * - AFE_PORT_SAMPLE_RATE_11025
+ * - AFE_PORT_SAMPLE_RATE_12K
+ * - AFE_PORT_SAMPLE_RATE_16K
+ * - AFE_PORT_SAMPLE_RATE_22050
+ * - AFE_PORT_SAMPLE_RATE_24K
+ * - AFE_PORT_SAMPLE_RATE_32K
+ * - AFE_PORT_SAMPLE_RATE_44P1K
+ * - AFE_PORT_SAMPLE_RATE_48K
+ * - AFE_PORT_SAMPLE_RATE_96K
+ * - AFE_PORT_SAMPLE_RATE_192K
+ */
+ u32 sample_rate;
+/* Bit width of the sample.
+ * Supported values: 16, 24
+ */
+ u16 bit_width;
+/* Number of channels.
+ * Supported values: 1 and 2
+ */
+ u16 num_channels;
+/* Data format supported by the USB. The supported value is
+ * 0 (#AFE_USB_AUDIO_DATA_FORMAT_LINEAR_PCM).
+ */
+ u16 data_format;
+/* this field must be 0 */
+ u16 reserved;
+/* device token of actual end USB audio device */
+ u32 dev_token;
+/* endianness of this interface */
+ u32 endian;
+/* service interval */
+ u32 service_interval;
+} __packed;
+
+/**
+ * struct afe_param_id_usb_audio_dev_params
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @dev_token: device token of actual end USB audio device
+ **/
+struct afe_param_id_usb_audio_dev_params {
+ u32 cfg_minor_version;
+ u32 dev_token;
+} __packed;
+
+/**
+ * struct afe_param_id_usb_audio_dev_lpcm_fmt
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @endian: endianness of this interface
+ **/
+struct afe_param_id_usb_audio_dev_lpcm_fmt {
+ u32 cfg_minor_version;
+ u32 endian;
+} __packed;
+
+#define AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL 0x000102B7
+
+/**
+ * struct afe_param_id_usb_audio_svc_interval
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @svc_interval: service interval
+ **/
+struct afe_param_id_usb_audio_svc_interval {
+ u32 cfg_minor_version;
+ u32 svc_interval;
+} __packed;
+
union afe_port_config {
struct afe_param_id_hdmi_multi_chan_audio_cfg hdmi_multi_ch;
struct afe_param_id_slimbus_cfg slim_cfg;
struct afe_param_id_i2s_cfg i2s_cfg;
struct afe_param_id_tdm_cfg tdm_cfg;
struct afe_param_id_cdc_dma_cfg dma_cfg;
+ struct afe_param_id_usb_cfg usb_cfg;
} __packed;
@@ -833,6 +924,7 @@ static struct afe_port_map port_maps[AFE_PORT_MAX] = {
RX_CODEC_DMA_RX_6, 1, 1},
[RX_CODEC_DMA_RX_7] = { AFE_PORT_ID_RX_CODEC_DMA_RX_7,
RX_CODEC_DMA_RX_7, 1, 1},
+ [USB_RX] = { AFE_PORT_ID_USB_RX, USB_RX, 1, 1},
};
static void q6afe_port_free(struct kref *ref)
@@ -1291,6 +1383,99 @@ void q6afe_tdm_port_prepare(struct q6afe_port *port,
EXPORT_SYMBOL_GPL(q6afe_tdm_port_prepare);
/**
+ * afe_port_send_usb_dev_param() - Send USB dev token
+ *
+ * @port: Instance of afe port
+ * @cardidx: USB SND card index to reference
+ * @pcmidx: USB SND PCM device index to reference
+ *
+ * The USB dev token carries information about which USB SND card instance and
+ * PCM device to execute the offload on. This information is carried through
+ * to the stream enable QMI request, which is handled by the offload class
+ * driver. The information is parsed to determine which USB device to query
+ * the required resources for.
+ */
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx)
+{
+ struct afe_param_id_usb_audio_dev_params usb_dev;
+ int ret;
+
+ memset(&usb_dev, 0, sizeof(usb_dev));
+
+ usb_dev.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ usb_dev.dev_token = (cardidx << 16) | (pcmidx << 8);
+ ret = q6afe_port_set_param_v2(port, &usb_dev,
+ AFE_PARAM_ID_USB_AUDIO_DEV_PARAMS,
+ AFE_MODULE_AUDIO_DEV_INTERFACE,
+ sizeof(usb_dev));
+ if (ret)
+ dev_err(port->afe->dev, "%s: AFE device param cmd failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(afe_port_send_usb_dev_param);
+
+static int afe_port_send_usb_params(struct q6afe_port *port, struct q6afe_usb_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+ struct afe_param_id_usb_audio_dev_lpcm_fmt lpcm_fmt;
+ struct afe_param_id_usb_audio_svc_interval svc_int;
+ int ret;
+
+ if (!pcfg) {
+ dev_err(port->afe->dev, "%s: Error, no configuration data\n", __func__);
+ return -EINVAL;
+ }
+
+ memset(&lpcm_fmt, 0, sizeof(lpcm_fmt));
+ memset(&svc_int, 0, sizeof(svc_int));
+
+ lpcm_fmt.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ lpcm_fmt.endian = pcfg->usb_cfg.endian;
+ ret = q6afe_port_set_param_v2(port, &lpcm_fmt,
+ AFE_PARAM_ID_USB_AUDIO_DEV_LPCM_FMT,
+ AFE_MODULE_AUDIO_DEV_INTERFACE, sizeof(lpcm_fmt));
+ if (ret) {
+ dev_err(port->afe->dev, "%s: AFE device param cmd LPCM_FMT failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ svc_int.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ svc_int.svc_interval = pcfg->usb_cfg.service_interval;
+ ret = q6afe_port_set_param_v2(port, &svc_int,
+ AFE_PARAM_ID_USB_AUDIO_SVC_INTERVAL,
+ AFE_MODULE_AUDIO_DEV_INTERFACE, sizeof(svc_int));
+ if (ret)
+ dev_err(port->afe->dev, "%s: AFE device param cmd svc_interval failed %d\n",
+ __func__, ret);
+
+ return ret;
+}
+
+/**
+ * q6afe_usb_port_prepare() - Prepare usb afe port.
+ *
+ * @port: Instance of afe port
+ * @cfg: USB configuration for the afe port
+ *
+ */
+void q6afe_usb_port_prepare(struct q6afe_port *port,
+ struct q6afe_usb_cfg *cfg)
+{
+ union afe_port_config *pcfg = &port->port_cfg;
+
+ pcfg->usb_cfg.cfg_minor_version = AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG;
+ pcfg->usb_cfg.sample_rate = cfg->sample_rate;
+ pcfg->usb_cfg.num_channels = cfg->num_channels;
+ pcfg->usb_cfg.bit_width = cfg->bit_width;
+
+ afe_port_send_usb_params(port, cfg);
+}
+EXPORT_SYMBOL_GPL(q6afe_usb_port_prepare);
+
+/**
* q6afe_hdmi_port_prepare() - Prepare hdmi afe port.
*
* @port: Instance of afe port
@@ -1612,7 +1797,10 @@ struct q6afe_port *q6afe_port_get_from_id(struct device *dev, int id)
break;
case AFE_PORT_ID_WSA_CODEC_DMA_RX_0 ... AFE_PORT_ID_RX_CODEC_DMA_RX_7:
cfg_type = AFE_PARAM_ID_CODEC_DMA_CONFIG;
- break;
+ break;
+ case AFE_PORT_ID_USB_RX:
+ cfg_type = AFE_PARAM_ID_USB_AUDIO_CONFIG;
+ break;
default:
dev_err(dev, "Invalid port id 0x%x\n", port_id);
return ERR_PTR(-EINVAL);
diff --git a/sound/soc/qcom/qdsp6/q6afe.h b/sound/soc/qcom/qdsp6/q6afe.h
index 65d0676075e1..a29abe4ce436 100644
--- a/sound/soc/qcom/qdsp6/q6afe.h
+++ b/sound/soc/qcom/qdsp6/q6afe.h
@@ -3,7 +3,7 @@
#ifndef __Q6AFE_H__
#define __Q6AFE_H__
-#define AFE_PORT_MAX 129
+#define AFE_PORT_MAX 137
#define MSM_AFE_PORT_TYPE_RX 0
#define MSM_AFE_PORT_TYPE_TX 1
@@ -203,6 +203,36 @@ struct q6afe_cdc_dma_cfg {
u16 active_channels_mask;
};
+/**
+ * struct q6afe_usb_cfg
+ * @cfg_minor_version: Minor version used for tracking USB audio device
+ * configuration.
+ * Supported values:
+ * AFE_API_MINOR_VERSION_USB_AUDIO_CONFIG
+ * @sample_rate: Sampling rate of the port
+ * Supported values:
+ * AFE_PORT_SAMPLE_RATE_8K
+ * AFE_PORT_SAMPLE_RATE_11025
+ * AFE_PORT_SAMPLE_RATE_12K
+ * AFE_PORT_SAMPLE_RATE_16K
+ * AFE_PORT_SAMPLE_RATE_22050
+ * AFE_PORT_SAMPLE_RATE_24K
+ * AFE_PORT_SAMPLE_RATE_32K
+ * AFE_PORT_SAMPLE_RATE_44P1K
+ * AFE_PORT_SAMPLE_RATE_48K
+ * AFE_PORT_SAMPLE_RATE_96K
+ * AFE_PORT_SAMPLE_RATE_192K
+ * @bit_width: Bit width of the sample.
+ * Supported values: 16, 24
+ * @num_channels: Number of channels
+ * Supported values: 1, 2
+ **/
+struct q6afe_usb_cfg {
+ u32 cfg_minor_version;
+ u32 sample_rate;
+ u16 bit_width;
+ u16 num_channels;
+};
struct q6afe_port_config {
struct q6afe_hdmi_cfg hdmi;
@@ -210,6 +240,7 @@ struct q6afe_port_config {
struct q6afe_i2s_cfg i2s_cfg;
struct q6afe_tdm_cfg tdm;
struct q6afe_cdc_dma_cfg dma_cfg;
+ struct q6afe_usb_cfg usb_audio;
};
struct q6afe_port;
@@ -219,6 +250,8 @@ int q6afe_port_start(struct q6afe_port *port);
int q6afe_port_stop(struct q6afe_port *port);
void q6afe_port_put(struct q6afe_port *port);
int q6afe_get_port_id(int index);
+void q6afe_usb_port_prepare(struct q6afe_port *port,
+ struct q6afe_usb_cfg *cfg);
void q6afe_hdmi_port_prepare(struct q6afe_port *port,
struct q6afe_hdmi_cfg *cfg);
void q6afe_slim_port_prepare(struct q6afe_port *port,
@@ -228,6 +261,7 @@ void q6afe_tdm_port_prepare(struct q6afe_port *port, struct q6afe_tdm_cfg *cfg);
void q6afe_cdc_dma_port_prepare(struct q6afe_port *port,
struct q6afe_cdc_dma_cfg *cfg);
+int afe_port_send_usb_dev_param(struct q6afe_port *port, int cardidx, int pcmidx);
int q6afe_port_set_sysclk(struct q6afe_port *port, int clk_id,
int clk_src, int clk_root,
unsigned int freq, int dir);
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
index 9c98a35ad099..a0d90462fd6a 100644
--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -206,7 +206,7 @@ static int q6apm_lpass_dai_prepare(struct snd_pcm_substream *substream, struct s
rc = q6apm_graph_start(dai_data->graph[dai->id]);
if (rc < 0) {
- dev_err(dai->dev, "fail to start APM port %x\n", dai->id);
+ dev_err(dai->dev, "Failed to start APM port %d\n", dai->id);
goto err;
}
dai_data->is_port_started[dai->id] = true;
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
index e758411603be..03838582aead 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-clocks.c
@@ -69,17 +69,17 @@ static unsigned long clk_q6dsp_recalc_rate(struct clk_hw *hw,
return clk->rate;
}
-static long clk_q6dsp_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *parent_rate)
+static int clk_q6dsp_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
- return rate;
+ return 0;
}
static const struct clk_ops clk_q6dsp_ops = {
.prepare = clk_q6dsp_prepare,
.unprepare = clk_q6dsp_unprepare,
.set_rate = clk_q6dsp_set_rate,
- .round_rate = clk_q6dsp_round_rate,
+ .determine_rate = clk_q6dsp_determine_rate,
.recalc_rate = clk_q6dsp_recalc_rate,
};
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
index 4919001de08b..4eed54b071a5 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
@@ -99,6 +99,26 @@
static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
{
.playback = {
+ .stream_name = "USB Playback",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .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,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ },
+ .id = USB_RX,
+ .name = "USB_RX",
+ },
+ {
+ .playback = {
.stream_name = "HDMI Playback",
.rates = SNDRV_PCM_RATE_48000 |
SNDRV_PCM_RATE_96000 |
@@ -624,6 +644,9 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7:
q6dsp_audio_fe_dais[i].ops = cfg->q6dma_ops;
break;
+ case USB_RX:
+ q6dsp_audio_fe_dais[i].ops = cfg->q6usb_ops;
+ break;
default:
break;
}
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
index 7f052c8a1257..d8dde6dd0aca 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.h
@@ -11,6 +11,7 @@ struct q6dsp_audio_port_dai_driver_config {
const struct snd_soc_dai_ops *q6i2s_ops;
const struct snd_soc_dai_ops *q6tdm_ops;
const struct snd_soc_dai_ops *q6dma_ops;
+ const struct snd_soc_dai_ops *q6usb_ops;
};
struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
diff --git a/sound/soc/qcom/qdsp6/q6routing.c b/sound/soc/qcom/qdsp6/q6routing.c
index 90228699ba7d..f49243daa517 100644
--- a/sound/soc/qcom/qdsp6/q6routing.c
+++ b/sound/soc/qcom/qdsp6/q6routing.c
@@ -435,6 +435,7 @@ static struct session_data *get_session_from_id(struct msm_routing_data *data,
return NULL;
}
+
/**
* q6routing_stream_close() - Deregister a stream
*
@@ -515,6 +516,9 @@ static int msm_routing_put_audio_mixer(struct snd_kcontrol *kcontrol,
return 1;
}
+static const struct snd_kcontrol_new usb_rx_mixer_controls[] = {
+ Q6ROUTING_RX_MIXERS(USB_RX) };
+
static const struct snd_kcontrol_new hdmi_mixer_controls[] = {
Q6ROUTING_RX_MIXERS(HDMI_RX) };
@@ -933,6 +937,9 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
SND_SOC_DAPM_MIXER("RX_CODEC_DMA_RX_7 Audio Mixer", SND_SOC_NOPM, 0, 0,
rx_codec_dma_rx_7_mixer_controls,
ARRAY_SIZE(rx_codec_dma_rx_7_mixer_controls)),
+ SND_SOC_DAPM_MIXER("USB_RX Audio Mixer", SND_SOC_NOPM, 0, 0,
+ usb_rx_mixer_controls,
+ ARRAY_SIZE(usb_rx_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia1 Mixer", SND_SOC_NOPM, 0, 0,
mmul1_mixer_controls, ARRAY_SIZE(mmul1_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia2 Mixer", SND_SOC_NOPM, 0, 0,
@@ -949,7 +956,6 @@ static const struct snd_soc_dapm_widget msm_qdsp6_widgets[] = {
mmul7_mixer_controls, ARRAY_SIZE(mmul7_mixer_controls)),
SND_SOC_DAPM_MIXER("MultiMedia8 Mixer", SND_SOC_NOPM, 0, 0,
mmul8_mixer_controls, ARRAY_SIZE(mmul8_mixer_controls)),
-
};
static const struct snd_soc_dapm_route intercon[] = {
@@ -1026,6 +1032,7 @@ static const struct snd_soc_dapm_route intercon[] = {
Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_5 Audio Mixer", "RX_CODEC_DMA_RX_5"),
Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_6 Audio Mixer", "RX_CODEC_DMA_RX_6"),
Q6ROUTING_RX_DAPM_ROUTE("RX_CODEC_DMA_RX_7 Audio Mixer", "RX_CODEC_DMA_RX_7"),
+ Q6ROUTING_RX_DAPM_ROUTE("USB_RX Audio Mixer", "USB_RX"),
Q6ROUTING_TX_DAPM_ROUTE("MultiMedia1 Mixer"),
Q6ROUTING_TX_DAPM_ROUTE("MultiMedia2 Mixer"),
Q6ROUTING_TX_DAPM_ROUTE("MultiMedia3 Mixer"),
diff --git a/sound/soc/qcom/qdsp6/q6usb.c b/sound/soc/qcom/qdsp6/q6usb.c
new file mode 100644
index 000000000000..ebe0c2425927
--- /dev/null
+++ b/sound/soc/qcom/qdsp6/q6usb.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-map-ops.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/iommu.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <sound/asound.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/q6usboffload.h>
+#include <sound/soc.h>
+#include <sound/soc-usb.h>
+
+#include <dt-bindings/sound/qcom,q6afe.h>
+
+#include "q6afe.h"
+#include "q6dsp-lpass-ports.h"
+
+#define Q6_USB_SID_MASK 0xF
+
+struct q6usb_port_data {
+ struct auxiliary_device uauxdev;
+ struct q6afe_usb_cfg usb_cfg;
+ struct snd_soc_usb *usb;
+ struct snd_soc_jack *hs_jack;
+ struct q6usb_offload priv;
+
+ /* Protects against operations between SOC USB and ASoC */
+ struct mutex mutex;
+ struct list_head devices;
+};
+
+static const struct snd_soc_dapm_widget q6usb_dai_widgets[] = {
+ SND_SOC_DAPM_HP("USB_RX_BE", NULL),
+};
+
+static const struct snd_soc_dapm_route q6usb_dapm_routes[] = {
+ {"USB Playback", NULL, "USB_RX_BE"},
+};
+
+static int q6usb_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(dai->dev);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ int direction = substream->stream;
+ struct q6afe_port *q6usb_afe;
+ struct snd_soc_usb_device *sdev;
+ int ret = -EINVAL;
+
+ mutex_lock(&data->mutex);
+
+ /* No active chip index */
+ if (list_empty(&data->devices))
+ goto out;
+
+ sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list);
+
+ ret = snd_soc_usb_find_supported_format(sdev->chip_idx, params, direction);
+ if (ret < 0)
+ goto out;
+
+ q6usb_afe = q6afe_port_get_from_id(cpu_dai->dev, USB_RX);
+ if (IS_ERR(q6usb_afe)) {
+ ret = PTR_ERR(q6usb_afe);
+ goto out;
+ }
+
+ /* Notify audio DSP about the devices being offloaded */
+ ret = afe_port_send_usb_dev_param(q6usb_afe, sdev->card_idx,
+ sdev->ppcm_idx[sdev->num_playback - 1]);
+
+out:
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops q6usb_ops = {
+ .hw_params = q6usb_hw_params,
+};
+
+static struct snd_soc_dai_driver q6usb_be_dais[] = {
+ {
+ .playback = {
+ .stream_name = "USB BE RX",
+ .rates = SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
+ SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 |
+ SNDRV_PCM_RATE_192000,
+ .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,
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_max = 192000,
+ .rate_min = 8000,
+ },
+ .id = USB_RX,
+ .name = "USB_RX_BE",
+ .ops = &q6usb_ops,
+ },
+};
+
+static int q6usb_audio_ports_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ int id = args->args[0];
+ int ret = -EINVAL;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(q6usb_be_dais); i++) {
+ if (q6usb_be_dais[i].id == id) {
+ *dai_name = q6usb_be_dais[i].name;
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int q6usb_get_pcm_id_from_widget(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_pcm_runtime *rtd;
+ struct snd_soc_dai *dai;
+
+ for_each_card_rtds(w->dapm->card, rtd) {
+ dai = snd_soc_rtd_to_cpu(rtd, 0);
+ /*
+ * Only look for playback widget. RTD number carries the assigned
+ * PCM index.
+ */
+ if (dai->stream[0].widget == w)
+ return rtd->id;
+ }
+
+ return -1;
+}
+
+static int q6usb_usb_mixer_enabled(struct snd_soc_dapm_widget *w)
+{
+ struct snd_soc_dapm_path *p;
+
+ /* Checks to ensure USB path is enabled/connected */
+ snd_soc_dapm_widget_for_each_sink_path(w, p)
+ if (!strcmp(p->sink->name, "USB Mixer") && p->connect)
+ return 1;
+
+ return 0;
+}
+
+static int q6usb_get_pcm_id(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_dapm_path *p;
+ int pidx;
+
+ /*
+ * Traverse widgets to find corresponding FE widget. The DAI links are
+ * built like the following:
+ * MultiMedia* <-> MM_DL* <-> USB Mixer*
+ */
+ for_each_card_widgets(component->card, w) {
+ if (!strncmp(w->name, "MultiMedia", 10)) {
+ /*
+ * Look up all paths associated with the FE widget to see if
+ * the USB BE is enabled. The sink widget is responsible to
+ * link with the USB mixers.
+ */
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (q6usb_usb_mixer_enabled(p->sink)) {
+ pidx = q6usb_get_pcm_id_from_widget(w);
+ return pidx;
+ }
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int q6usb_update_offload_route(struct snd_soc_component *component, int card,
+ int pcm, int direction, enum snd_soc_usb_kctl path,
+ long *route)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+ struct snd_soc_usb_device *sdev;
+ int ret = 0;
+ int idx = -1;
+
+ mutex_lock(&data->mutex);
+
+ if (list_empty(&data->devices) ||
+ direction == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = -ENODEV;
+ goto out;
+ }
+
+ sdev = list_last_entry(&data->devices, struct snd_soc_usb_device, list);
+
+ /*
+ * Will always look for last PCM device discovered/probed as the
+ * active offload index.
+ */
+ if (card == sdev->card_idx &&
+ pcm == sdev->ppcm_idx[sdev->num_playback - 1]) {
+ idx = path == SND_SOC_USB_KCTL_CARD_ROUTE ?
+ component->card->snd_card->number :
+ q6usb_get_pcm_id(component);
+ }
+
+out:
+ route[0] = idx;
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int q6usb_alsa_connection_cb(struct snd_soc_usb *usb,
+ struct snd_soc_usb_device *sdev, bool connected)
+{
+ struct q6usb_port_data *data;
+
+ if (!usb->component)
+ return -ENODEV;
+
+ data = dev_get_drvdata(usb->component->dev);
+
+ mutex_lock(&data->mutex);
+ if (connected) {
+ if (data->hs_jack)
+ snd_jack_report(data->hs_jack->jack, SND_JACK_USB);
+
+ /* Selects the latest USB headset plugged in for offloading */
+ list_add_tail(&sdev->list, &data->devices);
+ } else {
+ list_del(&sdev->list);
+
+ if (data->hs_jack)
+ snd_jack_report(data->hs_jack->jack, 0);
+ }
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static void q6usb_component_disable_jack(struct q6usb_port_data *data)
+{
+ /* Offload jack has already been disabled */
+ if (!data->hs_jack)
+ return;
+
+ snd_jack_report(data->hs_jack->jack, 0);
+ data->hs_jack = NULL;
+}
+
+static void q6usb_component_enable_jack(struct q6usb_port_data *data,
+ struct snd_soc_jack *jack)
+{
+ snd_jack_report(jack->jack, !list_empty(&data->devices) ? SND_JACK_USB : 0);
+ data->hs_jack = jack;
+}
+
+static int q6usb_component_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *priv)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+
+ mutex_lock(&data->mutex);
+ if (jack)
+ q6usb_component_enable_jack(data, jack);
+ else
+ q6usb_component_disable_jack(data);
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static void q6usb_dai_aux_release(struct device *dev) {}
+
+static int q6usb_dai_add_aux_device(struct q6usb_port_data *data,
+ struct auxiliary_device *auxdev)
+{
+ int ret;
+
+ auxdev->dev.parent = data->priv.dev;
+ auxdev->dev.release = q6usb_dai_aux_release;
+ auxdev->name = "qc-usb-audio-offload";
+
+ ret = auxiliary_device_init(auxdev);
+ if (ret)
+ return ret;
+
+ ret = auxiliary_device_add(auxdev);
+ if (ret)
+ auxiliary_device_uninit(auxdev);
+
+ return ret;
+}
+
+static int q6usb_component_probe(struct snd_soc_component *component)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+ struct snd_soc_usb *usb;
+ int ret;
+
+ /* Add the QC USB SND aux device */
+ ret = q6usb_dai_add_aux_device(data, &data->uauxdev);
+ if (ret < 0)
+ return ret;
+
+ usb = snd_soc_usb_allocate_port(component, &data->priv);
+ if (IS_ERR(usb))
+ return -ENOMEM;
+
+ usb->connection_status_cb = q6usb_alsa_connection_cb;
+ usb->update_offload_route_info = q6usb_update_offload_route;
+
+ snd_soc_usb_add_port(usb);
+ data->usb = usb;
+
+ return 0;
+}
+
+static void q6usb_component_remove(struct snd_soc_component *component)
+{
+ struct q6usb_port_data *data = dev_get_drvdata(component->dev);
+
+ snd_soc_usb_remove_port(data->usb);
+ auxiliary_device_delete(&data->uauxdev);
+ auxiliary_device_uninit(&data->uauxdev);
+ snd_soc_usb_free_port(data->usb);
+}
+
+static const struct snd_soc_component_driver q6usb_dai_component = {
+ .probe = q6usb_component_probe,
+ .set_jack = q6usb_component_set_jack,
+ .remove = q6usb_component_remove,
+ .name = "q6usb-dai-component",
+ .dapm_widgets = q6usb_dai_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(q6usb_dai_widgets),
+ .dapm_routes = q6usb_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(q6usb_dapm_routes),
+ .of_xlate_dai_name = q6usb_audio_ports_of_xlate_dai_name,
+};
+
+static int q6usb_dai_dev_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct q6usb_port_data *data;
+ struct device *dev = &pdev->dev;
+ struct of_phandle_args args;
+ int ret;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = of_property_read_u16(node, "qcom,usb-audio-intr-idx",
+ &data->priv.intr_num);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to read intr idx.\n");
+ return ret;
+ }
+
+ ret = of_parse_phandle_with_fixed_args(node, "iommus", 1, 0, &args);
+ if (!ret)
+ data->priv.sid = args.args[0] & Q6_USB_SID_MASK;
+
+ ret = devm_mutex_init(dev, &data->mutex);
+ if (ret < 0)
+ return ret;
+
+ data->priv.domain = iommu_get_domain_for_dev(&pdev->dev);
+
+ data->priv.dev = dev;
+ INIT_LIST_HEAD(&data->devices);
+ dev_set_drvdata(dev, data);
+
+ return devm_snd_soc_register_component(dev, &q6usb_dai_component,
+ q6usb_be_dais, ARRAY_SIZE(q6usb_be_dais));
+}
+
+static const struct of_device_id q6usb_dai_device_id[] = {
+ { .compatible = "qcom,q6usb" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, q6usb_dai_device_id);
+
+static struct platform_driver q6usb_dai_platform_driver = {
+ .driver = {
+ .name = "q6usb-dai",
+ .of_match_table = q6usb_dai_device_id,
+ },
+ .probe = q6usb_dai_dev_probe,
+ /*
+ * Remove not required as resources are cleaned up as part of
+ * component removal. Others are device managed resources.
+ */
+};
+module_platform_driver(q6usb_dai_platform_driver);
+
+MODULE_DESCRIPTION("Q6 USB backend dai driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 311377317176..73f9f82c4e25 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -186,6 +186,9 @@ static int sc8280xp_platform_probe(struct platform_device *pdev)
static const struct of_device_id snd_sc8280xp_dt_match[] = {
{.compatible = "qcom,qcm6490-idp-sndcard", "qcm6490"},
{.compatible = "qcom,qcs6490-rb3gen2-sndcard", "qcs6490"},
+ {.compatible = "qcom,qcs8275-sndcard", "qcs8275"},
+ {.compatible = "qcom,qcs9075-sndcard", "qcs9075"},
+ {.compatible = "qcom,qcs9100-sndcard", "qcs9100"},
{.compatible = "qcom,sc8280xp-sndcard", "sc8280xp"},
{.compatible = "qcom,sm8450-sndcard", "sm8450"},
{.compatible = "qcom,sm8550-sndcard", "sm8550"},
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index fcc7df75346f..a233b80049ee 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -91,6 +91,10 @@ static int sdm845_slim_snd_hw_params(struct snd_pcm_substream *substream,
else
ret = snd_soc_dai_set_channel_map(cpu_dai, tx_ch_cnt,
tx_ch, 0, NULL);
+ if (ret != 0 && ret != -ENOTSUPP) {
+ dev_err(rtd->dev, "failed to set cpu chan map, err:%d\n", ret);
+ return ret;
+ }
}
return 0;
diff --git a/sound/soc/qcom/sm8250.c b/sound/soc/qcom/sm8250.c
index 9039107972e2..f5b75a06e5bd 100644
--- a/sound/soc/qcom/sm8250.c
+++ b/sound/soc/qcom/sm8250.c
@@ -13,9 +13,9 @@
#include <linux/input-event-codes.h>
#include "qdsp6/q6afe.h"
#include "common.h"
+#include "usb_offload_utils.h"
#include "sdw.h"
-#define DRIVER_NAME "sm8250"
#define MI2S_BCLK_RATE 1536000
struct sm8250_snd_data {
@@ -23,14 +23,37 @@ struct sm8250_snd_data {
struct snd_soc_card *card;
struct sdw_stream_runtime *sruntime[AFE_PORT_MAX];
struct snd_soc_jack jack;
+ struct snd_soc_jack usb_offload_jack;
+ bool usb_offload_jack_setup;
+ struct snd_soc_jack dp_jack;
bool jack_setup;
};
static int sm8250_snd_init(struct snd_soc_pcm_runtime *rtd)
{
struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ switch (cpu_dai->id) {
+ case DISPLAY_PORT_RX:
+ return qcom_snd_dp_jack_setup(rtd, &data->dp_jack, 0);
+ case USB_RX:
+ return qcom_snd_usb_offload_jack_setup(rtd, &data->usb_offload_jack,
+ &data->usb_offload_jack_setup);
+ default:
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+ }
+}
+
+static void sm8250_snd_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sm8250_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+
+ if (cpu_dai->id == USB_RX)
+ qcom_snd_usb_offload_jack_remove(rtd,
+ &data->usb_offload_jack_setup);
- return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
}
static int sm8250_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -148,6 +171,7 @@ static void sm8250_add_be_ops(struct snd_soc_card *card)
for_each_card_prelinks(card, i, link) {
if (link->no_pcm == 1) {
link->init = sm8250_snd_init;
+ link->exit = sm8250_snd_exit;
link->be_hw_params_fixup = sm8250_be_hw_params_fixup;
link->ops = &sm8250_be_ops;
}
@@ -178,15 +202,17 @@ static int sm8250_platform_probe(struct platform_device *pdev)
if (ret)
return ret;
- card->driver_name = DRIVER_NAME;
+ card->driver_name = of_device_get_match_data(dev);
sm8250_add_be_ops(card);
return devm_snd_soc_register_card(dev, card);
}
static const struct of_device_id snd_sm8250_dt_match[] = {
- {.compatible = "qcom,sm8250-sndcard"},
- {.compatible = "qcom,qrb4210-rb2-sndcard"},
- {.compatible = "qcom,qrb5165-rb5-sndcard"},
+ { .compatible = "fairphone,fp4-sndcard", .data = "sm7225" },
+ { .compatible = "fairphone,fp5-sndcard", .data = "qcm6490" },
+ { .compatible = "qcom,qrb4210-rb2-sndcard", .data = "sm4250" },
+ { .compatible = "qcom,qrb5165-rb5-sndcard", .data = "sm8250" },
+ { .compatible = "qcom,sm8250-sndcard", .data = "sm8250" },
{}
};
diff --git a/sound/soc/qcom/usb_offload_utils.c b/sound/soc/qcom/usb_offload_utils.c
new file mode 100644
index 000000000000..0a24b278fcdf
--- /dev/null
+++ b/sound/soc/qcom/usb_offload_utils.c
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <dt-bindings/sound/qcom,q6afe.h>
+#include <linux/module.h>
+#include <sound/jack.h>
+#include <sound/soc-usb.h>
+
+#include "usb_offload_utils.h"
+
+int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret = 0;
+
+ if (cpu_dai->id != USB_RX)
+ return -EINVAL;
+
+ if (!*jack_setup) {
+ ret = snd_soc_usb_setup_offload_jack(codec_dai->component, jack);
+ if (ret)
+ return ret;
+ }
+
+ *jack_setup = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_usb_offload_jack_setup);
+
+int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
+ bool *jack_setup)
+{
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
+ int ret = 0;
+
+ if (cpu_dai->id != USB_RX)
+ return -EINVAL;
+
+ if (*jack_setup) {
+ ret = snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+ if (ret)
+ return ret;
+ }
+
+ *jack_setup = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_usb_offload_jack_remove);
+MODULE_DESCRIPTION("ASoC Q6 USB offload controls");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/qcom/usb_offload_utils.h b/sound/soc/qcom/usb_offload_utils.h
new file mode 100644
index 000000000000..c3f411f565b0
--- /dev/null
+++ b/sound/soc/qcom/usb_offload_utils.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#ifndef __QCOM_SND_USB_OFFLOAD_UTILS_H__
+#define __QCOM_SND_USB_OFFLOAD_UTILS_H__
+
+#include <sound/soc.h>
+
+#if IS_ENABLED(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS)
+int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup);
+
+int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
+ bool *jack_setup);
+#else
+static inline int qcom_snd_usb_offload_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack,
+ bool *jack_setup)
+{
+ return -ENODEV;
+}
+
+static inline int qcom_snd_usb_offload_jack_remove(struct snd_soc_pcm_runtime *rtd,
+ bool *jack_setup)
+{
+ return -ENODEV;
+}
+#endif /* IS_ENABLED(CONFIG_SND_SOC_QCOM_OFFLOAD_UTILS) */
+#endif /* __QCOM_SND_USB_OFFLOAD_UTILS_H__ */
diff --git a/sound/soc/renesas/Kconfig b/sound/soc/renesas/Kconfig
index cb01fb36355f..11c2027c88a7 100644
--- a/sound/soc/renesas/Kconfig
+++ b/sound/soc/renesas/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-menu "SoC Audio support for Renesas SoCs"
+menu "Renesas"
depends on SUPERH || ARCH_RENESAS || COMPILE_TEST
config SND_SOC_PCM_SH7760
@@ -46,6 +46,13 @@ config SND_SOC_RCAR
help
This option enables R-Car SRU/SCU/SSIU/SSI sound support
+config SND_SOC_MSIOF
+ tristate "R-Car series MSIOF support"
+ depends on OF
+ select SND_DMAENGINE_PCM
+ help
+ This option enables R-Car MSIOF sound support
+
config SND_SOC_RZ
tristate "RZ/G2L series SSIF-2 support"
depends on ARCH_RZG2L || COMPILE_TEST
diff --git a/sound/soc/renesas/rcar/Makefile b/sound/soc/renesas/rcar/Makefile
index 45eb875a912a..3a2c875595bd 100644
--- a/sound/soc/renesas/rcar/Makefile
+++ b/sound/soc/renesas/rcar/Makefile
@@ -1,3 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
snd-soc-rcar-y := core.o gen.o dma.o adg.o ssi.o ssiu.o src.o ctu.o mix.o dvc.o cmd.o debugfs.o
obj-$(CONFIG_SND_SOC_RCAR) += snd-soc-rcar.o
+
+snd-soc-msiof-y := msiof.o
+obj-$(CONFIG_SND_SOC_MSIOF) += snd-soc-msiof.o
diff --git a/sound/soc/renesas/rcar/adg.c b/sound/soc/renesas/rcar/adg.c
index 191f212d338c..8641b73d1f77 100644
--- a/sound/soc/renesas/rcar/adg.c
+++ b/sound/soc/renesas/rcar/adg.c
@@ -19,6 +19,7 @@
#define CLKOUT3 3
#define CLKOUTMAX 4
+#define BRGCKR_31 (1 << 31)
#define BRRx_MASK(x) (0x3FF & x)
static struct rsnd_mod_ops adg_ops = {
@@ -30,6 +31,7 @@ static struct rsnd_mod_ops adg_ops = {
#define ADG_HZ_SIZE 2
struct rsnd_adg {
+ struct clk *adg;
struct clk *clkin[CLKINMAX];
struct clk *clkout[CLKOUTMAX];
struct clk *null_clk;
@@ -361,10 +363,13 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *ssi_mod, unsigned int rate)
rsnd_adg_set_ssi_clk(ssi_mod, data);
+ ckr = adg->ckr & ~BRGCKR_31;
if (0 == (rate % 8000))
- ckr = 0x80000000; /* BRGB output = 48kHz */
-
- rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr | ckr);
+ ckr |= BRGCKR_31; /* use BRGB output = 48kHz */
+ if (ckr != adg->ckr) {
+ rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr);
+ adg->ckr = ckr;
+ }
dev_dbg(dev, "CLKOUT is based on BRG%c (= %dHz)\n",
(ckr) ? 'B' : 'A',
@@ -382,6 +387,10 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
int ret = 0, i;
if (enable) {
+ ret = clk_prepare_enable(adg->adg);
+ if (ret < 0)
+ return ret;
+
rsnd_mod_bset(adg_mod, BRGCKR, 0x80770000, adg->ckr);
rsnd_mod_write(adg_mod, BRRA, adg->brga);
rsnd_mod_write(adg_mod, BRRB, adg->brgb);
@@ -415,6 +424,10 @@ int rsnd_adg_clk_control(struct rsnd_priv *priv, int enable)
if (ret < 0)
rsnd_adg_clk_disable(priv);
+ /* disable adg */
+ if (!enable)
+ clk_disable_unprepare(adg->adg);
+
return ret;
}
@@ -471,6 +484,16 @@ static int rsnd_adg_get_clkin(struct rsnd_priv *priv)
clkin_size = ARRAY_SIZE(clkin_name_gen4);
}
+ /*
+ * get adg
+ * No "adg" is not error
+ */
+ clk = devm_clk_get(dev, "adg");
+ if (IS_ERR_OR_NULL(clk))
+ clk = rsnd_adg_null_clk_get(priv);
+ adg->adg = clk;
+
+ /* get clkin */
for (i = 0; i < clkin_size; i++) {
clk = devm_clk_get(dev, clkin_name[i]);
@@ -683,6 +706,9 @@ static int rsnd_adg_get_clkout(struct rsnd_priv *priv)
}
rsnd_adg_get_clkout_end:
+ if (0 == (req_rate[0] % 8000))
+ ckr |= BRGCKR_31; /* use BRGB output = 48kHz */
+
adg->ckr = ckr;
adg->brga = brga;
adg->brgb = brgb;
diff --git a/sound/soc/renesas/rcar/core.c b/sound/soc/renesas/rcar/core.c
index 30afc942d381..37d954495ea5 100644
--- a/sound/soc/renesas/rcar/core.c
+++ b/sound/soc/renesas/rcar/core.c
@@ -597,7 +597,7 @@ int rsnd_dai_connect(struct rsnd_mod *mod,
dev_dbg(dev, "%s is connected to io (%s)\n",
rsnd_mod_name(mod),
- rsnd_io_is_play(io) ? "Playback" : "Capture");
+ snd_pcm_direction_name(io->substream->stream));
return 0;
}
@@ -1075,7 +1075,6 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv,
{
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *ssiu_np = rsnd_ssiu_of_node(priv);
- struct device_node *np;
int is_play = rsnd_io_is_play(io);
int i;
@@ -1094,7 +1093,7 @@ static void rsnd_parse_tdm_split_mode(struct rsnd_priv *priv,
if (!node)
break;
- for_each_child_of_node(ssiu_np, np) {
+ for_each_child_of_node_scoped(ssiu_np, np) {
if (np == node) {
rsnd_flags_set(io, RSND_STREAM_TDM_SPLIT);
dev_dbg(dev, "%s is part of TDM Split\n", io->name);
@@ -1154,21 +1153,18 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai, char *name,
{
struct rsnd_priv *priv = rsnd_rdai_to_priv(rdai);
struct device *dev = rsnd_priv_to_dev(priv);
- struct device_node *np;
int i;
if (!node)
return;
i = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
struct rsnd_mod *mod;
i = rsnd_node_fixed_index(dev, np, name, i);
- if (i < 0) {
- of_node_put(np);
+ if (i < 0)
break;
- }
mod = mod_get(priv, i);
@@ -1217,16 +1213,13 @@ int rsnd_node_fixed_index(struct device *dev, struct device_node *node, char *na
int rsnd_node_count(struct rsnd_priv *priv, struct device_node *node, char *name)
{
struct device *dev = rsnd_priv_to_dev(priv);
- struct device_node *np;
int i;
i = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
i = rsnd_node_fixed_index(dev, np, name, i);
- if (i < 0) {
- of_node_put(np);
+ if (i < 0)
return 0;
- }
i++;
}
@@ -1250,7 +1243,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph)
{
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
- struct device_node *ports, *node;
+ struct device_node *node;
int nr = 0;
int i = 0;
@@ -1270,7 +1263,7 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph)
of_node_put(node);
- for_each_child_of_node(np, node) {
+ for_each_child_of_node_scoped(np, node) {
if (!of_node_name_eq(node, RSND_NODE_DAI))
continue;
@@ -1279,7 +1272,6 @@ static int rsnd_dai_of_node(struct rsnd_priv *priv, int *is_graph)
i++;
if (i >= RSND_MAX_COMPONENT) {
dev_info(dev, "reach to max component\n");
- of_node_put(node);
break;
}
}
@@ -1290,7 +1282,7 @@ audio_graph:
/*
* Audio-Graph-Card
*/
- for_each_child_of_node(np, ports) {
+ for_each_child_of_node_scoped(np, ports) {
node = rsnd_pick_endpoint_node_for_ports(ports, np);
if (!node)
continue;
@@ -1299,7 +1291,6 @@ audio_graph:
i++;
if (i >= RSND_MAX_COMPONENT) {
dev_info(dev, "reach to max component\n");
- of_node_put(ports);
break;
}
}
@@ -1482,8 +1473,13 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
int dai_i;
nr = rsnd_dai_of_node(priv, &is_graph);
+
+ /*
+ * There is a case that it is used only for ADG (Sound Clock).
+ * No DAI is not error
+ */
if (!nr)
- return -EINVAL;
+ return 0;
rdrv = devm_kcalloc(dev, nr, sizeof(*rdrv), GFP_KERNEL);
rdai = devm_kcalloc(dev, nr, sizeof(*rdai), GFP_KERNEL);
@@ -1500,10 +1496,9 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
dai_i = 0;
if (is_graph) {
struct device_node *dai_np_port;
- struct device_node *ports;
struct device_node *dai_np;
- for_each_child_of_node(np, ports) {
+ for_each_child_of_node_scoped(np, ports) {
dai_np_port = rsnd_pick_endpoint_node_for_ports(ports, np);
if (!dai_np_port)
continue;
@@ -1520,14 +1515,11 @@ static int rsnd_dai_probe(struct rsnd_priv *priv)
}
}
} else {
- struct device_node *node;
- struct device_node *dai_np;
-
- for_each_child_of_node(np, node) {
+ for_each_child_of_node_scoped(np, node) {
if (!of_node_name_eq(node, RSND_NODE_DAI))
continue;
- for_each_child_of_node(node, dai_np) {
+ for_each_child_of_node_scoped(node, dai_np) {
__rsnd_dai_probe(priv, dai_np, np, dai_i, dai_i);
if (!rsnd_is_gen1(priv) && !rsnd_is_gen2(priv)) {
rdai = rsnd_rdai_get(priv, dai_i);
diff --git a/sound/soc/renesas/rcar/ctu.c b/sound/soc/renesas/rcar/ctu.c
index a26ec7b780cd..bd4c61f9fb3c 100644
--- a/sound/soc/renesas/rcar/ctu.c
+++ b/sound/soc/renesas/rcar/ctu.c
@@ -316,7 +316,6 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
int rsnd_ctu_probe(struct rsnd_priv *priv)
{
struct device_node *node;
- struct device_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_ctu *ctu;
struct clk *clk;
@@ -344,7 +343,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv)
i = 0;
ret = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
ctu = rsnd_ctu_get(priv, i);
/*
@@ -357,16 +356,13 @@ int rsnd_ctu_probe(struct rsnd_priv *priv)
clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
- of_node_put(np);
goto rsnd_ctu_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
clk, RSND_MOD_CTU, i);
- if (ret) {
- of_node_put(np);
+ if (ret)
goto rsnd_ctu_probe_done;
- }
i++;
}
diff --git a/sound/soc/renesas/rcar/dma.c b/sound/soc/renesas/rcar/dma.c
index 2342bbb6fe92..2035ce06fe4c 100644
--- a/sound/soc/renesas/rcar/dma.c
+++ b/sound/soc/renesas/rcar/dma.c
@@ -194,14 +194,12 @@ struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node, char *nam
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) {
+ for_each_child_of_node_scoped(of_node, np) {
i = rsnd_node_fixed_index(dev, np, name, i);
if (i < 0) {
chan = NULL;
- of_node_put(np);
break;
}
diff --git a/sound/soc/renesas/rcar/dvc.c b/sound/soc/renesas/rcar/dvc.c
index da91dd301aab..988cbddbc611 100644
--- a/sound/soc/renesas/rcar/dvc.c
+++ b/sound/soc/renesas/rcar/dvc.c
@@ -324,7 +324,6 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
int rsnd_dvc_probe(struct rsnd_priv *priv)
{
struct device_node *node;
- struct device_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_dvc *dvc;
struct clk *clk;
@@ -352,7 +351,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
i = 0;
ret = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
dvc = rsnd_dvc_get(priv, i);
snprintf(name, RSND_DVC_NAME_SIZE, "%s.%d",
@@ -361,16 +360,13 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
- of_node_put(np);
goto rsnd_dvc_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
clk, RSND_MOD_DVC, i);
- if (ret) {
- of_node_put(np);
+ if (ret)
goto rsnd_dvc_probe_done;
- }
i++;
}
diff --git a/sound/soc/renesas/rcar/mix.c b/sound/soc/renesas/rcar/mix.c
index 024d91cc8748..aea74e703305 100644
--- a/sound/soc/renesas/rcar/mix.c
+++ b/sound/soc/renesas/rcar/mix.c
@@ -288,7 +288,6 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
int rsnd_mix_probe(struct rsnd_priv *priv)
{
struct device_node *node;
- struct device_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mix *mix;
struct clk *clk;
@@ -316,7 +315,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv)
i = 0;
ret = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
mix = rsnd_mix_get(priv, i);
snprintf(name, MIX_NAME_SIZE, "%s.%d",
@@ -325,16 +324,13 @@ int rsnd_mix_probe(struct rsnd_priv *priv)
clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
- of_node_put(np);
goto rsnd_mix_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
clk, RSND_MOD_MIX, i);
- if (ret) {
- of_node_put(np);
+ if (ret)
goto rsnd_mix_probe_done;
- }
i++;
}
diff --git a/sound/soc/renesas/rcar/msiof.c b/sound/soc/renesas/rcar/msiof.c
new file mode 100644
index 000000000000..36d31ab8ac6a
--- /dev/null
+++ b/sound/soc/renesas/rcar/msiof.c
@@ -0,0 +1,528 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Renesas R-Car MSIOF (Clock-Synchronized Serial Interface with FIFO) I2S driver
+//
+// Copyright (C) 2025 Renesas Solutions Corp.
+// Author: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+//
+
+/*
+ * [NOTE]
+ *
+ * This driver doesn't support Clock/Frame Provider Mode
+ *
+ * Basically MSIOF is created for SPI, but we can use it as I2S (Sound), etc. Because of it, when
+ * we use it as I2S (Sound) with Provider Mode, we need to send dummy TX data even though it was
+ * used for RX. Because SPI HW needs TX Clock/Frame output for RX purpose.
+ * But it makes driver code complex in I2S (Sound).
+ *
+ * And when we use it as I2S (Sound) as Provider Mode, the clock source is [MSO clock] (= 133.33MHz)
+ * SoC internal clock. It is not for 48kHz/44.1kHz base clock. Thus the output/input will not be
+ * accurate sound.
+ *
+ * Because of these reasons, this driver doesn't support Clock/Frame Provider Mode. Use it as
+ * Clock/Frame Consumer Mode.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/spi/sh_msiof.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/soc.h>
+
+/* SISTR */
+#define SISTR_ERR_TX (SISTR_TFSERR | SISTR_TFOVF | SISTR_TFUDF)
+#define SISTR_ERR_RX (SISTR_RFSERR | SISTR_RFOVF | SISTR_RFUDF)
+#define SISTR_ERR (SISTR_ERR_TX | SISTR_ERR_RX)
+
+/*
+ * The data on memory in 24bit case is located at <right> side
+ * [ xxxxxx]
+ * [ xxxxxx]
+ * [ xxxxxx]
+ *
+ * HW assuming signal in 24bit case is located at <left> side
+ * ---+ +---------+
+ * +---------+ +---------+...
+ * [xxxxxx ][xxxxxx ][xxxxxx ]
+ *
+ * When we use 24bit data, it will be transferred via 32bit width via DMA,
+ * and MSIOF/DMA doesn't support data shift, we can't use 24bit data correctly.
+ * There is no such issue on 16/32bit data case.
+ */
+#define MSIOF_RATES SNDRV_PCM_RATE_8000_192000
+#define MSIOF_FMTS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+struct msiof_priv {
+ struct device *dev;
+ struct snd_pcm_substream *substream[SNDRV_PCM_STREAM_LAST + 1];
+ spinlock_t lock;
+ void __iomem *base;
+ resource_size_t phy_addr;
+
+ /* for error */
+ int err_syc[SNDRV_PCM_STREAM_LAST + 1];
+ int err_ovf[SNDRV_PCM_STREAM_LAST + 1];
+ int err_udf[SNDRV_PCM_STREAM_LAST + 1];
+
+ /* bit field */
+ u32 flags;
+#define MSIOF_FLAGS_NEED_DELAY (1 << 0)
+};
+#define msiof_flag_has(priv, flag) (priv->flags & flag)
+#define msiof_flag_set(priv, flag) (priv->flags |= flag)
+
+#define msiof_is_play(substream) ((substream)->stream == SNDRV_PCM_STREAM_PLAYBACK)
+#define msiof_read(priv, reg) ioread32((priv)->base + reg)
+#define msiof_write(priv, reg, val) iowrite32(val, (priv)->base + reg)
+#define msiof_status_clear(priv) msiof_write(priv, SISTR, SISTR_ERR)
+
+static void msiof_update(struct msiof_priv *priv, u32 reg, u32 mask, u32 val)
+{
+ u32 old = msiof_read(priv, reg);
+ u32 new = (old & ~mask) | (val & mask);
+
+ if (old != new)
+ msiof_write(priv, reg, new);
+}
+
+static void msiof_update_and_wait(struct msiof_priv *priv, u32 reg, u32 mask, u32 val, u32 expect)
+{
+ u32 data;
+ int ret;
+
+ msiof_update(priv, reg, mask, val);
+
+ ret = readl_poll_timeout_atomic(priv->base + reg, data,
+ (data & mask) == expect, 1, 128);
+ if (ret)
+ dev_warn(priv->dev, "write timeout [0x%02x] 0x%08x / 0x%08x\n",
+ reg, data, expect);
+}
+
+static int msiof_hw_start(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct msiof_priv *priv = snd_soc_component_get_drvdata(component);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int is_play = msiof_is_play(substream);
+ int width = snd_pcm_format_width(runtime->format);
+ u32 val;
+
+ /*
+ * see
+ * [NOTE] on top of this driver
+ */
+ /*
+ * see
+ * Datasheet 109.3.6 [Transmit and Receive Procedures]
+ *
+ * TX: Fig 109.14 - Fig 109.23
+ * RX: Fig 109.15
+ */
+
+ /* reset errors */
+ priv->err_syc[substream->stream] =
+ priv->err_ovf[substream->stream] =
+ priv->err_udf[substream->stream] = 0;
+
+ /* SITMDRx */
+ if (is_play) {
+ val = SITMDR1_PCON |
+ FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) |
+ SIMDR1_SYNCAC | SIMDR1_XXSTP;
+ if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY))
+ val |= FIELD_PREP(SIMDR1_DTDL, 1);
+
+ msiof_write(priv, SITMDR1, val);
+
+ val = FIELD_PREP(SIMDR2_BITLEN1, width - 1);
+ msiof_write(priv, SITMDR2, val | FIELD_PREP(SIMDR2_GRP, 1));
+ msiof_write(priv, SITMDR3, val);
+
+ }
+ /* SIRMDRx */
+ else {
+ val = FIELD_PREP(SIMDR1_SYNCMD, SIMDR1_SYNCMD_LR) |
+ SIMDR1_SYNCAC;
+ if (msiof_flag_has(priv, MSIOF_FLAGS_NEED_DELAY))
+ val |= FIELD_PREP(SIMDR1_DTDL, 1);
+
+ msiof_write(priv, SIRMDR1, val);
+
+ val = FIELD_PREP(SIMDR2_BITLEN1, width - 1);
+ msiof_write(priv, SIRMDR2, val | FIELD_PREP(SIMDR2_GRP, 1));
+ msiof_write(priv, SIRMDR3, val);
+ }
+
+ /* SIIER */
+ if (is_play)
+ val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX;
+ else
+ val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX;
+ msiof_update(priv, SIIER, val, val);
+
+ /* SICTR */
+ if (is_play)
+ val = SICTR_TXE | SICTR_TEDG;
+ else
+ val = SICTR_RXE | SICTR_REDG;
+ msiof_update_and_wait(priv, SICTR, val, val, val);
+
+ msiof_status_clear(priv);
+
+ /* Start DMAC */
+ snd_dmaengine_pcm_trigger(substream, cmd);
+
+ return 0;
+}
+
+static int msiof_hw_stop(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct msiof_priv *priv = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ int is_play = msiof_is_play(substream);
+ u32 val;
+
+ /* SIIER */
+ if (is_play)
+ val = SIIER_TDREQE | SIIER_TDMAE | SISTR_ERR_TX;
+ else
+ val = SIIER_RDREQE | SIIER_RDMAE | SISTR_ERR_RX;
+ msiof_update(priv, SIIER, val, 0);
+
+ /* Stop DMAC */
+ snd_dmaengine_pcm_trigger(substream, cmd);
+
+ /* SICTR */
+ if (is_play)
+ val = SICTR_TXE;
+ else
+ val = SICTR_RXE;
+ msiof_update_and_wait(priv, SICTR, val, 0, 0);
+
+ /* indicate error status if exist */
+ if (priv->err_syc[substream->stream] ||
+ priv->err_ovf[substream->stream] ||
+ priv->err_udf[substream->stream])
+ dev_warn(dev, "FSERR(%s) = %d, FOVF = %d, FUDF = %d\n",
+ snd_pcm_direction_name(substream->stream),
+ priv->err_syc[substream->stream],
+ priv->err_ovf[substream->stream],
+ priv->err_udf[substream->stream]);
+
+ return 0;
+}
+
+static int msiof_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct msiof_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ /*
+ * It supports Clock/Frame Consumer Mode only
+ * see
+ * [NOTE] on top of this driver
+ */
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ /* others are error */
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ /* it supports NB_NF only */
+ case SND_SOC_DAIFMT_NB_NF:
+ default:
+ break;
+ /* others are error */
+ case SND_SOC_DAIFMT_NB_IF:
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ msiof_flag_set(priv, MSIOF_FLAGS_NEED_DELAY);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Select below from Sound Card, not auto
+ * SND_SOC_DAIFMT_CBC_CFC
+ * SND_SOC_DAIFMT_CBP_CFP
+ */
+static const u64 msiof_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
+static const struct snd_soc_dai_ops msiof_dai_ops = {
+ .set_fmt = msiof_dai_set_fmt,
+ .auto_selectable_formats = &msiof_dai_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static struct snd_soc_dai_driver msiof_dai_driver = {
+ .name = "msiof-dai",
+ .playback = {
+ .rates = MSIOF_RATES,
+ .formats = MSIOF_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .capture = {
+ .rates = MSIOF_RATES,
+ .formats = MSIOF_FMTS,
+ .channels_min = 2,
+ .channels_max = 2,
+ },
+ .ops = &msiof_dai_ops,
+};
+
+static struct snd_pcm_hardware msiof_pcm_hardware = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .buffer_bytes_max = 64 * 1024,
+ .period_bytes_min = 32,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 32,
+ .fifo_size = 64,
+};
+
+static int msiof_open(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ struct device *dev = component->dev;
+ struct dma_chan *chan;
+ static const char * const dma_names[] = {"rx", "tx"};
+ int is_play = msiof_is_play(substream);
+ int ret;
+
+ chan = of_dma_request_slave_channel(dev->of_node, dma_names[is_play]);
+ if (IS_ERR(chan))
+ return PTR_ERR(chan);
+
+ ret = snd_dmaengine_pcm_open(substream, chan);
+ if (ret < 0)
+ goto open_err_dma;
+
+ snd_soc_set_runtime_hwparams(substream, &msiof_pcm_hardware);
+
+ ret = snd_pcm_hw_constraint_integer(substream->runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+
+open_err_dma:
+ if (ret < 0)
+ dma_release_channel(chan);
+
+ return ret;
+}
+
+static int msiof_close(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_dmaengine_pcm_close_release_chan(substream);
+}
+
+static snd_pcm_uframes_t msiof_pointer(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream)
+{
+ return snd_dmaengine_pcm_pointer(substream);
+}
+
+#define PREALLOC_BUFFER (32 * 1024)
+#define PREALLOC_BUFFER_MAX (32 * 1024)
+static int msiof_new(struct snd_soc_component *component,
+ struct snd_soc_pcm_runtime *rtd)
+{
+ snd_pcm_set_managed_buffer_all(rtd->pcm, SNDRV_DMA_TYPE_DEV,
+ rtd->card->snd_card->dev,
+ PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+ return 0;
+}
+
+static int msiof_trigger(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream, int cmd)
+{
+ struct device *dev = component->dev;
+ struct msiof_priv *priv = dev_get_drvdata(dev);
+ unsigned long flags;
+ int ret = -EINVAL;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ priv->substream[substream->stream] = substream;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ ret = msiof_hw_start(component, substream, cmd);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ priv->substream[substream->stream] = NULL;
+ fallthrough;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ ret = msiof_hw_stop(component, substream, cmd);
+ break;
+ }
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static int msiof_hw_params(struct snd_soc_component *component,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct msiof_priv *priv = dev_get_drvdata(component->dev);
+ struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+ struct dma_slave_config cfg = {};
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&priv->lock, flags);
+
+ ret = snd_hwparams_to_dma_slave_config(substream, params, &cfg);
+ if (ret < 0)
+ goto hw_params_out;
+
+ cfg.dst_addr = priv->phy_addr + SITFDR;
+ cfg.src_addr = priv->phy_addr + SIRFDR;
+
+ ret = dmaengine_slave_config(chan, &cfg);
+hw_params_out:
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver msiof_component_driver = {
+ .name = "msiof",
+ .open = msiof_open,
+ .close = msiof_close,
+ .pointer = msiof_pointer,
+ .pcm_construct = msiof_new,
+ .trigger = msiof_trigger,
+ .hw_params = msiof_hw_params,
+};
+
+static irqreturn_t msiof_interrupt(int irq, void *data)
+{
+ struct msiof_priv *priv = data;
+ struct snd_pcm_substream *substream;
+ u32 sistr;
+
+ spin_lock(&priv->lock);
+
+ sistr = msiof_read(priv, SISTR);
+ msiof_status_clear(priv);
+
+ spin_unlock(&priv->lock);
+
+ /* overflow/underflow error */
+ substream = priv->substream[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream && (sistr & SISTR_ERR_TX)) {
+ // snd_pcm_stop_xrun(substream);
+ if (sistr & SISTR_TFSERR)
+ priv->err_syc[SNDRV_PCM_STREAM_PLAYBACK]++;
+ if (sistr & SISTR_TFOVF)
+ priv->err_ovf[SNDRV_PCM_STREAM_PLAYBACK]++;
+ if (sistr & SISTR_TFUDF)
+ priv->err_udf[SNDRV_PCM_STREAM_PLAYBACK]++;
+ }
+
+ substream = priv->substream[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream && (sistr & SISTR_ERR_RX)) {
+ // snd_pcm_stop_xrun(substream);
+ if (sistr & SISTR_RFSERR)
+ priv->err_syc[SNDRV_PCM_STREAM_CAPTURE]++;
+ if (sistr & SISTR_RFOVF)
+ priv->err_ovf[SNDRV_PCM_STREAM_CAPTURE]++;
+ if (sistr & SISTR_RFUDF)
+ priv->err_udf[SNDRV_PCM_STREAM_CAPTURE]++;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int msiof_probe(struct platform_device *pdev)
+{
+ struct msiof_priv *priv;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ int irq, ret;
+
+ /* Check MSIOF as Sound mode or SPI mode */
+ struct device_node *port __free(device_node) = of_graph_get_next_port(dev->of_node, NULL);
+ if (!port)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0)
+ return irq;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(priv->base))
+ return PTR_ERR(priv->base);
+
+ ret = devm_request_irq(dev, irq, msiof_interrupt, 0, dev_name(dev), priv);
+ if (ret)
+ return ret;
+
+ priv->dev = dev;
+ priv->phy_addr = res->start;
+
+ spin_lock_init(&priv->lock);
+ platform_set_drvdata(pdev, priv);
+
+ devm_pm_runtime_enable(dev);
+
+ ret = devm_snd_soc_register_component(dev, &msiof_component_driver,
+ &msiof_dai_driver, 1);
+
+ return ret;
+}
+
+static const struct of_device_id msiof_of_match[] = {
+ { .compatible = "renesas,rcar-gen4-msiof", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, msiof_of_match);
+
+static struct platform_driver msiof_driver = {
+ .driver = {
+ .name = "msiof-pcm-audio",
+ .of_match_table = msiof_of_match,
+ },
+ .probe = msiof_probe,
+};
+module_platform_driver(msiof_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car MSIOF I2S audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/renesas/rcar/src.c b/sound/soc/renesas/rcar/src.c
index 7d73b183bda6..f47bf38c2f94 100644
--- a/sound/soc/renesas/rcar/src.c
+++ b/sound/soc/renesas/rcar/src.c
@@ -715,7 +715,6 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
int rsnd_src_probe(struct rsnd_priv *priv)
{
struct device_node *node;
- struct device_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_src *src;
struct clk *clk;
@@ -742,14 +741,13 @@ int rsnd_src_probe(struct rsnd_priv *priv)
priv->src = src;
i = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
if (!of_device_is_available(np))
goto skip;
i = rsnd_node_fixed_index(dev, np, SRC_NAME, i);
if (i < 0) {
ret = -EINVAL;
- of_node_put(np);
goto rsnd_src_probe_done;
}
@@ -761,23 +759,19 @@ int rsnd_src_probe(struct rsnd_priv *priv)
src->irq = irq_of_parse_and_map(np, 0);
if (!src->irq) {
ret = -EINVAL;
- of_node_put(np);
goto rsnd_src_probe_done;
}
clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
- of_node_put(np);
goto rsnd_src_probe_done;
}
ret = rsnd_mod_init(priv, rsnd_mod_get(src),
&rsnd_src_ops, clk, RSND_MOD_SRC, i);
- if (ret) {
- of_node_put(np);
+ if (ret)
goto rsnd_src_probe_done;
- }
skip:
i++;
diff --git a/sound/soc/renesas/rcar/ssi.c b/sound/soc/renesas/rcar/ssi.c
index 0c6424a1fcac..d52056caa3ec 100644
--- a/sound/soc/renesas/rcar/ssi.c
+++ b/sound/soc/renesas/rcar/ssi.c
@@ -1115,7 +1115,6 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
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;
node = rsnd_ssi_of_node(priv);
@@ -1123,14 +1122,12 @@ void rsnd_parse_connect_ssi(struct rsnd_dai *rdai,
return;
i = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
struct rsnd_mod *mod;
i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
- if (i < 0) {
- of_node_put(np);
+ if (i < 0)
break;
- }
mod = rsnd_ssi_mod_get(priv, i);
@@ -1163,7 +1160,6 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
int rsnd_ssi_probe(struct rsnd_priv *priv)
{
struct device_node *node;
- struct device_node *np;
struct device *dev = rsnd_priv_to_dev(priv);
struct rsnd_mod_ops *ops;
struct clk *clk;
@@ -1191,14 +1187,13 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
priv->ssi_nr = nr;
i = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
if (!of_device_is_available(np))
goto skip;
i = rsnd_node_fixed_index(dev, np, SSI_NAME, i);
if (i < 0) {
ret = -EINVAL;
- of_node_put(np);
goto rsnd_ssi_probe_done;
}
@@ -1210,7 +1205,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
clk = devm_clk_get(dev, name);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
- of_node_put(np);
goto rsnd_ssi_probe_done;
}
@@ -1223,7 +1217,6 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
ssi->irq = irq_of_parse_and_map(np, 0);
if (!ssi->irq) {
ret = -EINVAL;
- of_node_put(np);
goto rsnd_ssi_probe_done;
}
@@ -1234,10 +1227,9 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
RSND_MOD_SSI, i);
- if (ret) {
- of_node_put(np);
+ if (ret)
goto rsnd_ssi_probe_done;
- }
+
skip:
i++;
}
diff --git a/sound/soc/renesas/rcar/ssiu.c b/sound/soc/renesas/rcar/ssiu.c
index 665e8b2db579..faf351126d57 100644
--- a/sound/soc/renesas/rcar/ssiu.c
+++ b/sound/soc/renesas/rcar/ssiu.c
@@ -478,17 +478,14 @@ void rsnd_parse_connect_ssiu(struct rsnd_dai *rdai,
/* use rcar_sound,ssiu if exist */
if (node) {
- struct device_node *np;
int i = 0;
- for_each_child_of_node(node, np) {
+ for_each_child_of_node_scoped(node, np) {
struct rsnd_mod *mod;
i = rsnd_node_fixed_index(dev, np, SSIU_NAME, i);
- if (i < 0) {
- of_node_put(np);
+ if (i < 0)
break;
- }
mod = rsnd_ssiu_mod_get(priv, i);
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index f98a2fa85edd..bd210fafe9fe 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -1,15 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
-config SND_SOC_ROCKCHIP
- tristate "ASoC support for Rockchip"
+menu "Rockchip"
depends on COMPILE_TEST || ARCH_ROCKCHIP
- help
- Say Y or M if you want to add support for codecs attached to
- the Rockchip SoCs' Audio interfaces. You will also need to
- select the audio interfaces to support below.
+ depends on HAVE_CLK
config SND_SOC_ROCKCHIP_I2S
tristate "Rockchip I2S Device Driver"
- depends on HAVE_CLK && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for I2S driver for
@@ -18,7 +13,6 @@ config SND_SOC_ROCKCHIP_I2S
config SND_SOC_ROCKCHIP_I2S_TDM
tristate "Rockchip I2S/TDM Device Driver"
- depends on HAVE_CLK && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for the I2S/TDM driver for
@@ -29,7 +23,6 @@ config SND_SOC_ROCKCHIP_I2S_TDM
config SND_SOC_ROCKCHIP_PDM
tristate "Rockchip PDM Controller Driver"
- depends on HAVE_CLK && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
select RATIONAL
help
@@ -37,9 +30,17 @@ config SND_SOC_ROCKCHIP_PDM
Rockchip PDM Controller. The Controller supports up to maximum of
8 channels record.
+config SND_SOC_ROCKCHIP_SAI
+ tristate "Rockchip SAI Controller Driver"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for the Rockchip Serial Audio
+ Interface controller found on Rockchip SoCs such as the RK3576. The
+ controller may support both playback and recording, with up to 4 lanes
+ for each and up to 128 channels per lane in TDM mode.
+
config SND_SOC_ROCKCHIP_SPDIF
tristate "Rockchip SPDIF Device Driver"
- depends on HAVE_CLK && SND_SOC_ROCKCHIP
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for SPDIF driver for
@@ -47,7 +48,7 @@ config SND_SOC_ROCKCHIP_SPDIF
config SND_SOC_ROCKCHIP_MAX98090
tristate "ASoC support for Rockchip boards using a MAX98090 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK
+ depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
@@ -58,7 +59,7 @@ config SND_SOC_ROCKCHIP_MAX98090
config SND_SOC_ROCKCHIP_RT5645
tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK
+ depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_RT5645
help
@@ -67,7 +68,7 @@ config SND_SOC_ROCKCHIP_RT5645
config SND_SOC_RK3288_HDMI_ANALOG
tristate "ASoC support multiple codecs for Rockchip RK3288 boards"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK
+ depends on I2C && GPIOLIB
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_HDMI_CODEC
select SND_SOC_ES8328_I2C
@@ -79,7 +80,7 @@ config SND_SOC_RK3288_HDMI_ANALOG
config SND_SOC_RK3399_GRU_SOUND
tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards"
- depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && HAVE_CLK && SPI
+ depends on I2C && GPIOLIB && SPI
select SND_SOC_ROCKCHIP_I2S
select SND_SOC_MAX98357A
select SND_SOC_RT5514
@@ -90,3 +91,5 @@ config SND_SOC_RK3399_GRU_SOUND
help
Say Y or M here if you want to add support multiple codecs for SoC
audio on Rockchip RK3399 GRU boards.
+
+endmenu
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index 2ee9c08131d1..af6dc1165347 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -3,10 +3,12 @@
snd-soc-rockchip-i2s-y := rockchip_i2s.o
snd-soc-rockchip-i2s-tdm-y := rockchip_i2s_tdm.o
snd-soc-rockchip-pdm-y := rockchip_pdm.o
+snd-soc-rockchip-sai-y := rockchip_sai.o
snd-soc-rockchip-spdif-y := rockchip_spdif.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_PDM) += snd-soc-rockchip-pdm.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_SAI) += snd-soc-rockchip-sai.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S_TDM) += snd-soc-rockchip-i2s-tdm.o
diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c
new file mode 100644
index 000000000000..6695349ee561
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_sai.c
@@ -0,0 +1,1529 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ALSA SoC Audio Layer - Rockchip SAI Controller driver
+ *
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ * Copyright (c) 2025 Collabora Ltd.
+ */
+
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/delay.h>
+#include <linux/of_device.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/spinlock.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/tlv.h>
+
+#include "rockchip_sai.h"
+
+#define DRV_NAME "rockchip-sai"
+
+#define CLK_SHIFT_RATE_HZ_MAX 5
+#define FW_RATIO_MAX 8
+#define FW_RATIO_MIN 1
+#define MAXBURST_PER_FIFO 8
+
+#define TIMEOUT_US 1000
+#define WAIT_TIME_MS_MAX 10000
+
+#define MAX_LANES 4
+
+enum fpw_mode {
+ FPW_ONE_BCLK_WIDTH,
+ FPW_ONE_SLOT_WIDTH,
+ FPW_HALF_FRAME_WIDTH,
+};
+
+struct rk_sai_dev {
+ struct device *dev;
+ struct clk *hclk;
+ struct clk *mclk;
+ struct regmap *regmap;
+ struct reset_control *rst_h;
+ struct reset_control *rst_m;
+ struct snd_dmaengine_dai_dma_data capture_dma_data;
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+ struct snd_pcm_substream *substreams[SNDRV_PCM_STREAM_LAST + 1];
+ unsigned int mclk_rate;
+ unsigned int wait_time[SNDRV_PCM_STREAM_LAST + 1];
+ unsigned int tx_lanes;
+ unsigned int rx_lanes;
+ unsigned int sdi[MAX_LANES];
+ unsigned int sdo[MAX_LANES];
+ unsigned int version;
+ enum fpw_mode fpw;
+ int fw_ratio;
+ bool has_capture;
+ bool has_playback;
+ bool is_master_mode;
+ bool is_tdm;
+ bool initialized;
+ /* protects register writes that depend on the state of XFER[1:0] */
+ spinlock_t xfer_lock;
+};
+
+static bool rockchip_sai_stream_valid(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ if (!substream)
+ return false;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ sai->has_playback)
+ return true;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ sai->has_capture)
+ return true;
+
+ return false;
+}
+
+static int rockchip_sai_fsync_lost_detect(struct rk_sai_dev *sai, bool en)
+{
+ unsigned int fw, cnt;
+
+ if (sai->is_master_mode || sai->version < SAI_VER_2311)
+ return 0;
+
+ regmap_read(sai->regmap, SAI_FSCR, &fw);
+ cnt = SAI_FSCR_FW_V(fw) << 1; /* two fsync lost */
+
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOSTC, SAI_INTCR_FSLOSTC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOST_MASK,
+ SAI_INTCR_FSLOST(en));
+ /*
+ * The `cnt` is the number of SCLK cycles of the CRU's SCLK signal that
+ * should be used as timeout. Consequently, in slave mode, this value
+ * is only correct if the CRU SCLK is equal to the external SCLK.
+ */
+ regmap_update_bits(sai->regmap, SAI_FS_TIMEOUT,
+ SAI_FS_TIMEOUT_VAL_MASK | SAI_FS_TIMEOUT_EN_MASK,
+ SAI_FS_TIMEOUT_VAL(cnt) | SAI_FS_TIMEOUT_EN(en));
+
+ return 0;
+}
+
+static int rockchip_sai_fsync_err_detect(struct rk_sai_dev *sai,
+ bool en)
+{
+ if (sai->is_master_mode || sai->version < SAI_VER_2311)
+ return 0;
+
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERRC, SAI_INTCR_FSERRC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERR_MASK,
+ SAI_INTCR_FSERR(en));
+
+ return 0;
+}
+
+static int rockchip_sai_poll_clk_idle(struct rk_sai_dev *sai)
+{
+ unsigned int reg, idle, val;
+ int ret;
+
+ if (sai->version >= SAI_VER_2307) {
+ reg = SAI_STATUS;
+ idle = SAI_STATUS_FS_IDLE;
+ idle = sai->version >= SAI_VER_2311 ? idle >> 1 : idle;
+ } else {
+ reg = SAI_XFER;
+ idle = SAI_XFER_FS_IDLE;
+ }
+
+ ret = regmap_read_poll_timeout_atomic(sai->regmap, reg, val,
+ (val & idle), 10, TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(sai->dev, "Failed to idle FS\n");
+
+ return ret;
+}
+
+static int rockchip_sai_poll_stream_idle(struct rk_sai_dev *sai, bool playback, bool capture)
+{
+ unsigned int reg, val;
+ unsigned int idle = 0;
+ int ret;
+
+ if (sai->version >= SAI_VER_2307) {
+ reg = SAI_STATUS;
+ if (playback)
+ idle |= SAI_STATUS_TX_IDLE;
+ if (capture)
+ idle |= SAI_STATUS_RX_IDLE;
+ idle = sai->version >= SAI_VER_2311 ? idle >> 1 : idle;
+ } else {
+ reg = SAI_XFER;
+ if (playback)
+ idle |= SAI_XFER_TX_IDLE;
+ if (capture)
+ idle |= SAI_XFER_RX_IDLE;
+ }
+
+ ret = regmap_read_poll_timeout_atomic(sai->regmap, reg, val,
+ (val & idle), 10, TIMEOUT_US);
+ if (ret < 0)
+ dev_warn(sai->dev, "Failed to idle stream\n");
+
+ return ret;
+}
+
+/**
+ * rockchip_sai_xfer_clk_stop_and_wait() - stop the xfer clock and wait for it to be idle
+ * @sai: pointer to the driver instance's rk_sai_dev struct
+ * @to_restore: pointer to store the CLK/FSS register values in as they were
+ * found before they were cleared, or NULL.
+ *
+ * Clear the XFER_CLK and XFER_FSS registers if needed, then busy-waits for the
+ * XFER clocks to be idle. Before clearing the bits, it stores the state of the
+ * registers as it encountered them in to_restore if it isn't NULL.
+ *
+ * Context: Any context. Expects sai->xfer_lock to be held by caller.
+ */
+static void rockchip_sai_xfer_clk_stop_and_wait(struct rk_sai_dev *sai, unsigned int *to_restore)
+{
+ unsigned int mask = SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK;
+ unsigned int disable = SAI_XFER_CLK_DIS | SAI_XFER_FSS_DIS;
+ unsigned int val;
+
+ assert_spin_locked(&sai->xfer_lock);
+
+ regmap_read(sai->regmap, SAI_XFER, &val);
+ if ((val & mask) == disable)
+ goto wait_for_idle;
+
+ if (sai->is_master_mode)
+ regmap_update_bits(sai->regmap, SAI_XFER, mask, disable);
+
+wait_for_idle:
+ rockchip_sai_poll_clk_idle(sai);
+
+ if (to_restore)
+ *to_restore = val;
+}
+
+static int rockchip_sai_runtime_suspend(struct device *dev)
+{
+ struct rk_sai_dev *sai = dev_get_drvdata(dev);
+ unsigned long flags;
+
+ rockchip_sai_fsync_lost_detect(sai, 0);
+ rockchip_sai_fsync_err_detect(sai, 0);
+
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, NULL);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+
+ regcache_cache_only(sai->regmap, true);
+ /*
+ * After FS is idle, we should wait at least 2 BCLK cycles to make sure
+ * the CLK gate operation has completed, and only then disable mclk.
+ *
+ * Otherwise, the BCLK is still ungated, and once the mclk is enabled,
+ * there is a risk that a few BCLK cycles leak. This is true especially
+ * at low speeds, such as with a samplerate of 8k.
+ *
+ * Ideally we'd adjust the delay based on the samplerate, but it's such
+ * a tiny value that we can just delay for the maximum clock period
+ * for the sake of simplicity.
+ *
+ * The maximum BCLK period is 31us @ 8K-8Bit (64kHz BCLK). We wait for
+ * 40us to give ourselves a safety margin in case udelay falls short.
+ */
+ udelay(40);
+ clk_disable_unprepare(sai->mclk);
+ clk_disable_unprepare(sai->hclk);
+
+ return 0;
+}
+
+static int rockchip_sai_runtime_resume(struct device *dev)
+{
+ struct rk_sai_dev *sai = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(sai->hclk);
+ if (ret)
+ goto err_hclk;
+
+ ret = clk_prepare_enable(sai->mclk);
+ if (ret)
+ goto err_mclk;
+
+ regcache_cache_only(sai->regmap, false);
+ regcache_mark_dirty(sai->regmap);
+ ret = regcache_sync(sai->regmap);
+ if (ret)
+ goto err_regmap;
+
+ return 0;
+
+err_regmap:
+ clk_disable_unprepare(sai->mclk);
+err_mclk:
+ clk_disable_unprepare(sai->hclk);
+err_hclk:
+ return ret;
+}
+
+static void rockchip_sai_fifo_xrun_detect(struct rk_sai_dev *sai,
+ int stream, bool en)
+{
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /* clear irq status which was asserted before TXUIE enabled */
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIC, SAI_INTCR_TXUIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIE_MASK,
+ SAI_INTCR_TXUIE(en));
+ } else {
+ /* clear irq status which was asserted before RXOIE enabled */
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIC, SAI_INTCR_RXOIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIE_MASK,
+ SAI_INTCR_RXOIE(en));
+ }
+}
+
+static void rockchip_sai_dma_ctrl(struct rk_sai_dev *sai,
+ int stream, bool en)
+{
+ if (!en)
+ rockchip_sai_fifo_xrun_detect(sai, stream, 0);
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(sai->regmap, SAI_DMACR,
+ SAI_DMACR_TDE_MASK,
+ SAI_DMACR_TDE(en));
+ } else {
+ regmap_update_bits(sai->regmap, SAI_DMACR,
+ SAI_DMACR_RDE_MASK,
+ SAI_DMACR_RDE(en));
+ }
+
+ if (en)
+ rockchip_sai_fifo_xrun_detect(sai, stream, 1);
+}
+
+static void rockchip_sai_reset(struct rk_sai_dev *sai)
+{
+ /*
+ * It is advised to reset the hclk domain before resetting the mclk
+ * domain, especially in slave mode without a clock input.
+ *
+ * To deal with the aforementioned case of slave mode without a clock
+ * input, we work around a potential issue by resetting the whole
+ * controller, bringing it back into master mode, and then recovering
+ * the controller configuration in the regmap.
+ */
+ reset_control_assert(sai->rst_h);
+ udelay(10);
+ reset_control_deassert(sai->rst_h);
+ udelay(10);
+ reset_control_assert(sai->rst_m);
+ udelay(10);
+ reset_control_deassert(sai->rst_m);
+ udelay(10);
+
+ /* recover regmap config */
+ regcache_mark_dirty(sai->regmap);
+ regcache_sync(sai->regmap);
+}
+
+static int rockchip_sai_clear(struct rk_sai_dev *sai, unsigned int clr)
+{
+ unsigned int val = 0;
+ int ret = 0;
+
+ regmap_update_bits(sai->regmap, SAI_CLR, clr, clr);
+ ret = regmap_read_poll_timeout_atomic(sai->regmap, SAI_CLR, val,
+ !(val & clr), 10, TIMEOUT_US);
+ if (ret < 0) {
+ dev_warn(sai->dev, "Failed to clear %u\n", clr);
+ rockchip_sai_reset(sai);
+ }
+
+ return ret;
+}
+
+static void rockchip_sai_xfer_start(struct rk_sai_dev *sai, int stream)
+{
+ unsigned int msk, val;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ msk = SAI_XFER_TXS_MASK;
+ val = SAI_XFER_TXS_EN;
+
+ } else {
+ msk = SAI_XFER_RXS_MASK;
+ val = SAI_XFER_RXS_EN;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_XFER, msk, val);
+}
+
+static void rockchip_sai_xfer_stop(struct rk_sai_dev *sai, int stream)
+{
+ unsigned int msk = 0, val = 0, clr = 0;
+ bool capture = stream == SNDRV_PCM_STREAM_CAPTURE || stream < 0;
+ bool playback = stream == SNDRV_PCM_STREAM_PLAYBACK || stream < 0;
+ /* could be <= 0 but we don't want to depend on enum values */
+
+ if (playback) {
+ msk |= SAI_XFER_TXS_MASK;
+ val |= SAI_XFER_TXS_DIS;
+ clr |= SAI_CLR_TXC;
+ }
+ if (capture) {
+ msk |= SAI_XFER_RXS_MASK;
+ val |= SAI_XFER_RXS_DIS;
+ clr |= SAI_CLR_RXC;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_XFER, msk, val);
+ rockchip_sai_poll_stream_idle(sai, playback, capture);
+
+ rockchip_sai_clear(sai, clr);
+}
+
+static void rockchip_sai_start(struct rk_sai_dev *sai, int stream)
+{
+ rockchip_sai_dma_ctrl(sai, stream, 1);
+ rockchip_sai_xfer_start(sai, stream);
+}
+
+static void rockchip_sai_stop(struct rk_sai_dev *sai, int stream)
+{
+ rockchip_sai_dma_ctrl(sai, stream, 0);
+ rockchip_sai_xfer_stop(sai, stream);
+}
+
+static void rockchip_sai_fmt_create(struct rk_sai_dev *sai, unsigned int fmt)
+{
+ unsigned int xcr_mask = 0, xcr_val = 0, xsft_mask = 0, xsft_val = 0;
+ unsigned int fscr_mask = 0, fscr_val = 0;
+
+ assert_spin_locked(&sai->xfer_lock);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_RIGHT_J:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_R | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(0);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_DUAL;
+ sai->fpw = FPW_HALF_FRAME_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(0);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_DUAL;
+ sai->fpw = FPW_HALF_FRAME_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_1;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ if (sai->is_tdm)
+ xsft_val = SAI_XSHIFT_RIGHT(1);
+ else
+ xsft_val = SAI_XSHIFT_RIGHT(2);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_DUAL;
+ sai->fpw = FPW_HALF_FRAME_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(2);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_RISING;
+ sai->fpw = FPW_ONE_BCLK_WIDTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ xcr_mask = SAI_XCR_VDJ_MASK | SAI_XCR_EDGE_SHIFT_MASK;
+ xcr_val = SAI_XCR_VDJ_L | SAI_XCR_EDGE_SHIFT_0;
+ xsft_mask = SAI_XSHIFT_RIGHT_MASK;
+ xsft_val = SAI_XSHIFT_RIGHT(0);
+ fscr_mask = SAI_FSCR_EDGE_MASK;
+ fscr_val = SAI_FSCR_EDGE_RISING;
+ sai->fpw = FPW_ONE_BCLK_WIDTH;
+ break;
+ default:
+ dev_err(sai->dev, "Unsupported fmt %u\n", fmt);
+ break;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_TXCR, xcr_mask, xcr_val);
+ regmap_update_bits(sai->regmap, SAI_RXCR, xcr_mask, xcr_val);
+ regmap_update_bits(sai->regmap, SAI_TX_SHIFT, xsft_mask, xsft_val);
+ regmap_update_bits(sai->regmap, SAI_RX_SHIFT, xsft_mask, xsft_val);
+ regmap_update_bits(sai->regmap, SAI_FSCR, fscr_mask, fscr_val);
+}
+
+static int rockchip_sai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned int mask = 0, val = 0;
+ unsigned int clk_gates;
+ unsigned long flags;
+ int ret = 0;
+
+ pm_runtime_get_sync(dai->dev);
+
+ mask = SAI_CKR_MSS_MASK;
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ val = SAI_CKR_MSS_MASTER;
+ sai->is_master_mode = true;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ val = SAI_CKR_MSS_SLAVE;
+ sai->is_master_mode = false;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
+ if (sai->initialized) {
+ if (sai->has_capture && sai->has_playback)
+ rockchip_sai_xfer_stop(sai, -1);
+ else if (sai->has_capture)
+ rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_CAPTURE);
+ else
+ rockchip_sai_xfer_stop(sai, SNDRV_PCM_STREAM_PLAYBACK);
+ } else {
+ rockchip_sai_clear(sai, 0);
+ sai->initialized = true;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
+
+ mask = SAI_CKR_CKP_MASK | SAI_CKR_FSP_MASK;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ val = SAI_CKR_CKP_NORMAL | SAI_CKR_FSP_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val = SAI_CKR_CKP_INVERTED | SAI_CKR_FSP_INVERTED;
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_CKR, mask, val);
+
+ rockchip_sai_fmt_create(sai, fmt);
+
+err_xfer_unlock:
+ if (clk_gates)
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
+ clk_gates);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+err_pm_put:
+ pm_runtime_put(dai->dev);
+
+ return ret;
+}
+
+static int rockchip_sai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ unsigned int mclk_rate, mclk_req_rate, bclk_rate, div_bclk;
+ unsigned int ch_per_lane, slot_width;
+ unsigned int val, fscr, reg;
+ unsigned int lanes, req_lanes;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ dma_data = snd_soc_dai_get_dma_data(dai, substream);
+ dma_data->maxburst = MAXBURST_PER_FIFO * params_channels(params) / 2;
+
+ pm_runtime_get_sync(sai->dev);
+
+ regmap_read(sai->regmap, SAI_DMACR, &val);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ reg = SAI_TXCR;
+ lanes = sai->tx_lanes;
+ } else {
+ reg = SAI_RXCR;
+ lanes = sai->rx_lanes;
+ }
+
+ if (!sai->is_tdm) {
+ req_lanes = DIV_ROUND_UP(params_channels(params), 2);
+ if (lanes < req_lanes) {
+ dev_err(sai->dev, "not enough lanes (%d) for requested number of %s channels (%d)\n",
+ lanes, reg == SAI_TXCR ? "playback" : "capture",
+ params_channels(params));
+ ret = -EINVAL;
+ goto err_pm_put;
+ } else {
+ lanes = req_lanes;
+ }
+ }
+
+ dev_dbg(sai->dev, "using %d lanes totalling %d%s channels for %s\n",
+ lanes, params_channels(params), sai->is_tdm ? " (TDM)" : "",
+ reg == SAI_TXCR ? "playback" : "capture");
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ case SNDRV_PCM_FORMAT_U8:
+ val = SAI_XCR_VDW(8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ val = SAI_XCR_VDW(16);
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ val = SAI_XCR_VDW(24);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ case SNDRV_PCM_FORMAT_IEC958_SUBFRAME_LE:
+ val = SAI_XCR_VDW(32);
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_pm_put;
+ }
+
+ val |= SAI_XCR_CSR(lanes);
+
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+
+ regmap_update_bits(sai->regmap, reg, SAI_XCR_VDW_MASK | SAI_XCR_CSR_MASK, val);
+
+ regmap_read(sai->regmap, reg, &val);
+
+ slot_width = SAI_XCR_SBW_V(val);
+ ch_per_lane = params_channels(params) / lanes;
+
+ regmap_update_bits(sai->regmap, reg, SAI_XCR_SNB_MASK,
+ SAI_XCR_SNB(ch_per_lane));
+
+ fscr = SAI_FSCR_FW(sai->fw_ratio * slot_width * ch_per_lane);
+
+ switch (sai->fpw) {
+ case FPW_ONE_BCLK_WIDTH:
+ fscr |= SAI_FSCR_FPW(1);
+ break;
+ case FPW_ONE_SLOT_WIDTH:
+ fscr |= SAI_FSCR_FPW(slot_width);
+ break;
+ case FPW_HALF_FRAME_WIDTH:
+ fscr |= SAI_FSCR_FPW(sai->fw_ratio * slot_width * ch_per_lane / 2);
+ break;
+ default:
+ dev_err(sai->dev, "Invalid Frame Pulse Width %d\n", sai->fpw);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_FSCR,
+ SAI_FSCR_FW_MASK | SAI_FSCR_FPW_MASK, fscr);
+
+ if (sai->is_master_mode) {
+ bclk_rate = sai->fw_ratio * slot_width * ch_per_lane * params_rate(params);
+ ret = clk_set_rate(sai->mclk, sai->mclk_rate);
+ if (ret) {
+ dev_err(sai->dev, "Failed to set mclk to %u: %pe\n",
+ sai->mclk_rate, ERR_PTR(ret));
+ goto err_xfer_unlock;
+ }
+
+ mclk_rate = clk_get_rate(sai->mclk);
+ if (mclk_rate < bclk_rate) {
+ dev_err(sai->dev, "Mismatch mclk: %u, at least %u\n",
+ mclk_rate, bclk_rate);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ div_bclk = DIV_ROUND_CLOSEST(mclk_rate, bclk_rate);
+ mclk_req_rate = bclk_rate * div_bclk;
+
+ if (mclk_rate < mclk_req_rate - CLK_SHIFT_RATE_HZ_MAX ||
+ mclk_rate > mclk_req_rate + CLK_SHIFT_RATE_HZ_MAX) {
+ dev_err(sai->dev, "Mismatch mclk: %u, expected %u (+/- %dHz)\n",
+ mclk_rate, mclk_req_rate, CLK_SHIFT_RATE_HZ_MAX);
+ ret = -EINVAL;
+ goto err_xfer_unlock;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_CKR, SAI_CKR_MDIV_MASK,
+ SAI_CKR_MDIV(div_bclk));
+ }
+
+err_xfer_unlock:
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+err_pm_put:
+ pm_runtime_put(sai->dev);
+
+ return ret;
+}
+
+static int rockchip_sai_prepare(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ if (sai->is_master_mode) {
+ /*
+ * We should wait for the first BCLK pulse to have definitely
+ * occurred after any DIV settings have potentially been
+ * changed in order to guarantee a clean clock signal once we
+ * ungate the clock.
+ *
+ * Ideally, this would be done depending on the samplerate, but
+ * for the sake of simplicity, we'll just delay for the maximum
+ * possible clock offset time, which is quite a small value.
+ *
+ * The maximum BCLK offset is 15.6us @ 8K-8Bit (64kHz BCLK). We
+ * wait for 20us in order to give us a safety margin in case
+ * udelay falls short.
+ */
+ udelay(20);
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK |
+ SAI_XFER_FSS_MASK,
+ SAI_XFER_CLK_EN |
+ SAI_XFER_FSS_EN);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+ }
+
+ rockchip_sai_fsync_lost_detect(sai, 1);
+ rockchip_sai_fsync_err_detect(sai, 1);
+
+ return 0;
+}
+
+static void rockchip_sai_path_config(struct rk_sai_dev *sai,
+ int num, bool is_rx)
+{
+ int i;
+
+ if (is_rx)
+ for (i = 0; i < num; i++)
+ regmap_update_bits(sai->regmap, SAI_PATH_SEL,
+ SAI_RX_PATH_MASK(i),
+ SAI_RX_PATH(i, sai->sdi[i]));
+ else
+ for (i = 0; i < num; i++)
+ regmap_update_bits(sai->regmap, SAI_PATH_SEL,
+ SAI_TX_PATH_MASK(i),
+ SAI_TX_PATH(i, sai->sdo[i]));
+}
+
+static int rockchip_sai_path_prepare(struct rk_sai_dev *sai,
+ struct device_node *np,
+ bool is_rx)
+{
+ const char *path_prop;
+ unsigned int *data;
+ unsigned int *lanes;
+ int i, num, ret;
+
+ if (is_rx) {
+ path_prop = "rockchip,sai-rx-route";
+ data = sai->sdi;
+ lanes = &sai->rx_lanes;
+ } else {
+ path_prop = "rockchip,sai-tx-route";
+ data = sai->sdo;
+ lanes = &sai->tx_lanes;
+ }
+
+ num = of_count_phandle_with_args(np, path_prop, NULL);
+ if (num == -ENOENT) {
+ return 0;
+ } else if (num > MAX_LANES || num == 0) {
+ dev_err(sai->dev, "found %d entries in %s, outside of range 1 to %d\n",
+ num, path_prop, MAX_LANES);
+ return -EINVAL;
+ } else if (num < 0) {
+ dev_err(sai->dev, "error in %s property: %pe\n", path_prop,
+ ERR_PTR(num));
+ return num;
+ }
+
+ *lanes = num;
+ ret = device_property_read_u32_array(sai->dev, path_prop, data, num);
+ if (ret < 0) {
+ dev_err(sai->dev, "failed to read property '%s': %pe\n",
+ path_prop, ERR_PTR(ret));
+ return ret;
+ }
+
+ for (i = 0; i < num; i++) {
+ if (data[i] >= MAX_LANES) {
+ dev_err(sai->dev, "%s[%d] is %d, should be less than %d\n",
+ path_prop, i, data[i], MAX_LANES);
+ return -EINVAL;
+ }
+ }
+
+ rockchip_sai_path_config(sai, num, is_rx);
+
+ return 0;
+}
+
+static int rockchip_sai_parse_paths(struct rk_sai_dev *sai,
+ struct device_node *np)
+{
+ int ret;
+
+ if (sai->has_playback) {
+ sai->tx_lanes = 1;
+ ret = rockchip_sai_path_prepare(sai, np, false);
+ if (ret < 0) {
+ dev_err(sai->dev, "Failed to prepare TX path: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ }
+
+ if (sai->has_capture) {
+ sai->rx_lanes = 1;
+ ret = rockchip_sai_path_prepare(sai, np, true);
+ if (ret < 0) {
+ dev_err(sai->dev, "Failed to prepare RX path: %pe\n",
+ ERR_PTR(ret));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int rockchip_sai_trigger(struct snd_pcm_substream *substream,
+ int cmd, struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ int ret = 0;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ rockchip_sai_start(sai, substream->stream);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ rockchip_sai_stop(sai, substream->stream);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+
+static int rockchip_sai_dai_probe(struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai,
+ sai->has_playback ? &sai->playback_dma_data : NULL,
+ sai->has_capture ? &sai->capture_dma_data : NULL);
+
+ return 0;
+}
+
+static int rockchip_sai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ int stream = substream->stream;
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return 0;
+
+ if (sai->substreams[stream])
+ return -EBUSY;
+
+ if (sai->wait_time[stream])
+ substream->wait_time = sai->wait_time[stream];
+
+ sai->substreams[stream] = substream;
+
+ return 0;
+}
+
+static void rockchip_sai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ if (!rockchip_sai_stream_valid(substream, dai))
+ return;
+
+ sai->substreams[substream->stream] = NULL;
+}
+
+static int rockchip_sai_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+ unsigned long flags;
+ unsigned int clk_gates;
+ int sw = slot_width;
+
+ if (!slots) {
+ /* Disabling TDM, set slot width back to 32 bits */
+ sai->is_tdm = false;
+ sw = 32;
+ } else {
+ sai->is_tdm = true;
+ }
+
+ if (sw < 16 || sw > 32)
+ return -EINVAL;
+
+ pm_runtime_get_sync(dai->dev);
+ spin_lock_irqsave(&sai->xfer_lock, flags);
+ rockchip_sai_xfer_clk_stop_and_wait(sai, &clk_gates);
+ regmap_update_bits(sai->regmap, SAI_TXCR, SAI_XCR_SBW_MASK,
+ SAI_XCR_SBW(sw));
+ regmap_update_bits(sai->regmap, SAI_RXCR, SAI_XCR_SBW_MASK,
+ SAI_XCR_SBW(sw));
+ regmap_update_bits(sai->regmap, SAI_XFER,
+ SAI_XFER_CLK_MASK | SAI_XFER_FSS_MASK,
+ clk_gates);
+ spin_unlock_irqrestore(&sai->xfer_lock, flags);
+ pm_runtime_put(dai->dev);
+
+ return 0;
+}
+
+static int rockchip_sai_set_sysclk(struct snd_soc_dai *dai, int stream,
+ unsigned int freq, int dir)
+{
+ struct rk_sai_dev *sai = snd_soc_dai_get_drvdata(dai);
+
+ sai->mclk_rate = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rockchip_sai_dai_ops = {
+ .probe = rockchip_sai_dai_probe,
+ .startup = rockchip_sai_startup,
+ .shutdown = rockchip_sai_shutdown,
+ .hw_params = rockchip_sai_hw_params,
+ .set_fmt = rockchip_sai_set_fmt,
+ .set_sysclk = rockchip_sai_set_sysclk,
+ .prepare = rockchip_sai_prepare,
+ .trigger = rockchip_sai_trigger,
+ .set_tdm_slot = rockchip_sai_set_tdm_slot,
+};
+
+static const struct snd_soc_dai_driver rockchip_sai_dai = {
+ .ops = &rockchip_sai_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static bool rockchip_sai_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_TXCR:
+ case SAI_FSCR:
+ case SAI_RXCR:
+ case SAI_MONO_CR:
+ case SAI_XFER:
+ case SAI_CLR:
+ case SAI_CKR:
+ case SAI_DMACR:
+ case SAI_INTCR:
+ case SAI_TXDR:
+ case SAI_PATH_SEL:
+ case SAI_TX_SLOT_MASK0:
+ case SAI_TX_SLOT_MASK1:
+ case SAI_TX_SLOT_MASK2:
+ case SAI_TX_SLOT_MASK3:
+ case SAI_RX_SLOT_MASK0:
+ case SAI_RX_SLOT_MASK1:
+ case SAI_RX_SLOT_MASK2:
+ case SAI_RX_SLOT_MASK3:
+ case SAI_TX_SHIFT:
+ case SAI_RX_SHIFT:
+ case SAI_FSXN:
+ case SAI_FS_TIMEOUT:
+ case SAI_LOOPBACK_LR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_sai_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_TXCR:
+ case SAI_FSCR:
+ case SAI_RXCR:
+ case SAI_MONO_CR:
+ case SAI_XFER:
+ case SAI_CLR:
+ case SAI_CKR:
+ case SAI_TXFIFOLR:
+ case SAI_RXFIFOLR:
+ case SAI_DMACR:
+ case SAI_INTCR:
+ case SAI_INTSR:
+ case SAI_TXDR:
+ case SAI_RXDR:
+ case SAI_PATH_SEL:
+ case SAI_TX_SLOT_MASK0:
+ case SAI_TX_SLOT_MASK1:
+ case SAI_TX_SLOT_MASK2:
+ case SAI_TX_SLOT_MASK3:
+ case SAI_RX_SLOT_MASK0:
+ case SAI_RX_SLOT_MASK1:
+ case SAI_RX_SLOT_MASK2:
+ case SAI_RX_SLOT_MASK3:
+ case SAI_TX_DATA_CNT:
+ case SAI_RX_DATA_CNT:
+ case SAI_TX_SHIFT:
+ case SAI_RX_SHIFT:
+ case SAI_STATUS:
+ case SAI_VERSION:
+ case SAI_FSXN:
+ case SAI_FS_TIMEOUT:
+ case SAI_LOOPBACK_LR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_sai_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_XFER:
+ case SAI_INTCR:
+ case SAI_INTSR:
+ case SAI_CLR:
+ case SAI_TXFIFOLR:
+ case SAI_RXFIFOLR:
+ case SAI_TXDR:
+ case SAI_RXDR:
+ case SAI_TX_DATA_CNT:
+ case SAI_RX_DATA_CNT:
+ case SAI_STATUS:
+ case SAI_VERSION:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rockchip_sai_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SAI_RXDR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default rockchip_sai_reg_defaults[] = {
+ { SAI_TXCR, 0x00000bff },
+ { SAI_FSCR, 0x0001f03f },
+ { SAI_RXCR, 0x00000bff },
+ { SAI_PATH_SEL, 0x0000e4e4 },
+};
+
+static const struct regmap_config rockchip_sai_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SAI_LOOPBACK_LR,
+ .reg_defaults = rockchip_sai_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rockchip_sai_reg_defaults),
+ .writeable_reg = rockchip_sai_wr_reg,
+ .readable_reg = rockchip_sai_rd_reg,
+ .volatile_reg = rockchip_sai_volatile_reg,
+ .precious_reg = rockchip_sai_precious_reg,
+ .cache_type = REGCACHE_FLAT,
+};
+
+static int rockchip_sai_init_dai(struct rk_sai_dev *sai, struct resource *res,
+ struct snd_soc_dai_driver **dp)
+{
+ struct device_node *node = sai->dev->of_node;
+ struct snd_soc_dai_driver *dai;
+ struct property *dma_names;
+ const char *dma_name;
+
+ of_property_for_each_string(node, "dma-names", dma_names, dma_name) {
+ if (!strcmp(dma_name, "tx"))
+ sai->has_playback = true;
+ if (!strcmp(dma_name, "rx"))
+ sai->has_capture = true;
+ }
+
+ dai = devm_kmemdup(sai->dev, &rockchip_sai_dai,
+ sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ if (sai->has_playback) {
+ dai->playback.stream_name = "Playback";
+ dai->playback.channels_min = 1;
+ dai->playback.channels_max = 512;
+ dai->playback.rates = SNDRV_PCM_RATE_8000_384000;
+ dai->playback.formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+
+ sai->playback_dma_data.addr = res->start + SAI_TXDR;
+ sai->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ sai->playback_dma_data.maxburst = MAXBURST_PER_FIFO;
+ }
+
+ if (sai->has_capture) {
+ dai->capture.stream_name = "Capture";
+ dai->capture.channels_min = 1;
+ dai->capture.channels_max = 512;
+ dai->capture.rates = SNDRV_PCM_RATE_8000_384000;
+ dai->capture.formats = SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE;
+
+ sai->capture_dma_data.addr = res->start + SAI_RXDR;
+ sai->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+ sai->capture_dma_data.maxburst = MAXBURST_PER_FIFO;
+ }
+
+ regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_TDL_MASK,
+ SAI_DMACR_TDL(16));
+ regmap_update_bits(sai->regmap, SAI_DMACR, SAI_DMACR_RDL_MASK,
+ SAI_DMACR_RDL(16));
+
+ if (dp)
+ *dp = dai;
+
+ return 0;
+}
+
+static const char * const mono_text[] = { "Disable", "Enable" };
+
+static DECLARE_TLV_DB_SCALE(rmss_tlv, 0, 128, 0);
+
+static const char * const lplrc_text[] = { "L:MIC R:LP", "L:LP R:MIC" };
+static const char * const lplr_text[] = { "Disable", "Enable" };
+
+static const char * const lpx_text[] = {
+ "From SDO0", "From SDO1", "From SDO2", "From SDO3" };
+
+static const char * const lps_text[] = { "Disable", "Enable" };
+static const char * const sync_out_text[] = { "From CRU", "From IO" };
+static const char * const sync_in_text[] = { "From IO", "From Sync Port" };
+
+static const char * const rpaths_text[] = {
+ "From SDI0", "From SDI1", "From SDI2", "From SDI3" };
+
+static const char * const tpaths_text[] = {
+ "From PATH0", "From PATH1", "From PATH2", "From PATH3" };
+
+/* MONO_CR */
+static SOC_ENUM_SINGLE_DECL(rmono_switch, SAI_MONO_CR, 1, mono_text);
+static SOC_ENUM_SINGLE_DECL(tmono_switch, SAI_MONO_CR, 0, mono_text);
+
+/* PATH_SEL */
+static SOC_ENUM_SINGLE_DECL(lp3_enum, SAI_PATH_SEL, 28, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp2_enum, SAI_PATH_SEL, 26, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp1_enum, SAI_PATH_SEL, 24, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp0_enum, SAI_PATH_SEL, 22, lpx_text);
+static SOC_ENUM_SINGLE_DECL(lp3_switch, SAI_PATH_SEL, 21, lps_text);
+static SOC_ENUM_SINGLE_DECL(lp2_switch, SAI_PATH_SEL, 20, lps_text);
+static SOC_ENUM_SINGLE_DECL(lp1_switch, SAI_PATH_SEL, 19, lps_text);
+static SOC_ENUM_SINGLE_DECL(lp0_switch, SAI_PATH_SEL, 18, lps_text);
+static SOC_ENUM_SINGLE_DECL(sync_out_switch, SAI_PATH_SEL, 17, sync_out_text);
+static SOC_ENUM_SINGLE_DECL(sync_in_switch, SAI_PATH_SEL, 16, sync_in_text);
+static SOC_ENUM_SINGLE_DECL(rpath3_enum, SAI_PATH_SEL, 14, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath2_enum, SAI_PATH_SEL, 12, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath1_enum, SAI_PATH_SEL, 10, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(rpath0_enum, SAI_PATH_SEL, 8, rpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath3_enum, SAI_PATH_SEL, 6, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath2_enum, SAI_PATH_SEL, 4, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath1_enum, SAI_PATH_SEL, 2, tpaths_text);
+static SOC_ENUM_SINGLE_DECL(tpath0_enum, SAI_PATH_SEL, 0, tpaths_text);
+
+/* LOOPBACK_LR */
+static SOC_ENUM_SINGLE_DECL(lp3lrc_enum, SAI_LOOPBACK_LR, 7, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp2lrc_enum, SAI_LOOPBACK_LR, 6, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp1lrc_enum, SAI_LOOPBACK_LR, 5, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp0lrc_enum, SAI_LOOPBACK_LR, 4, lplrc_text);
+static SOC_ENUM_SINGLE_DECL(lp3lr_switch, SAI_LOOPBACK_LR, 3, lplr_text);
+static SOC_ENUM_SINGLE_DECL(lp2lr_switch, SAI_LOOPBACK_LR, 2, lplr_text);
+static SOC_ENUM_SINGLE_DECL(lp1lr_switch, SAI_LOOPBACK_LR, 1, lplr_text);
+static SOC_ENUM_SINGLE_DECL(lp0lr_switch, SAI_LOOPBACK_LR, 0, lplr_text);
+
+static int rockchip_sai_wait_time_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = WAIT_TIME_MS_MAX;
+ uinfo->value.integer.step = 1;
+
+ return 0;
+}
+
+static int rockchip_sai_rd_wait_time_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_CAPTURE];
+
+ return 0;
+}
+
+static int rockchip_sai_rd_wait_time_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+ return -EINVAL;
+
+ sai->wait_time[SNDRV_PCM_STREAM_CAPTURE] = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+static int rockchip_sai_wr_wait_time_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK];
+
+ return 0;
+}
+
+static int rockchip_sai_wr_wait_time_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct rk_sai_dev *sai = snd_soc_component_get_drvdata(component);
+
+ if (ucontrol->value.integer.value[0] > WAIT_TIME_MS_MAX)
+ return -EINVAL;
+
+ sai->wait_time[SNDRV_PCM_STREAM_PLAYBACK] = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
+#define SAI_PCM_WAIT_TIME(xname, xhandler_get, xhandler_put) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_PCM, .name = xname, \
+ .info = rockchip_sai_wait_time_info, \
+ .get = xhandler_get, .put = xhandler_put }
+
+static const struct snd_kcontrol_new rockchip_sai_controls[] = {
+ SOC_SINGLE_TLV("Receive Mono Slot Select", SAI_MONO_CR,
+ 2, 128, 0, rmss_tlv),
+ SOC_ENUM("Receive Mono Switch", rmono_switch),
+ SOC_ENUM("Transmit Mono Switch", tmono_switch),
+
+ SOC_ENUM("SDI3 Loopback I2S LR Channel Sel", lp3lrc_enum),
+ SOC_ENUM("SDI2 Loopback I2S LR Channel Sel", lp2lrc_enum),
+ SOC_ENUM("SDI1 Loopback I2S LR Channel Sel", lp1lrc_enum),
+ SOC_ENUM("SDI0 Loopback I2S LR Channel Sel", lp0lrc_enum),
+ SOC_ENUM("SDI3 Loopback I2S LR Switch", lp3lr_switch),
+ SOC_ENUM("SDI2 Loopback I2S LR Switch", lp2lr_switch),
+ SOC_ENUM("SDI1 Loopback I2S LR Switch", lp1lr_switch),
+ SOC_ENUM("SDI0 Loopback I2S LR Switch", lp0lr_switch),
+
+ SOC_ENUM("SDI3 Loopback Src Select", lp3_enum),
+ SOC_ENUM("SDI2 Loopback Src Select", lp2_enum),
+ SOC_ENUM("SDI1 Loopback Src Select", lp1_enum),
+ SOC_ENUM("SDI0 Loopback Src Select", lp0_enum),
+ SOC_ENUM("SDI3 Loopback Switch", lp3_switch),
+ SOC_ENUM("SDI2 Loopback Switch", lp2_switch),
+ SOC_ENUM("SDI1 Loopback Switch", lp1_switch),
+ SOC_ENUM("SDI0 Loopback Switch", lp0_switch),
+ SOC_ENUM("Sync Out Switch", sync_out_switch),
+ SOC_ENUM("Sync In Switch", sync_in_switch),
+ SOC_ENUM("Receive PATH3 Source Select", rpath3_enum),
+ SOC_ENUM("Receive PATH2 Source Select", rpath2_enum),
+ SOC_ENUM("Receive PATH1 Source Select", rpath1_enum),
+ SOC_ENUM("Receive PATH0 Source Select", rpath0_enum),
+ SOC_ENUM("Transmit SDO3 Source Select", tpath3_enum),
+ SOC_ENUM("Transmit SDO2 Source Select", tpath2_enum),
+ SOC_ENUM("Transmit SDO1 Source Select", tpath1_enum),
+ SOC_ENUM("Transmit SDO0 Source Select", tpath0_enum),
+
+ SAI_PCM_WAIT_TIME("PCM Read Wait Time MS",
+ rockchip_sai_rd_wait_time_get,
+ rockchip_sai_rd_wait_time_put),
+ SAI_PCM_WAIT_TIME("PCM Write Wait Time MS",
+ rockchip_sai_wr_wait_time_get,
+ rockchip_sai_wr_wait_time_put),
+};
+
+static const struct snd_soc_component_driver rockchip_sai_component = {
+ .name = DRV_NAME,
+ .controls = rockchip_sai_controls,
+ .num_controls = ARRAY_SIZE(rockchip_sai_controls),
+ .legacy_dai_naming = 1,
+};
+
+static irqreturn_t rockchip_sai_isr(int irq, void *devid)
+{
+ struct rk_sai_dev *sai = (struct rk_sai_dev *)devid;
+ struct snd_pcm_substream *substream;
+ u32 val;
+
+ regmap_read(sai->regmap, SAI_INTSR, &val);
+ if (val & SAI_INTSR_TXUI_ACT) {
+ dev_warn_ratelimited(sai->dev, "TX FIFO Underrun\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIC, SAI_INTCR_TXUIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_TXUIE_MASK,
+ SAI_INTCR_TXUIE(0));
+ substream = sai->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ if (val & SAI_INTSR_RXOI_ACT) {
+ dev_warn_ratelimited(sai->dev, "RX FIFO Overrun\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIC, SAI_INTCR_RXOIC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_RXOIE_MASK,
+ SAI_INTCR_RXOIE(0));
+ substream = sai->substreams[SNDRV_PCM_STREAM_CAPTURE];
+ if (substream)
+ snd_pcm_stop_xrun(substream);
+ }
+
+ if (val & SAI_INTSR_FSERRI_ACT) {
+ dev_warn_ratelimited(sai->dev, "Frame Sync Error\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERRC, SAI_INTCR_FSERRC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSERR_MASK,
+ SAI_INTCR_FSERR(0));
+ }
+
+ if (val & SAI_INTSR_FSLOSTI_ACT) {
+ dev_warn_ratelimited(sai->dev, "Frame Sync Lost\n");
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOSTC, SAI_INTCR_FSLOSTC);
+ regmap_update_bits(sai->regmap, SAI_INTCR,
+ SAI_INTCR_FSLOST_MASK,
+ SAI_INTCR_FSLOST(0));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int rockchip_sai_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct rk_sai_dev *sai;
+ struct snd_soc_dai_driver *dai;
+ struct resource *res;
+ void __iomem *regs;
+ int ret, irq;
+
+ sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ if (!sai)
+ return -ENOMEM;
+
+ sai->dev = &pdev->dev;
+ sai->fw_ratio = 1;
+ /* match to register default */
+ sai->is_master_mode = true;
+ dev_set_drvdata(&pdev->dev, sai);
+
+ spin_lock_init(&sai->xfer_lock);
+
+ sai->rst_h = devm_reset_control_get_optional_exclusive(&pdev->dev, "h");
+ if (IS_ERR(sai->rst_h))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_h),
+ "Error in 'h' reset control\n");
+
+ sai->rst_m = devm_reset_control_get_optional_exclusive(&pdev->dev, "m");
+ if (IS_ERR(sai->rst_m))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->rst_m),
+ "Error in 'm' reset control\n");
+
+ regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(regs))
+ return dev_err_probe(&pdev->dev, PTR_ERR(regs),
+ "Failed to get and ioremap resource\n");
+
+ sai->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &rockchip_sai_regmap_config);
+ if (IS_ERR(sai->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->regmap),
+ "Failed to initialize regmap\n");
+
+ irq = platform_get_irq_optional(pdev, 0);
+ if (irq > 0) {
+ ret = devm_request_irq(&pdev->dev, irq, rockchip_sai_isr,
+ IRQF_SHARED, node->name, sai);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret,
+ "Failed to request irq %d\n", irq);
+ } else {
+ dev_dbg(&pdev->dev, "Asked for an IRQ but got %d\n", irq);
+ }
+
+ sai->mclk = devm_clk_get(&pdev->dev, "mclk");
+ if (IS_ERR(sai->mclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->mclk),
+ "Failed to get mclk\n");
+
+ sai->hclk = devm_clk_get_enabled(&pdev->dev, "hclk");
+ if (IS_ERR(sai->hclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sai->hclk),
+ "Failed to get hclk\n");
+
+ regmap_read(sai->regmap, SAI_VERSION, &sai->version);
+
+ ret = rockchip_sai_init_dai(sai, res, &dai);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to initialize DAI\n");
+
+ ret = rockchip_sai_parse_paths(sai, node);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to parse paths\n");
+
+ /*
+ * From here on, all register accesses need to be wrapped in
+ * pm_runtime_get_sync/pm_runtime_put calls
+ *
+ * NB: we don't rely on _resume_and_get in case of !CONFIG_PM
+ */
+ devm_pm_runtime_enable(&pdev->dev);
+ pm_runtime_get_noresume(&pdev->dev);
+ ret = rockchip_sai_runtime_resume(&pdev->dev);
+ if (ret)
+ return dev_err_probe(&pdev->dev, ret, "Failed to resume device\n");
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register PCM: %d\n", ret);
+ goto err_runtime_suspend;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &rockchip_sai_component,
+ dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to register component: %d\n", ret);
+ goto err_runtime_suspend;
+ }
+
+ pm_runtime_use_autosuspend(&pdev->dev);
+ pm_runtime_put(&pdev->dev);
+
+ clk_disable_unprepare(sai->hclk);
+
+ return 0;
+
+err_runtime_suspend:
+ /* If we're !CONFIG_PM, we get -ENOSYS and disable manually */
+ if (pm_runtime_put(&pdev->dev))
+ rockchip_sai_runtime_suspend(&pdev->dev);
+
+ return ret;
+}
+
+static void rockchip_sai_remove(struct platform_device *pdev)
+{
+#ifndef CONFIG_PM
+ rockchip_sai_runtime_suspend(&pdev->dev);
+#endif
+}
+
+static const struct dev_pm_ops rockchip_sai_pm_ops = {
+ SET_RUNTIME_PM_OPS(rockchip_sai_runtime_suspend, rockchip_sai_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
+};
+
+static const struct of_device_id rockchip_sai_match[] = {
+ { .compatible = "rockchip,rk3576-sai", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rockchip_sai_match);
+
+static struct platform_driver rockchip_sai_driver = {
+ .probe = rockchip_sai_probe,
+ .remove = rockchip_sai_remove,
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = rockchip_sai_match,
+ .pm = &rockchip_sai_pm_ops,
+ },
+};
+module_platform_driver(rockchip_sai_driver);
+
+MODULE_DESCRIPTION("Rockchip SAI ASoC Interface");
+MODULE_AUTHOR("Sugar Zhang <sugar.zhang@rock-chips.com>");
+MODULE_AUTHOR("Nicolas Frattaroli <nicolas.frattaroli@collabora.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/rockchip/rockchip_sai.h b/sound/soc/rockchip/rockchip_sai.h
new file mode 100644
index 000000000000..c359c5d0311c
--- /dev/null
+++ b/sound/soc/rockchip/rockchip_sai.h
@@ -0,0 +1,251 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * ALSA SoC Audio Layer - Rockchip SAI Controller driver
+ *
+ * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
+ */
+
+#ifndef _ROCKCHIP_SAI_H
+#define _ROCKCHIP_SAI_H
+
+/* XCR Transmit / Receive Control Register */
+#define SAI_XCR_START_SEL_MASK BIT(23)
+#define SAI_XCR_START_SEL_CHAINED BIT(23)
+#define SAI_XCR_START_SEL_STANDALONE 0
+#define SAI_XCR_EDGE_SHIFT_MASK BIT(22)
+#define SAI_XCR_EDGE_SHIFT_1 BIT(22)
+#define SAI_XCR_EDGE_SHIFT_0 0
+#define SAI_XCR_CSR_MASK GENMASK(21, 20)
+#define SAI_XCR_CSR(x) ((x - 1) << 20)
+#define SAI_XCR_CSR_V(v) ((((v) & SAI_XCR_CSR_MASK) >> 20) + 1)
+#define SAI_XCR_SJM_MASK BIT(19)
+#define SAI_XCR_SJM_L BIT(19)
+#define SAI_XCR_SJM_R 0
+#define SAI_XCR_FBM_MASK BIT(18)
+#define SAI_XCR_FBM_LSB BIT(18)
+#define SAI_XCR_FBM_MSB 0
+#define SAI_XCR_SNB_MASK GENMASK(17, 11)
+#define SAI_XCR_SNB(x) ((x - 1) << 11)
+#define SAI_XCR_VDJ_MASK BIT(10)
+#define SAI_XCR_VDJ_L BIT(10)
+#define SAI_XCR_VDJ_R 0
+#define SAI_XCR_SBW_MASK GENMASK(9, 5)
+#define SAI_XCR_SBW(x) ((x - 1) << 5)
+#define SAI_XCR_SBW_V(v) ((((v) & SAI_XCR_SBW_MASK) >> 5) + 1)
+#define SAI_XCR_VDW_MASK GENMASK(4, 0)
+#define SAI_XCR_VDW(x) ((x - 1) << 0)
+
+/* FSCR Frame Sync Control Register */
+#define SAI_FSCR_EDGE_MASK BIT(24)
+#define SAI_FSCR_EDGE_DUAL BIT(24)
+#define SAI_FSCR_EDGE_RISING 0
+#define SAI_FSCR_FPW_MASK GENMASK(23, 12)
+#define SAI_FSCR_FPW(x) ((x - 1) << 12)
+#define SAI_FSCR_FW_MASK GENMASK(11, 0)
+#define SAI_FSCR_FW(x) ((x - 1) << 0)
+#define SAI_FSCR_FW_V(v) ((((v) & SAI_FSCR_FW_MASK) >> 0) + 1)
+
+/* MONO_CR Mono Control Register */
+#define SAI_MCR_RX_MONO_SLOT_MASK GENMASK(8, 2)
+#define SAI_MCR_RX_MONO_SLOT_SEL(x) ((x - 1) << 2)
+#define SAI_MCR_RX_MONO_MASK BIT(1)
+#define SAI_MCR_RX_MONO_EN BIT(1)
+#define SAI_MCR_RX_MONO_DIS 0
+#define SAI_MCR_TX_MONO_MASK BIT(0)
+#define SAI_MCR_TX_MONO_EN BIT(0)
+#define SAI_MCR_TX_MONO_DIS 0
+
+/* XFER Transfer Start Register */
+#define SAI_XFER_RX_IDLE BIT(8)
+#define SAI_XFER_TX_IDLE BIT(7)
+#define SAI_XFER_FS_IDLE BIT(6)
+/*
+ * Used for TX only (VERSION >= SAI_VER_2311)
+ *
+ * SCLK/FSYNC auto gated when TX FIFO empty.
+ */
+#define SAI_XFER_TX_AUTO_MASK BIT(6)
+#define SAI_XFER_TX_AUTO_EN BIT(6)
+#define SAI_XFER_TX_AUTO_DIS 0
+#define SAI_XFER_RX_CNT_MASK BIT(5)
+#define SAI_XFER_RX_CNT_EN BIT(5)
+#define SAI_XFER_RX_CNT_DIS 0
+#define SAI_XFER_TX_CNT_MASK BIT(4)
+#define SAI_XFER_TX_CNT_EN BIT(4)
+#define SAI_XFER_TX_CNT_DIS 0
+#define SAI_XFER_RXS_MASK BIT(3)
+#define SAI_XFER_RXS_EN BIT(3)
+#define SAI_XFER_RXS_DIS 0
+#define SAI_XFER_TXS_MASK BIT(2)
+#define SAI_XFER_TXS_EN BIT(2)
+#define SAI_XFER_TXS_DIS 0
+#define SAI_XFER_FSS_MASK BIT(1)
+#define SAI_XFER_FSS_EN BIT(1)
+#define SAI_XFER_FSS_DIS 0
+#define SAI_XFER_CLK_MASK BIT(0)
+#define SAI_XFER_CLK_EN BIT(0)
+#define SAI_XFER_CLK_DIS 0
+
+/* CLR Clear Logic Register */
+#define SAI_CLR_FCR BIT(3) /* TODO: what is this? */
+#define SAI_CLR_FSC BIT(2)
+#define SAI_CLR_RXC BIT(1)
+#define SAI_CLR_TXC BIT(0)
+
+/* CKR Clock Generation Register */
+#define SAI_CKR_MDIV_MASK GENMASK(14, 3)
+#define SAI_CKR_MDIV(x) ((x - 1) << 3)
+#define SAI_CKR_MSS_MASK BIT(2)
+#define SAI_CKR_MSS_SLAVE BIT(2)
+#define SAI_CKR_MSS_MASTER 0
+#define SAI_CKR_CKP_MASK BIT(1)
+#define SAI_CKR_CKP_INVERTED BIT(1)
+#define SAI_CKR_CKP_NORMAL 0
+#define SAI_CKR_FSP_MASK BIT(0)
+#define SAI_CKR_FSP_INVERTED BIT(0)
+#define SAI_CKR_FSP_NORMAL 0
+
+/* DMACR DMA Control Register */
+#define SAI_DMACR_RDE_MASK BIT(24)
+#define SAI_DMACR_RDE(x) ((x) << 24)
+#define SAI_DMACR_RDL_MASK GENMASK(20, 16)
+#define SAI_DMACR_RDL(x) ((x - 1) << 16)
+#define SAI_DMACR_RDL_V(v) ((((v) & SAI_DMACR_RDL_MASK) >> 16) + 1)
+#define SAI_DMACR_TDE_MASK BIT(8)
+#define SAI_DMACR_TDE(x) ((x) << 8)
+#define SAI_DMACR_TDL_MASK GENMASK(4, 0)
+#define SAI_DMACR_TDL(x) ((x) << 0)
+#define SAI_DMACR_TDL_V(v) (((v) & SAI_DMACR_TDL_MASK) >> 0)
+
+/* INTCR Interrupt Ctrl Register */
+#define SAI_INTCR_FSLOSTC BIT(28)
+#define SAI_INTCR_FSLOST_MASK BIT(27)
+#define SAI_INTCR_FSLOST(x) ((x) << 27)
+#define SAI_INTCR_FSERRC BIT(26)
+#define SAI_INTCR_FSERR_MASK BIT(25)
+#define SAI_INTCR_FSERR(x) ((x) << 25)
+#define SAI_INTCR_RXOIC BIT(18)
+#define SAI_INTCR_RXOIE_MASK BIT(17)
+#define SAI_INTCR_RXOIE(x) ((x) << 17)
+#define SAI_INTCR_TXUIC BIT(2)
+#define SAI_INTCR_TXUIE_MASK BIT(1)
+#define SAI_INTCR_TXUIE(x) ((x) << 1)
+
+/* INTSR Interrupt Status Register */
+#define SAI_INTSR_FSLOSTI_INA 0
+#define SAI_INTSR_FSLOSTI_ACT BIT(19)
+#define SAI_INTSR_FSERRI_INA 0
+#define SAI_INTSR_FSERRI_ACT BIT(18)
+#define SAI_INTSR_RXOI_INA 0
+#define SAI_INTSR_RXOI_ACT BIT(17)
+#define SAI_INTSR_TXUI_INA 0
+#define SAI_INTSR_TXUI_ACT BIT(1)
+
+/* PATH_SEL: Transfer / Receive Path Select Register */
+#define SAI_RX_PATH_SHIFT(x) (8 + (x) * 2)
+#define SAI_RX_PATH_MASK(x) (0x3 << SAI_RX_PATH_SHIFT(x))
+#define SAI_RX_PATH(x, v) ((v) << SAI_RX_PATH_SHIFT(x))
+#define SAI_TX_PATH_SHIFT(x) (0 + (x) * 2)
+#define SAI_TX_PATH_MASK(x) (0x3 << SAI_TX_PATH_SHIFT(x))
+#define SAI_TX_PATH(x, v) ((v) << SAI_TX_PATH_SHIFT(x))
+
+/* XSHIFT: Transfer / Receive Frame Sync Shift Register */
+
+/*
+ * TX-ONLY: LEFT Direction Feature
+ * +------------------------------------------------+
+ * | DATA LEFTx (step: 0.5 cycle) | FSYNC Edge |
+ * +------------------------------------------------+
+ */
+#define SAI_XSHIFT_LEFT_MASK GENMASK(25, 24)
+#define SAI_XSHIFT_LEFT(x) ((x) << 24)
+/*
+ * +------------------------------------------------+
+ * | FSYNC Edge | DATA RIGHTx (step: 0.5 cycle) |
+ * +------------------------------------------------+
+ */
+#define SAI_XSHIFT_RIGHT_MASK GENMASK(23, 0)
+#define SAI_XSHIFT_RIGHT(x) (x)
+
+/* XFIFOLR: Transfer / Receive FIFO Level Register */
+#define SAI_FIFOLR_XFL3_SHIFT 18
+#define SAI_FIFOLR_XFL3_MASK GENMASK(23, 18)
+#define SAI_FIFOLR_XFL2_SHIFT 12
+#define SAI_FIFOLR_XFL2_MASK GENMASK(17, 12)
+#define SAI_FIFOLR_XFL1_SHIFT 6
+#define SAI_FIFOLR_XFL1_MASK GENMASK(11, 6)
+#define SAI_FIFOLR_XFL0_SHIFT 0
+#define SAI_FIFOLR_XFL0_MASK GENMASK(5, 0)
+
+/* STATUS Status Register (VERSION >= SAI_VER_2307) */
+#define SAI_STATUS_RX_IDLE BIT(3)
+#define SAI_STATUS_TX_IDLE BIT(2)
+#define SAI_STATUS_FS_IDLE BIT(1)
+
+/* VERSION */
+/*
+ * Updates:
+ *
+ * VERSION >= SAI_VER_2311
+ *
+ * Support Frame Sync xN (FSXN)
+ * Support Frame Sync Error Detect (FSE)
+ * Support Frame Sync Lost Detect (FSLOST)
+ * Support Force Clear (FCR)
+ * Support SAIn-Chained (e.g. SAI0-CLK-DATA + SAI3-DATA +...)
+ * Support Transmit Auto Gate Mode
+ * Support Timing Shift Left for TX
+ *
+ * Optimize SCLK/FSYNC Timing Alignment
+ *
+ * VERSION >= SAI_VER_2403
+ *
+ * Support Loopback LR Select (e.g. L:MIC R:LP)
+ *
+ */
+#define SAI_VER_2307 0x23073576
+#define SAI_VER_2311 0x23112118
+#define SAI_VER_2401 0x24013506
+#define SAI_VER_2403 0x24031103
+
+/* FS_TIMEOUT: Frame Sync Timeout Register */
+#define SAI_FS_TIMEOUT_VAL_MASK GENMASK(31, 1)
+#define SAI_FS_TIMEOUT_VAL(x) ((x) << 1)
+#define SAI_FS_TIMEOUT_EN_MASK BIT(0)
+#define SAI_FS_TIMEOUT_EN(x) ((x) << 0)
+
+/* SAI Registers */
+#define SAI_TXCR (0x0000)
+#define SAI_FSCR (0x0004)
+#define SAI_RXCR (0x0008)
+#define SAI_MONO_CR (0x000c)
+#define SAI_XFER (0x0010)
+#define SAI_CLR (0x0014)
+#define SAI_CKR (0x0018)
+#define SAI_TXFIFOLR (0x001c)
+#define SAI_RXFIFOLR (0x0020)
+#define SAI_DMACR (0x0024)
+#define SAI_INTCR (0x0028)
+#define SAI_INTSR (0x002c)
+#define SAI_TXDR (0x0030)
+#define SAI_RXDR (0x0034)
+#define SAI_PATH_SEL (0x0038)
+#define SAI_TX_SLOT_MASK0 (0x003c)
+#define SAI_TX_SLOT_MASK1 (0x0040)
+#define SAI_TX_SLOT_MASK2 (0x0044)
+#define SAI_TX_SLOT_MASK3 (0x0048)
+#define SAI_RX_SLOT_MASK0 (0x004c)
+#define SAI_RX_SLOT_MASK1 (0x0050)
+#define SAI_RX_SLOT_MASK2 (0x0054)
+#define SAI_RX_SLOT_MASK3 (0x0058)
+#define SAI_TX_DATA_CNT (0x005c)
+#define SAI_RX_DATA_CNT (0x0060)
+#define SAI_TX_SHIFT (0x0064)
+#define SAI_RX_SHIFT (0x0068)
+#define SAI_STATUS (0x006c)
+#define SAI_VERSION (0x0070)
+#define SAI_FSXN (0x0074)
+#define SAI_FS_TIMEOUT (0x0078)
+#define SAI_LOOPBACK_LR (0x007c)
+
+#endif /* _ROCKCHIP_SAI_H */
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 60b4b7b75215..ec7204f57fd4 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_SOC_SAMSUNG
- tristate "ASoC support for Samsung"
+ tristate "Samsung"
depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
depends on COMMON_CLK
select SND_SOC_GENERIC_DMAENGINE_PCM
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c
index 8dc3b2da4c8f..61ed5e69391a 100644
--- a/sound/soc/samsung/bells.c
+++ b/sound/soc/samsung/bells.c
@@ -133,8 +133,6 @@ static int bells_set_bias_level_post(struct snd_soc_card *card,
break;
}
- dapm->bias_level = level;
-
return 0;
}
diff --git a/sound/soc/samsung/littlemill.c b/sound/soc/samsung/littlemill.c
index 5a02aac9b423..c8b06894ac88 100644
--- a/sound/soc/samsung/littlemill.c
+++ b/sound/soc/samsung/littlemill.c
@@ -95,8 +95,6 @@ static int littlemill_set_bias_level_post(struct snd_soc_card *card,
break;
}
- dapm->bias_level = level;
-
return 0;
}
diff --git a/sound/soc/samsung/speyside.c b/sound/soc/samsung/speyside.c
index 9262e5626584..f4cc5684ef0a 100644
--- a/sound/soc/samsung/speyside.c
+++ b/sound/soc/samsung/speyside.c
@@ -90,8 +90,6 @@ static int speyside_set_bias_level_post(struct snd_soc_card *card,
break;
}
- card->dapm.bias_level = level;
-
return 0;
}
diff --git a/sound/soc/samsung/tobermory.c b/sound/soc/samsung/tobermory.c
index d0f0c01365aa..1d0a782402f0 100644
--- a/sound/soc/samsung/tobermory.c
+++ b/sound/soc/samsung/tobermory.c
@@ -91,8 +91,6 @@ static int tobermory_set_bias_level_post(struct snd_soc_card *card,
break;
}
- dapm->bias_level = level;
-
return 0;
}
diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig
index ee20b9914aa1..6a3ba43f26bd 100644
--- a/sound/soc/sdca/Kconfig
+++ b/sound/soc/sdca/Kconfig
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "SoundWire (SDCA)"
config SND_SOC_SDCA
tristate
@@ -7,5 +8,24 @@ config SND_SOC_SDCA
This option enables support for the MIPI SoundWire Device
Class for Audio (SDCA).
+config SND_SOC_SDCA_HID
+ bool "SDCA HID support"
+ depends on SND_SOC_SDCA
+ depends on HID=y || HID=SND_SOC_SDCA
+ default y
+ help
+ This option enables support for audio jack button reporting using HID.
+
+config SND_SOC_SDCA_IRQ
+ bool "SDCA IRQ support"
+ select REGMAP
+ select REGMAP_IRQ
+ depends on SND_SOC_SDCA
+ default y
+ help
+ This option enables support for SDCA IRQs.
+
config SND_SOC_SDCA_OPTIONAL
def_tristate SND_SOC_SDCA || !SND_SOC_SDCA
+
+endmenu
diff --git a/sound/soc/sdca/Makefile b/sound/soc/sdca/Makefile
index dddc3e694256..5e51760cb651 100644
--- a/sound/soc/sdca/Makefile
+++ b/sound/soc/sdca/Makefile
@@ -1,5 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
-snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o
+snd-soc-sdca-y := sdca_functions.o sdca_device.o sdca_regmap.o sdca_asoc.o
+snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_HID) += sdca_hid.o
+snd-soc-sdca-$(CONFIG_SND_SOC_SDCA_IRQ) += sdca_interrupts.o
-obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o
+obj-$(CONFIG_SND_SOC_SDCA) += snd-soc-sdca.o
diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c
new file mode 100644
index 000000000000..c493ec530cc5
--- /dev/null
+++ b/sound/soc/sdca/sdca_asoc.c
@@ -0,0 +1,1616 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/bits.h>
+#include <linux/bitmap.h>
+#include <linux/build_bug.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/minmax.h>
+#include <linux/module.h>
+#include <linux/overflow.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/string_helpers.h>
+#include <linux/types.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/sdca.h>
+#include <sound/sdca_asoc.h>
+#include <sound/sdca_function.h>
+#include <sound/soc.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+static bool exported_control(struct sdca_entity *entity, struct sdca_control *control)
+{
+ switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
+ case SDCA_CTL_TYPE_S(GE, DETECTED_MODE):
+ return true;
+ default:
+ break;
+ }
+
+ return control->layers & (SDCA_ACCESS_LAYER_USER |
+ SDCA_ACCESS_LAYER_APPLICATION);
+}
+
+static bool readonly_control(struct sdca_control *control)
+{
+ return control->has_fixed || control->mode == SDCA_ACCESS_MODE_RO;
+}
+
+/**
+ * sdca_asoc_count_component - count the various component parts
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @num_widgets: Output integer pointer, will be filled with the
+ * required number of DAPM widgets for the Function.
+ * @num_routes: Output integer pointer, will be filled with the
+ * required number of DAPM routes for the Function.
+ * @num_controls: Output integer pointer, will be filled with the
+ * required number of ALSA controls for the Function.
+ * @num_dais: Output integer pointer, will be filled with the
+ * required number of ASoC DAIs for the Function.
+ *
+ * This function counts various things within the SDCA Function such
+ * that the calling driver can allocate appropriate space before
+ * calling the appropriate population functions.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_count_component(struct device *dev, struct sdca_function_data *function,
+ int *num_widgets, int *num_routes, int *num_controls,
+ int *num_dais)
+{
+ int i, j;
+
+ *num_widgets = function->num_entities - 1;
+ *num_routes = 0;
+ *num_controls = 0;
+ *num_dais = 0;
+
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ /* Add supply/DAI widget connections */
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ *num_routes += !!entity->iot.clock;
+ *num_routes += !!entity->iot.is_dataport;
+ *num_controls += !entity->iot.is_dataport;
+ *num_dais += !!entity->iot.is_dataport;
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ *num_routes += entity->pde.num_managed;
+ break;
+ default:
+ break;
+ }
+
+ if (entity->group)
+ (*num_routes)++;
+
+ /* Add primary entity connections from DisCo */
+ *num_routes += entity->num_sources;
+
+ for (j = 0; j < entity->num_controls; j++) {
+ if (exported_control(entity, &entity->controls[j]))
+ (*num_controls)++;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_count_component, "SND_SOC_SDCA");
+
+static const char *get_terminal_name(enum sdca_terminal_type type)
+{
+ switch (type) {
+ case SDCA_TERM_TYPE_LINEIN_STEREO:
+ return SDCA_TERM_TYPE_LINEIN_STEREO_NAME;
+ case SDCA_TERM_TYPE_LINEIN_FRONT_LR:
+ return SDCA_TERM_TYPE_LINEIN_FRONT_LR_NAME;
+ case SDCA_TERM_TYPE_LINEIN_CENTER_LFE:
+ return SDCA_TERM_TYPE_LINEIN_CENTER_LFE_NAME;
+ case SDCA_TERM_TYPE_LINEIN_SURROUND_LR:
+ return SDCA_TERM_TYPE_LINEIN_SURROUND_LR_NAME;
+ case SDCA_TERM_TYPE_LINEIN_REAR_LR:
+ return SDCA_TERM_TYPE_LINEIN_REAR_LR_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_STEREO:
+ return SDCA_TERM_TYPE_LINEOUT_STEREO_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_FRONT_LR:
+ return SDCA_TERM_TYPE_LINEOUT_FRONT_LR_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_CENTER_LFE:
+ return SDCA_TERM_TYPE_LINEOUT_CENTER_LFE_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_SURROUND_LR:
+ return SDCA_TERM_TYPE_LINEOUT_SURROUND_LR_NAME;
+ case SDCA_TERM_TYPE_LINEOUT_REAR_LR:
+ return SDCA_TERM_TYPE_LINEOUT_REAR_LR_NAME;
+ case SDCA_TERM_TYPE_MIC_JACK:
+ return SDCA_TERM_TYPE_MIC_JACK_NAME;
+ case SDCA_TERM_TYPE_STEREO_JACK:
+ return SDCA_TERM_TYPE_STEREO_JACK_NAME;
+ case SDCA_TERM_TYPE_FRONT_LR_JACK:
+ return SDCA_TERM_TYPE_FRONT_LR_JACK_NAME;
+ case SDCA_TERM_TYPE_CENTER_LFE_JACK:
+ return SDCA_TERM_TYPE_CENTER_LFE_JACK_NAME;
+ case SDCA_TERM_TYPE_SURROUND_LR_JACK:
+ return SDCA_TERM_TYPE_SURROUND_LR_JACK_NAME;
+ case SDCA_TERM_TYPE_REAR_LR_JACK:
+ return SDCA_TERM_TYPE_REAR_LR_JACK_NAME;
+ case SDCA_TERM_TYPE_HEADPHONE_JACK:
+ return SDCA_TERM_TYPE_HEADPHONE_JACK_NAME;
+ case SDCA_TERM_TYPE_HEADSET_JACK:
+ return SDCA_TERM_TYPE_HEADSET_JACK_NAME;
+ default:
+ return NULL;
+ }
+}
+
+static int entity_early_parse_ge(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity)
+{
+ struct sdca_control_range *range;
+ struct sdca_control *control;
+ struct snd_kcontrol_new *kctl;
+ struct soc_enum *soc_enum;
+ const char *control_name;
+ unsigned int *values;
+ const char **texts;
+ int i;
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_GE_SELECTED_MODE);
+ if (!control)
+ return -EINVAL;
+
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ range = sdca_control_find_range(dev, entity, control, SDCA_SELECTED_MODE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, control->label);
+ if (!control_name)
+ return -ENOMEM;
+
+ kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL);
+ if (!kctl)
+ return -ENOMEM;
+
+ soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL);
+ if (!soc_enum)
+ return -ENOMEM;
+
+ texts = devm_kcalloc(dev, range->rows + 3, sizeof(*texts), GFP_KERNEL);
+ if (!texts)
+ return -ENOMEM;
+
+ values = devm_kcalloc(dev, range->rows + 3, sizeof(*values), GFP_KERNEL);
+ if (!values)
+ return -ENOMEM;
+
+ texts[0] = "Jack Unplugged";
+ texts[1] = "Jack Unknown";
+ texts[2] = "Detection in Progress";
+ values[0] = SDCA_DETECTED_MODE_JACK_UNPLUGGED;
+ values[1] = SDCA_DETECTED_MODE_JACK_UNKNOWN;
+ values[2] = SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS;
+ for (i = 0; i < range->rows; i++) {
+ enum sdca_terminal_type type;
+
+ type = sdca_range(range, SDCA_SELECTED_MODE_TERM_TYPE, i);
+
+ values[i + 3] = sdca_range(range, SDCA_SELECTED_MODE_INDEX, i);
+ texts[i + 3] = get_terminal_name(type);
+ if (!texts[i + 3]) {
+ dev_err(dev, "%s: unrecognised terminal type: %#x\n",
+ entity->label, type);
+ return -EINVAL;
+ }
+ }
+
+ soc_enum->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ soc_enum->items = range->rows + 3;
+ soc_enum->mask = roundup_pow_of_two(soc_enum->items) - 1;
+ soc_enum->texts = texts;
+ soc_enum->values = values;
+
+ kctl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl->name = control_name;
+ kctl->info = snd_soc_info_enum_double;
+ kctl->get = snd_soc_dapm_get_enum_double;
+ kctl->put = snd_soc_dapm_put_enum_double;
+ kctl->private_value = (unsigned long)soc_enum;
+
+ entity->ge.kctl = kctl;
+
+ return 0;
+}
+
+static void add_route(struct snd_soc_dapm_route **route, const char *sink,
+ const char *control, const char *source)
+{
+ (*route)->sink = sink;
+ (*route)->control = control;
+ (*route)->source = source;
+ (*route)++;
+}
+
+static int entity_parse_simple(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route,
+ enum snd_soc_dapm_type id)
+{
+ int i;
+
+ (*widget)->id = id;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ return 0;
+}
+
+static int entity_parse_it(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ int i;
+
+ if (entity->iot.is_dataport) {
+ const char *aif_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, "Playback");
+ if (!aif_name)
+ return -ENOMEM;
+
+ (*widget)->id = snd_soc_dapm_aif_in;
+
+ add_route(route, entity->label, NULL, aif_name);
+ } else {
+ (*widget)->id = snd_soc_dapm_mic;
+ }
+
+ if (entity->iot.clock)
+ add_route(route, entity->label, NULL, entity->iot.clock->label);
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ (*widget)++;
+
+ return 0;
+}
+
+static int entity_parse_ot(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ int i;
+
+ if (entity->iot.is_dataport) {
+ const char *aif_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, "Capture");
+ if (!aif_name)
+ return -ENOMEM;
+
+ (*widget)->id = snd_soc_dapm_aif_out;
+
+ add_route(route, aif_name, NULL, entity->label);
+ } else {
+ (*widget)->id = snd_soc_dapm_spk;
+ }
+
+ if (entity->iot.clock)
+ add_route(route, entity->label, NULL, entity->iot.clock->label);
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ (*widget)++;
+
+ return 0;
+}
+
+static int entity_pde_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kctl, int event)
+{
+ struct snd_soc_component *component = widget->dapm->component;
+ struct sdca_entity *entity = widget->priv;
+ static const int polls = 100;
+ unsigned int reg, val;
+ int from, to, i;
+ int poll_us;
+ int ret;
+
+ if (!component)
+ return -EIO;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMD:
+ from = widget->on_val;
+ to = widget->off_val;
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ from = widget->off_val;
+ to = widget->on_val;
+ break;
+ default:
+ return 0;
+ }
+
+ for (i = 0; i < entity->pde.num_max_delay; i++) {
+ struct sdca_pde_delay *delay = &entity->pde.max_delay[i];
+
+ if (delay->from_ps == from && delay->to_ps == to) {
+ poll_us = delay->us / polls;
+ break;
+ }
+ }
+
+ reg = SDW_SDCA_CTL(SDW_SDCA_CTL_FUNC(widget->reg),
+ SDW_SDCA_CTL_ENT(widget->reg),
+ SDCA_CTL_PDE_ACTUAL_PS, 0);
+
+ for (i = 0; i < polls; i++) {
+ if (i)
+ fsleep(poll_us);
+
+ ret = regmap_read(component->regmap, reg, &val);
+ if (ret)
+ return ret;
+ else if (val == to)
+ return 0;
+ }
+
+ dev_err(component->dev, "%s: power transition failed: %x\n",
+ entity->label, val);
+ return -ETIMEDOUT;
+}
+
+static int entity_parse_pde(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ unsigned int target = (1 << SDCA_PDE_PS0) | (1 << SDCA_PDE_PS3);
+ struct sdca_control_range *range;
+ struct sdca_control *control;
+ unsigned int mask = 0;
+ int i;
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_PDE_REQUESTED_PS);
+ if (!control)
+ return -EINVAL;
+
+ /* Power should only be controlled by the driver */
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ range = sdca_control_find_range(dev, entity, control, SDCA_REQUESTED_PS_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++)
+ mask |= 1 << sdca_range(range, SDCA_REQUESTED_PS_STATE, i);
+
+ if ((mask & target) != target) {
+ dev_err(dev, "%s: power control missing states\n", entity->label);
+ return -EINVAL;
+ }
+
+ (*widget)->id = snd_soc_dapm_supply;
+ (*widget)->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ (*widget)->mask = GENMASK(control->nbits - 1, 0);
+ (*widget)->on_val = SDCA_PDE_PS0;
+ (*widget)->off_val = SDCA_PDE_PS3;
+ (*widget)->event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD;
+ (*widget)->event = entity_pde_event;
+ (*widget)->priv = entity;
+ (*widget)++;
+
+ for (i = 0; i < entity->pde.num_managed; i++)
+ add_route(route, entity->pde.managed[i]->label, NULL, entity->label);
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ return 0;
+}
+
+/* Device selector units are controlled through a group entity */
+static int entity_parse_su_device(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct sdca_control_range *range;
+ int num_routes = 0;
+ int i, j;
+
+ if (!entity->group) {
+ dev_err(dev, "%s: device selector unit missing group\n", entity->label);
+ return -EINVAL;
+ }
+
+ range = sdca_selector_find_range(dev, entity->group, SDCA_CTL_GE_SELECTED_MODE,
+ SDCA_SELECTED_MODE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ (*widget)->id = snd_soc_dapm_mux;
+ (*widget)->kcontrol_news = entity->group->ge.kctl;
+ (*widget)->num_kcontrols = 1;
+ (*widget)++;
+
+ for (i = 0; i < entity->group->ge.num_modes; i++) {
+ struct sdca_ge_mode *mode = &entity->group->ge.modes[i];
+
+ for (j = 0; j < mode->num_controls; j++) {
+ struct sdca_ge_control *affected = &mode->controls[j];
+ int term;
+
+ if (affected->id != entity->id ||
+ affected->sel != SDCA_CTL_SU_SELECTOR ||
+ !affected->val)
+ continue;
+
+ if (affected->val - 1 >= entity->num_sources) {
+ dev_err(dev, "%s: bad control value: %#x\n",
+ entity->label, affected->val);
+ return -EINVAL;
+ }
+
+ if (++num_routes > entity->num_sources) {
+ dev_err(dev, "%s: too many input routes\n", entity->label);
+ return -EINVAL;
+ }
+
+ term = sdca_range_search(range, SDCA_SELECTED_MODE_INDEX,
+ mode->val, SDCA_SELECTED_MODE_TERM_TYPE);
+ if (!term) {
+ dev_err(dev, "%s: mode not found: %#x\n",
+ entity->label, mode->val);
+ return -EINVAL;
+ }
+
+ add_route(route, entity->label, get_terminal_name(term),
+ entity->sources[affected->val - 1]->label);
+ }
+ }
+
+ return 0;
+}
+
+/* Class selector units will be exported as an ALSA control */
+static int entity_parse_su_class(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct snd_kcontrol_new *kctl;
+ struct soc_enum *soc_enum;
+ const char **texts;
+ int i;
+
+ kctl = devm_kzalloc(dev, sizeof(*kctl), GFP_KERNEL);
+ if (!kctl)
+ return -ENOMEM;
+
+ soc_enum = devm_kzalloc(dev, sizeof(*soc_enum), GFP_KERNEL);
+ if (!soc_enum)
+ return -ENOMEM;
+
+ texts = devm_kcalloc(dev, entity->num_sources + 1, sizeof(*texts), GFP_KERNEL);
+ if (!texts)
+ return -ENOMEM;
+
+ texts[0] = "No Signal";
+ for (i = 0; i < entity->num_sources; i++)
+ texts[i + 1] = entity->sources[i]->label;
+
+ soc_enum->reg = SDW_SDCA_CTL(function->desc->adr, entity->id, control->sel, 0);
+ soc_enum->items = entity->num_sources + 1;
+ soc_enum->mask = roundup_pow_of_two(soc_enum->items) - 1;
+ soc_enum->texts = texts;
+
+ kctl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl->name = "Route";
+ kctl->info = snd_soc_info_enum_double;
+ kctl->get = snd_soc_dapm_get_enum_double;
+ kctl->put = snd_soc_dapm_put_enum_double;
+ kctl->private_value = (unsigned long)soc_enum;
+
+ (*widget)->id = snd_soc_dapm_mux;
+ (*widget)->kcontrol_news = kctl;
+ (*widget)->num_kcontrols = 1;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, texts[i + 1], entity->sources[i]->label);
+
+ return 0;
+}
+
+static int entity_parse_su(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct sdca_control *control;
+
+ if (!entity->num_sources) {
+ dev_err(dev, "%s: selector with no inputs\n", entity->label);
+ return -EINVAL;
+ }
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_SU_SELECTOR);
+ if (!control)
+ return -EINVAL;
+
+ if (control->layers == SDCA_ACCESS_LAYER_DEVICE)
+ return entity_parse_su_device(dev, function, entity, widget, route);
+
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ return entity_parse_su_class(dev, function, entity, control, widget, route);
+}
+
+static int entity_parse_mu(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ struct sdca_control *control;
+ struct snd_kcontrol_new *kctl;
+ int i;
+
+ if (!entity->num_sources) {
+ dev_err(dev, "%s: selector 1 or more inputs\n", entity->label);
+ return -EINVAL;
+ }
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_MU_MIXER);
+ if (!control)
+ return -EINVAL;
+
+ /* MU control should be through DAPM */
+ if (control->layers != SDCA_ACCESS_LAYER_CLASS)
+ dev_warn(dev, "%s: unexpected access layer: %x\n",
+ entity->label, control->layers);
+
+ kctl = devm_kcalloc(dev, entity->num_sources, sizeof(*kctl), GFP_KERNEL);
+ if (!kctl)
+ return -ENOMEM;
+
+ for (i = 0; i < entity->num_sources; i++) {
+ const char *control_name;
+ struct soc_mixer_control *mc;
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %d",
+ control->label, i + 1);
+ if (!control_name)
+ return -ENOMEM;
+
+ mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ mc->reg = SND_SOC_NOPM;
+ mc->rreg = SND_SOC_NOPM;
+ mc->invert = 1; // Ensure default is connected
+ mc->min = 0;
+ mc->max = 1;
+
+ kctl[i].name = control_name;
+ kctl[i].private_value = (unsigned long)mc;
+ kctl[i].iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ kctl[i].info = snd_soc_info_volsw;
+ kctl[i].get = snd_soc_dapm_get_volsw;
+ kctl[i].put = snd_soc_dapm_put_volsw;
+ }
+
+ (*widget)->id = snd_soc_dapm_mixer;
+ (*widget)->kcontrol_news = kctl;
+ (*widget)->num_kcontrols = entity->num_sources;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, kctl[i].name, entity->sources[i]->label);
+
+ return 0;
+}
+
+static int entity_cs_event(struct snd_soc_dapm_widget *widget,
+ struct snd_kcontrol *kctl, int event)
+{
+ struct snd_soc_component *component = widget->dapm->component;
+ struct sdca_entity *entity = widget->priv;
+
+ if (!component)
+ return -EIO;
+
+ if (entity->cs.max_delay)
+ fsleep(entity->cs.max_delay);
+
+ return 0;
+}
+
+static int entity_parse_cs(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_dapm_widget **widget,
+ struct snd_soc_dapm_route **route)
+{
+ int i;
+
+ (*widget)->id = snd_soc_dapm_supply;
+ (*widget)->subseq = 1; /* Ensure these run after PDEs */
+ (*widget)->event_flags = SND_SOC_DAPM_POST_PMU;
+ (*widget)->event = entity_cs_event;
+ (*widget)->priv = entity;
+ (*widget)++;
+
+ for (i = 0; i < entity->num_sources; i++)
+ add_route(route, entity->label, NULL, entity->sources[i]->label);
+
+ return 0;
+}
+
+/**
+ * sdca_asoc_populate_dapm - fill in arrays of DAPM widgets and routes
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @widget: Array of DAPM widgets to be populated.
+ * @route: Array of DAPM routes to be populated.
+ *
+ * This function populates arrays of DAPM widgets and routes from the
+ * DisCo information for a particular SDCA Function. Typically,
+ * snd_soc_asoc_count_component will be used to allocate appropriately
+ * sized arrays before calling this function.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_dapm(struct device *dev, struct sdca_function_data *function,
+ struct snd_soc_dapm_widget *widget,
+ struct snd_soc_dapm_route *route)
+{
+ int ret;
+ int i;
+
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ /*
+ * Some entities need to add controls "early" as they are
+ * referenced by other entities.
+ */
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_GE:
+ ret = entity_early_parse_ge(dev, function, entity);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ widget->name = entity->label;
+ widget->reg = SND_SOC_NOPM;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ ret = entity_parse_it(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ ret = entity_parse_ot(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_PDE:
+ ret = entity_parse_pde(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_SU:
+ ret = entity_parse_su(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_MU:
+ ret = entity_parse_mu(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_CS:
+ ret = entity_parse_cs(dev, function, entity, &widget, &route);
+ break;
+ case SDCA_ENTITY_TYPE_CX:
+ /*
+ * FIXME: For now we will just treat these as a supply,
+ * meaning all options are enabled.
+ */
+ dev_warn(dev, "%s: clock selectors not fully supported yet\n",
+ entity->label);
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_supply);
+ break;
+ case SDCA_ENTITY_TYPE_TG:
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_siggen);
+ break;
+ case SDCA_ENTITY_TYPE_GE:
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_supply);
+ break;
+ default:
+ ret = entity_parse_simple(dev, function, entity, &widget,
+ &route, snd_soc_dapm_pga);
+ break;
+ }
+ if (ret)
+ return ret;
+
+ if (entity->group)
+ add_route(&route, entity->label, NULL, entity->group->label);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_dapm, "SND_SOC_SDCA");
+
+static int control_limit_kctl(struct device *dev,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct snd_kcontrol_new *kctl)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
+ struct sdca_control_range *range;
+ int min, max, step;
+ unsigned int *tlv;
+ int shift;
+
+ if (control->type != SDCA_CTL_DATATYPE_Q7P8DB)
+ return 0;
+
+ /*
+ * FIXME: For now only handle the simple case of a single linear range
+ */
+ range = sdca_control_find_range(dev, entity, control, SDCA_VOLUME_LINEAR_NCOLS, 1);
+ if (!range)
+ return -EINVAL;
+
+ min = sdca_range(range, SDCA_VOLUME_LINEAR_MIN, 0);
+ max = sdca_range(range, SDCA_VOLUME_LINEAR_MAX, 0);
+ step = sdca_range(range, SDCA_VOLUME_LINEAR_STEP, 0);
+
+ min = sign_extend32(min, control->nbits - 1);
+ max = sign_extend32(max, control->nbits - 1);
+
+ /*
+ * FIXME: Only support power of 2 step sizes as this can be supported
+ * by a simple shift.
+ */
+ if (hweight32(step) != 1) {
+ dev_err(dev, "%s: %s: currently unsupported step size\n",
+ entity->label, control->label);
+ return -EINVAL;
+ }
+
+ /*
+ * The SDCA volumes are in steps of 1/256th of a dB, a step down of
+ * 64 (shift of 6) gives 1/4dB. 1/4dB is the smallest unit that is also
+ * representable in the ALSA TLVs which are in 1/100ths of a dB.
+ */
+ shift = max(ffs(step) - 1, 6);
+
+ tlv = devm_kcalloc(dev, 4, sizeof(*tlv), GFP_KERNEL);
+ if (!tlv)
+ return -ENOMEM;
+
+ tlv[0] = SNDRV_CTL_TLVT_DB_SCALE;
+ tlv[1] = 2 * sizeof(*tlv);
+ tlv[2] = (min * 100) >> 8;
+ tlv[3] = ((1 << shift) * 100) >> 8;
+
+ mc->min = min >> shift;
+ mc->max = max >> shift;
+ mc->shift = shift;
+ mc->rshift = shift;
+ mc->sign_bit = 15 - shift;
+
+ kctl->tlv.p = tlv;
+ kctl->access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+
+ return 0;
+}
+
+static int populate_control(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct snd_kcontrol_new **kctl)
+{
+ const char *control_suffix = "";
+ const char *control_name;
+ struct soc_mixer_control *mc;
+ int index = 0;
+ int ret;
+ int cn;
+
+ if (!exported_control(entity, control))
+ return 0;
+
+ if (control->type == SDCA_CTL_DATATYPE_ONEBIT)
+ control_suffix = " Switch";
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s%s", entity->label,
+ control->label, control_suffix);
+ if (!control_name)
+ return -ENOMEM;
+
+ mc = devm_kzalloc(dev, sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ return -ENOMEM;
+
+ for_each_set_bit(cn, (unsigned long *)&control->cn_list,
+ BITS_PER_TYPE(control->cn_list)) {
+ switch (index++) {
+ case 0:
+ mc->reg = SDW_SDCA_CTL(function->desc->adr, entity->id,
+ control->sel, cn);
+ mc->rreg = mc->reg;
+ break;
+ case 1:
+ mc->rreg = SDW_SDCA_CTL(function->desc->adr, entity->id,
+ control->sel, cn);
+ break;
+ default:
+ dev_err(dev, "%s: %s: only mono/stereo controls supported\n",
+ entity->label, control->label);
+ return -EINVAL;
+ }
+ }
+
+ mc->min = 0;
+ mc->max = clamp((0x1ull << control->nbits) - 1, 0, type_max(mc->max));
+
+ (*kctl)->name = control_name;
+ (*kctl)->private_value = (unsigned long)mc;
+ (*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ (*kctl)->info = snd_soc_info_volsw;
+ (*kctl)->get = snd_soc_get_volsw;
+ (*kctl)->put = snd_soc_put_volsw;
+
+ if (readonly_control(control))
+ (*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READ;
+ else
+ (*kctl)->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+
+ ret = control_limit_kctl(dev, entity, control, *kctl);
+ if (ret)
+ return ret;
+
+ (*kctl)++;
+
+ return 0;
+}
+
+static int populate_pin_switch(struct device *dev,
+ struct sdca_entity *entity,
+ struct snd_kcontrol_new **kctl)
+{
+ const char *control_name;
+
+ control_name = devm_kasprintf(dev, GFP_KERNEL, "%s Switch", entity->label);
+ if (!control_name)
+ return -ENOMEM;
+
+ (*kctl)->name = control_name;
+ (*kctl)->private_value = (unsigned long)entity->label;
+ (*kctl)->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ (*kctl)->info = snd_soc_dapm_info_pin_switch;
+ (*kctl)->get = snd_soc_dapm_get_component_pin_switch;
+ (*kctl)->put = snd_soc_dapm_put_component_pin_switch;
+ (*kctl)++;
+
+ return 0;
+}
+
+/**
+ * sdca_asoc_populate_controls - fill in an array of ALSA controls for a Function
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @kctl: Array of ALSA controls to be populated.
+ *
+ * This function populates an array of ALSA controls from the DisCo
+ * information for a particular SDCA Function. Typically,
+ * snd_soc_asoc_count_component will be used to allocate an
+ * appropriately sized array before calling this function.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_controls(struct device *dev,
+ struct sdca_function_data *function,
+ struct snd_kcontrol_new *kctl)
+{
+ int i, j;
+ int ret;
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ case SDCA_ENTITY_TYPE_OT:
+ if (!entity->iot.is_dataport) {
+ ret = populate_pin_switch(dev, entity, &kctl);
+ if (ret)
+ return ret;
+ }
+ break;
+ default:
+ break;
+ }
+
+ for (j = 0; j < entity->num_controls; j++) {
+ ret = populate_control(dev, function, entity,
+ &entity->controls[j], &kctl);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_controls, "SND_SOC_SDCA");
+
+static unsigned int rate_find_mask(unsigned int rate)
+{
+ switch (rate) {
+ case 0:
+ return SNDRV_PCM_RATE_8000_768000;
+ case 5512:
+ return SNDRV_PCM_RATE_5512;
+ case 8000:
+ return SNDRV_PCM_RATE_8000;
+ case 11025:
+ return SNDRV_PCM_RATE_11025;
+ case 16000:
+ return SNDRV_PCM_RATE_16000;
+ case 22050:
+ return SNDRV_PCM_RATE_22050;
+ case 32000:
+ return SNDRV_PCM_RATE_32000;
+ case 44100:
+ return SNDRV_PCM_RATE_44100;
+ case 48000:
+ return SNDRV_PCM_RATE_48000;
+ case 64000:
+ return SNDRV_PCM_RATE_64000;
+ case 88200:
+ return SNDRV_PCM_RATE_88200;
+ case 96000:
+ return SNDRV_PCM_RATE_96000;
+ case 176400:
+ return SNDRV_PCM_RATE_176400;
+ case 192000:
+ return SNDRV_PCM_RATE_192000;
+ case 352800:
+ return SNDRV_PCM_RATE_352800;
+ case 384000:
+ return SNDRV_PCM_RATE_384000;
+ case 705600:
+ return SNDRV_PCM_RATE_705600;
+ case 768000:
+ return SNDRV_PCM_RATE_768000;
+ case 12000:
+ return SNDRV_PCM_RATE_12000;
+ case 24000:
+ return SNDRV_PCM_RATE_24000;
+ case 128000:
+ return SNDRV_PCM_RATE_128000;
+ default:
+ return 0;
+ }
+}
+
+static u64 width_find_mask(unsigned int bits)
+{
+ switch (bits) {
+ case 0:
+ return SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S20_LE | SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE;
+ case 8:
+ return SNDRV_PCM_FMTBIT_S8;
+ case 16:
+ return SNDRV_PCM_FMTBIT_S16_LE;
+ case 20:
+ return SNDRV_PCM_FMTBIT_S20_LE;
+ case 24:
+ return SNDRV_PCM_FMTBIT_S24_LE;
+ case 32:
+ return SNDRV_PCM_FMTBIT_S32_LE;
+ default:
+ return 0;
+ }
+}
+
+static int populate_rate_format(struct device *dev,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct snd_soc_pcm_stream *stream)
+{
+ struct sdca_control_range *range;
+ unsigned int sample_rate, sample_width;
+ unsigned int clock_rates = 0;
+ unsigned int rates = 0;
+ u64 formats = 0;
+ int sel, i;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ sel = SDCA_CTL_IT_USAGE;
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ sel = SDCA_CTL_OT_USAGE;
+ break;
+ default:
+ dev_err(dev, "%s: entity type has no usage control\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ if (entity->iot.clock) {
+ range = sdca_selector_find_range(dev, entity->iot.clock,
+ SDCA_CTL_CS_SAMPLERATEINDEX,
+ SDCA_SAMPLERATEINDEX_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ sample_rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i);
+ clock_rates |= rate_find_mask(sample_rate);
+ }
+ } else {
+ clock_rates = UINT_MAX;
+ }
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ sample_rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i);
+ sample_rate = rate_find_mask(sample_rate);
+
+ if (sample_rate & clock_rates) {
+ rates |= sample_rate;
+
+ sample_width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i);
+ formats |= width_find_mask(sample_width);
+ }
+ }
+
+ stream->formats = formats;
+ stream->rates = rates;
+
+ return 0;
+}
+
+/**
+ * sdca_asoc_populate_dais - fill in an array of DAI drivers for a Function
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @dais: Array of DAI drivers to be populated.
+ * @ops: DAI ops to be attached to each of the created DAI drivers.
+ *
+ * This function populates an array of ASoC DAI drivers from the DisCo
+ * information for a particular SDCA Function. Typically,
+ * snd_soc_asoc_count_component will be used to allocate an
+ * appropriately sized array before calling this function.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_dais(struct device *dev, struct sdca_function_data *function,
+ struct snd_soc_dai_driver *dais,
+ const struct snd_soc_dai_ops *ops)
+{
+ int i, j;
+ int ret;
+
+ for (i = 0, j = 0; i < function->num_entities - 1; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+ struct snd_soc_pcm_stream *stream;
+ const char *stream_suffix;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ stream = &dais[j].playback;
+ stream_suffix = "Playback";
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ stream = &dais[j].capture;
+ stream_suffix = "Capture";
+ break;
+ default:
+ continue;
+ }
+
+ /* Can't check earlier as only terminals have an iot member. */
+ if (!entity->iot.is_dataport)
+ continue;
+
+ stream->stream_name = devm_kasprintf(dev, GFP_KERNEL, "%s %s",
+ entity->label, stream_suffix);
+ if (!stream->stream_name)
+ return -ENOMEM;
+ /* Channels will be further limited by constraints */
+ stream->channels_min = 1;
+ stream->channels_max = SDCA_MAX_CHANNEL_COUNT;
+
+ ret = populate_rate_format(dev, function, entity, stream);
+ if (ret)
+ return ret;
+
+ dais[j].id = i;
+ dais[j].name = entity->label;
+ dais[j].ops = ops;
+ j++;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_dais, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_populate_component - fill in a component driver for a Function
+ * @dev: Pointer to the device against which allocations will be done.
+ * @function: Pointer to the Function information.
+ * @component_drv: Pointer to the component driver to be populated.
+ * @dai_drv: Pointer to the DAI driver array to be allocated and populated.
+ * @num_dai_drv: Pointer to integer that will be populated with the number of
+ * DAI drivers.
+ * @ops: DAI ops pointer that will be used for each DAI driver.
+ *
+ * This function populates a snd_soc_component_driver structure based
+ * on the DisCo information for a particular SDCA Function. It does
+ * all allocation internally.
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_populate_component(struct device *dev,
+ struct sdca_function_data *function,
+ struct snd_soc_component_driver *component_drv,
+ struct snd_soc_dai_driver **dai_drv, int *num_dai_drv,
+ const struct snd_soc_dai_ops *ops)
+{
+ struct snd_soc_dapm_widget *widgets;
+ struct snd_soc_dapm_route *routes;
+ struct snd_kcontrol_new *controls;
+ struct snd_soc_dai_driver *dais;
+ int num_widgets, num_routes, num_controls, num_dais;
+ int ret;
+
+ ret = sdca_asoc_count_component(dev, function, &num_widgets, &num_routes,
+ &num_controls, &num_dais);
+ if (ret)
+ return ret;
+
+ widgets = devm_kcalloc(dev, num_widgets, sizeof(*widgets), GFP_KERNEL);
+ if (!widgets)
+ return -ENOMEM;
+
+ routes = devm_kcalloc(dev, num_routes, sizeof(*routes), GFP_KERNEL);
+ if (!routes)
+ return -ENOMEM;
+
+ controls = devm_kcalloc(dev, num_controls, sizeof(*controls), GFP_KERNEL);
+ if (!controls)
+ return -ENOMEM;
+
+ dais = devm_kcalloc(dev, num_dais, sizeof(*dais), GFP_KERNEL);
+ if (!dais)
+ return -ENOMEM;
+
+ ret = sdca_asoc_populate_dapm(dev, function, widgets, routes);
+ if (ret)
+ return ret;
+
+ ret = sdca_asoc_populate_controls(dev, function, controls);
+ if (ret)
+ return ret;
+
+ ret = sdca_asoc_populate_dais(dev, function, dais, ops);
+ if (ret)
+ return ret;
+
+ component_drv->dapm_widgets = widgets;
+ component_drv->num_dapm_widgets = num_widgets;
+ component_drv->dapm_routes = routes;
+ component_drv->num_dapm_routes = num_routes;
+ component_drv->controls = controls;
+ component_drv->num_controls = num_controls;
+
+ *dai_drv = dais;
+ *num_dai_drv = num_dais;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_populate_component, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_set_constraints - constrain channels available on a DAI
+ * @dev: Pointer to the device, used for error messages.
+ * @regmap: Pointer to the Function register map.
+ * @function: Pointer to the Function information.
+ * @substream: Pointer to the PCM substream.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from startup().
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ static const unsigned int channel_list[] = {
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ };
+ struct sdca_entity *entity = &function->entities[dai->id];
+ struct snd_pcm_hw_constraint_list *constraint;
+ struct sdca_control_range *range;
+ struct sdca_control *control;
+ unsigned int channel_mask = 0;
+ int i, ret;
+
+ static_assert(ARRAY_SIZE(channel_list) == SDCA_MAX_CHANNEL_COUNT);
+ static_assert(sizeof(channel_mask) * BITS_PER_BYTE >= SDCA_MAX_CHANNEL_COUNT);
+
+ if (entity->type != SDCA_ENTITY_TYPE_IT)
+ return 0;
+
+ control = sdca_selector_find_control(dev, entity, SDCA_CTL_IT_CLUSTERINDEX);
+ if (!control)
+ return -EINVAL;
+
+ range = sdca_control_find_range(dev, entity, control, SDCA_CLUSTER_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ int clusterid = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i);
+ struct sdca_cluster *cluster;
+
+ cluster = sdca_id_find_cluster(dev, function, clusterid);
+ if (!cluster)
+ return -ENODEV;
+
+ channel_mask |= (1 << (cluster->num_channels - 1));
+ }
+
+ dev_dbg(dev, "%s: set channel constraint mask: %#x\n",
+ entity->label, channel_mask);
+
+ constraint = kzalloc(sizeof(*constraint), GFP_KERNEL);
+ if (!constraint)
+ return -ENOMEM;
+
+ constraint->count = ARRAY_SIZE(channel_list);
+ constraint->list = channel_list;
+ constraint->mask = channel_mask;
+
+ ret = snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ constraint);
+ if (ret) {
+ dev_err(dev, "%s: failed to add constraint: %d\n", entity->label, ret);
+ kfree(constraint);
+ return ret;
+ }
+
+ dai->priv = constraint;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_set_constraints, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_free_constraints - free constraint allocations
+ * @substream: Pointer to the PCM substream.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from shutdown().
+ */
+void sdca_asoc_free_constraints(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_pcm_hw_constraint_list *constraint = dai->priv;
+
+ kfree(constraint);
+}
+EXPORT_SYMBOL_NS(sdca_asoc_free_constraints, "SND_SOC_SDCA");
+
+/**
+ * sdca_asoc_get_port - return SoundWire port for a DAI
+ * @dev: Pointer to the device, used for error messages.
+ * @regmap: Pointer to the Function register map.
+ * @function: Pointer to the Function information.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from hw_params().
+ *
+ * Return: Returns a positive port number on success, and a negative error
+ * code on failure.
+ */
+int sdca_asoc_get_port(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct snd_soc_dai *dai)
+{
+ struct sdca_entity *entity = &function->entities[dai->id];
+ struct sdca_control_range *range;
+ unsigned int reg, val;
+ int sel = -EINVAL;
+ int i, ret;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ sel = SDCA_CTL_IT_DATAPORT_SELECTOR;
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ sel = SDCA_CTL_OT_DATAPORT_SELECTOR;
+ break;
+ default:
+ break;
+ }
+
+ if (sel < 0 || !entity->iot.is_dataport) {
+ dev_err(dev, "%s: port number only available for dataports\n",
+ entity->label);
+ return -EINVAL;
+ }
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_DATAPORT_SELECTOR_NCOLS,
+ SDCA_DATAPORT_SELECTOR_NROWS);
+ if (!range)
+ return -EINVAL;
+
+ reg = SDW_SDCA_CTL(function->desc->adr, entity->id, sel, 0);
+
+ ret = regmap_read(regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "%s: failed to read dataport selector: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ for (i = 0; i < range->rows; i++) {
+ static const u8 port_mask = 0xF;
+
+ sel = sdca_range(range, val & port_mask, i);
+
+ /*
+ * FIXME: Currently only a single dataport is supported, so
+ * return the first one found, technically up to 4 dataports
+ * could be linked, but this is not yet supported.
+ */
+ if (sel != 0xFF)
+ return sel;
+
+ val >>= hweight8(port_mask);
+ }
+
+ dev_err(dev, "%s: no dataport found\n", entity->label);
+ return -ENODEV;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_get_port, "SND_SOC_SDCA");
+
+static int set_cluster(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity, unsigned int channels)
+{
+ int sel = SDCA_CTL_IT_CLUSTERINDEX;
+ struct sdca_control_range *range;
+ int i, ret;
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_CLUSTER_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ int cluster_id = sdca_range(range, SDCA_CLUSTER_CLUSTERID, i);
+ struct sdca_cluster *cluster;
+
+ cluster = sdca_id_find_cluster(dev, function, cluster_id);
+ if (!cluster)
+ return -ENODEV;
+
+ if (cluster->num_channels == channels) {
+ int index = sdca_range(range, SDCA_CLUSTER_BYTEINDEX, i);
+ unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
+ entity->id, sel, 0);
+
+ ret = regmap_update_bits(regmap, reg, 0xFF, index);
+ if (ret) {
+ dev_err(dev, "%s: failed to write cluster index: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: set cluster to %d (%d channels)\n",
+ entity->label, index, channels);
+
+ return 0;
+ }
+ }
+
+ dev_err(dev, "%s: no cluster for %d channels\n", entity->label, channels);
+ return -EINVAL;
+}
+
+static int set_clock(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity, int target_rate)
+{
+ int sel = SDCA_CTL_CS_SAMPLERATEINDEX;
+ struct sdca_control_range *range;
+ int i, ret;
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_SAMPLERATEINDEX_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ unsigned int rate = sdca_range(range, SDCA_SAMPLERATEINDEX_RATE, i);
+
+ if (rate == target_rate) {
+ unsigned int index = sdca_range(range,
+ SDCA_SAMPLERATEINDEX_INDEX,
+ i);
+ unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
+ entity->id, sel, 0);
+
+ ret = regmap_update_bits(regmap, reg, 0xFF, index);
+ if (ret) {
+ dev_err(dev, "%s: failed to write clock rate: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: set clock rate to %d (%dHz)\n",
+ entity->label, index, rate);
+
+ return 0;
+ }
+ }
+
+ dev_err(dev, "%s: no clock rate for %dHz\n", entity->label, target_rate);
+ return -EINVAL;
+}
+
+static int set_usage(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity, int sel,
+ int target_rate, int target_width)
+{
+ struct sdca_control_range *range;
+ int i, ret;
+
+ range = sdca_selector_find_range(dev, entity, sel, SDCA_USAGE_NCOLS, 0);
+ if (!range)
+ return -EINVAL;
+
+ for (i = 0; i < range->rows; i++) {
+ unsigned int rate = sdca_range(range, SDCA_USAGE_SAMPLE_RATE, i);
+ unsigned int width = sdca_range(range, SDCA_USAGE_SAMPLE_WIDTH, i);
+
+ if ((!rate || rate == target_rate) && width == target_width) {
+ unsigned int usage = sdca_range(range, SDCA_USAGE_NUMBER, i);
+ unsigned int reg = SDW_SDCA_CTL(function->desc->adr,
+ entity->id, sel, 0);
+
+ ret = regmap_update_bits(regmap, reg, 0xFF, usage);
+ if (ret) {
+ dev_err(dev, "%s: failed to write usage: %d\n",
+ entity->label, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "%s: set usage to %#x (%dHz, %d bits)\n",
+ entity->label, usage, target_rate, target_width);
+
+ return 0;
+ }
+ }
+
+ dev_err(dev, "%s: no usage for %dHz, %dbits\n",
+ entity->label, target_rate, target_width);
+ return -EINVAL;
+}
+
+/**
+ * sdca_asoc_hw_params - set SDCA channels, sample rate and bit depth
+ * @dev: Pointer to the device, used for error messages.
+ * @regmap: Pointer to the Function register map.
+ * @function: Pointer to the Function information.
+ * @substream: Pointer to the PCM substream.
+ * @params: Pointer to the hardware parameters.
+ * @dai: Pointer to the ASoC DAI.
+ *
+ * Typically called from hw_params().
+ *
+ * Return: Returns zero on success, and a negative error code on failure.
+ */
+int sdca_asoc_hw_params(struct device *dev, struct regmap *regmap,
+ struct sdca_function_data *function,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sdca_entity *entity = &function->entities[dai->id];
+ int channels = params_channels(params);
+ int width = params_width(params);
+ int rate = params_rate(params);
+ int usage_sel;
+ int ret;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_IT:
+ ret = set_cluster(dev, regmap, function, entity, channels);
+ if (ret)
+ return ret;
+
+ usage_sel = SDCA_CTL_IT_USAGE;
+ break;
+ case SDCA_ENTITY_TYPE_OT:
+ usage_sel = SDCA_CTL_OT_USAGE;
+ break;
+ default:
+ dev_err(dev, "%s: hw_params on non-terminal entity\n", entity->label);
+ return -EINVAL;
+ }
+
+ if (entity->iot.clock) {
+ ret = set_clock(dev, regmap, function, entity->iot.clock, rate);
+ if (ret)
+ return ret;
+ }
+
+ ret = set_usage(dev, regmap, function, entity, usage_sel, rate, width);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_asoc_hw_params, "SND_SOC_SDCA");
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 493f390f087a..93767e73bc5f 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -19,6 +19,7 @@
#include <linux/types.h>
#include <sound/sdca.h>
#include <sound/sdca_function.h>
+#include <sound/sdca_hid.h>
/*
* Should be long enough to encompass all the MIPI DisCo properties.
@@ -211,7 +212,7 @@ static int find_sdca_init_table(struct device *dev,
} else if (num_init_writes % sizeof(*raw) != 0) {
dev_err(dev, "%pfwP: init table size invalid\n", function_node);
return -EINVAL;
- } else if (num_init_writes > SDCA_MAX_INIT_COUNT) {
+ } else if ((num_init_writes / sizeof(*raw)) > SDCA_MAX_INIT_COUNT) {
dev_err(dev, "%pfwP: maximum init table size exceeded\n", function_node);
return -EINVAL;
}
@@ -880,7 +881,8 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
control->value = tmp;
control->has_fixed = true;
}
-
+ fallthrough;
+ case SDCA_ACCESS_MODE_RO:
control->deferrable = fwnode_property_read_bool(control_node,
"mipi-sdca-control-deferrable");
break;
@@ -911,6 +913,8 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
&tmp);
if (!ret)
control->interrupt_position = tmp;
+ else
+ control->interrupt_position = SDCA_NO_INTERRUPT;
control->label = find_sdca_control_label(dev, entity, control);
if (!control->label)
@@ -1105,12 +1109,6 @@ static int find_sdca_entity_pde(struct device *dev,
return -EINVAL;
}
- /* There are 3 values per delay */
- delays = devm_kcalloc(dev, num_delays / mult_delay,
- sizeof(*delays), GFP_KERNEL);
- if (!delays)
- return -ENOMEM;
-
delay_list = kcalloc(num_delays, sizeof(*delay_list), GFP_KERNEL);
if (!delay_list)
return -ENOMEM;
@@ -1121,6 +1119,10 @@ static int find_sdca_entity_pde(struct device *dev,
num_delays /= mult_delay;
+ delays = devm_kcalloc(dev, num_delays, sizeof(*delays), GFP_KERNEL);
+ if (!delays)
+ return -ENOMEM;
+
for (i = 0, j = 0; i < num_delays; i++) {
delays[i].from_ps = delay_list[j++];
delays[i].to_ps = delay_list[j++];
@@ -1222,6 +1224,93 @@ bad_list:
return -EINVAL;
}
+static int
+find_sdca_entity_hide(struct device *dev, struct fwnode_handle *function_node,
+ struct fwnode_handle *entity_node, struct sdca_entity *entity)
+{
+ struct sdca_entity_hide *hide = &entity->hide;
+ unsigned int delay, *af_list = hide->af_number_list;
+ int nval, ret;
+ unsigned char *report_desc = NULL;
+
+ ret = fwnode_property_read_u32(entity_node,
+ "mipi-sdca-RxUMP-ownership-transition-maxdelay", &delay);
+ if (!ret)
+ hide->max_delay = delay;
+
+ nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDTx-supported-report-ids");
+ if (nval > 0) {
+ hide->num_hidtx_ids = nval;
+ hide->hidtx_ids = devm_kcalloc(dev, hide->num_hidtx_ids,
+ sizeof(*hide->hidtx_ids), GFP_KERNEL);
+ if (!hide->hidtx_ids)
+ return -ENOMEM;
+
+ ret = fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-HIDTx-supported-report-ids",
+ hide->hidtx_ids,
+ hide->num_hidtx_ids);
+ if (ret < 0)
+ return ret;
+ }
+
+ nval = fwnode_property_count_u32(entity_node, "mipi-sdca-HIDRx-supported-report-ids");
+ if (nval > 0) {
+ hide->num_hidrx_ids = nval;
+ hide->hidrx_ids = devm_kcalloc(dev, hide->num_hidrx_ids,
+ sizeof(*hide->hidrx_ids), GFP_KERNEL);
+ if (!hide->hidrx_ids)
+ return -ENOMEM;
+
+ ret = fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-HIDRx-supported-report-ids",
+ hide->hidrx_ids,
+ hide->num_hidrx_ids);
+ if (ret < 0)
+ return ret;
+ }
+
+ nval = fwnode_property_count_u32(entity_node, "mipi-sdca-hide-related-audio-function-list");
+ if (nval <= 0) {
+ dev_err(dev, "%pfwP: audio function numbers list missing: %d\n",
+ entity_node, nval);
+ return -EINVAL;
+ } else if (nval > SDCA_MAX_FUNCTION_COUNT) {
+ dev_err(dev, "%pfwP: maximum number of audio function exceeded\n", entity_node);
+ return -EINVAL;
+ }
+
+ hide->hide_reside_function_num = nval;
+ fwnode_property_read_u32_array(entity_node,
+ "mipi-sdca-hide-related-audio-function-list", af_list, nval);
+
+ nval = fwnode_property_count_u8(function_node, "mipi-sdca-hid-descriptor");
+ if (nval)
+ fwnode_property_read_u8_array(function_node, "mipi-sdca-hid-descriptor",
+ (u8 *)&hide->hid_desc, nval);
+
+ if (hide->hid_desc.bNumDescriptors) {
+ nval = fwnode_property_count_u8(function_node, "mipi-sdca-report-descriptor");
+ if (nval) {
+ report_desc = devm_kzalloc(dev, nval, GFP_KERNEL);
+ if (!report_desc)
+ return -ENOMEM;
+ hide->hid_report_desc = report_desc;
+ fwnode_property_read_u8_array(function_node, "mipi-sdca-report-descriptor",
+ report_desc, nval);
+
+ /* add HID device */
+ ret = sdca_add_hid_device(dev, entity);
+ if (ret) {
+ dev_err(dev, "%pfwP: failed to add HID device: %d\n", entity_node, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
static int find_sdca_entity(struct device *dev,
struct fwnode_handle *function_node,
struct fwnode_handle *entity_node,
@@ -1263,6 +1352,9 @@ static int find_sdca_entity(struct device *dev,
case SDCA_ENTITY_TYPE_GE:
ret = find_sdca_entity_ge(dev, entity_node, entity);
break;
+ case SDCA_ENTITY_TYPE_HIDE:
+ ret = find_sdca_entity_hide(dev, function_node, entity_node, entity);
+ break;
default:
break;
}
@@ -1543,7 +1635,6 @@ static int find_sdca_entity_connection(struct device *dev,
ret = fwnode_property_read_u64(entity_node, "mipi-sdca-input-pin-list", &pin_list);
if (ret == -EINVAL) {
/* Allow missing pin lists, assume no pins. */
- dev_warn(dev, "%s: missing pin list\n", entity->label);
return 0;
} else if (ret) {
dev_err(dev, "%s: failed to read pin list: %d\n", entity->label, ret);
@@ -1850,5 +1941,73 @@ int sdca_parse_function(struct device *dev,
}
EXPORT_SYMBOL_NS(sdca_parse_function, "SND_SOC_SDCA");
+struct sdca_control *sdca_selector_find_control(struct device *dev,
+ struct sdca_entity *entity,
+ const int sel)
+{
+ int i;
+
+ for (i = 0; i < entity->num_controls; i++) {
+ struct sdca_control *control = &entity->controls[i];
+
+ if (control->sel == sel)
+ return control;
+ }
+
+ dev_err(dev, "%s: control %#x: missing\n", entity->label, sel);
+ return NULL;
+}
+EXPORT_SYMBOL_NS(sdca_selector_find_control, "SND_SOC_SDCA");
+
+struct sdca_control_range *sdca_control_find_range(struct device *dev,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ int cols, int rows)
+{
+ struct sdca_control_range *range = &control->range;
+
+ if ((cols && range->cols != cols) || (rows && range->rows != rows) ||
+ !range->data) {
+ dev_err(dev, "%s: control %#x: ranges invalid (%d,%d)\n",
+ entity->label, control->sel, range->cols, range->rows);
+ return NULL;
+ }
+
+ return range;
+}
+EXPORT_SYMBOL_NS(sdca_control_find_range, "SND_SOC_SDCA");
+
+struct sdca_control_range *sdca_selector_find_range(struct device *dev,
+ struct sdca_entity *entity,
+ int sel, int cols, int rows)
+{
+ struct sdca_control *control;
+
+ control = sdca_selector_find_control(dev, entity, sel);
+ if (!control)
+ return NULL;
+
+ return sdca_control_find_range(dev, entity, control, cols, rows);
+}
+EXPORT_SYMBOL_NS(sdca_selector_find_range, "SND_SOC_SDCA");
+
+struct sdca_cluster *sdca_id_find_cluster(struct device *dev,
+ struct sdca_function_data *function,
+ const int id)
+{
+ int i;
+
+ for (i = 0; i < function->num_clusters; i++) {
+ struct sdca_cluster *cluster = &function->clusters[i];
+
+ if (cluster->id == id)
+ return cluster;
+ }
+
+ dev_err(dev, "%s: cluster %#x: missing\n", function->desc->name, id);
+ return NULL;
+}
+EXPORT_SYMBOL_NS(sdca_id_find_cluster, "SND_SOC_SDCA");
+
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("SDCA library");
diff --git a/sound/soc/sdca/sdca_hid.c b/sound/soc/sdca/sdca_hid.c
new file mode 100644
index 000000000000..967f7ec6fb79
--- /dev/null
+++ b/sound/soc/sdca/sdca_hid.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/acpi.h>
+#include <linux/byteorder/generic.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_hid.h>
+
+static int sdwhid_parse(struct hid_device *hid)
+{
+ struct sdca_entity *entity = hid->driver_data;
+ unsigned int rsize;
+ int ret;
+
+ rsize = le16_to_cpu(entity->hide.hid_desc.rpt_desc.wDescriptorLength);
+
+ if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) {
+ dev_err(&hid->dev, "invalid size of report descriptor (%u)\n", rsize);
+ return -EINVAL;
+ }
+
+ ret = hid_parse_report(hid, entity->hide.hid_report_desc, rsize);
+
+ if (!ret)
+ return 0;
+
+ dev_err(&hid->dev, "parsing report descriptor failed\n");
+ return ret;
+}
+
+static int sdwhid_start(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void sdwhid_stop(struct hid_device *hid)
+{
+}
+
+static int sdwhid_raw_request(struct hid_device *hid, unsigned char reportnum,
+ __u8 *buf, size_t len, unsigned char rtype, int reqtype)
+{
+ switch (reqtype) {
+ case HID_REQ_GET_REPORT:
+ /* not implemented yet */
+ return 0;
+ case HID_REQ_SET_REPORT:
+ /* not implemented yet */
+ return 0;
+ default:
+ return -EIO;
+ }
+}
+
+static int sdwhid_open(struct hid_device *hid)
+{
+ return 0;
+}
+
+static void sdwhid_close(struct hid_device *hid)
+{
+}
+
+static const struct hid_ll_driver sdw_hid_driver = {
+ .parse = sdwhid_parse,
+ .start = sdwhid_start,
+ .stop = sdwhid_stop,
+ .open = sdwhid_open,
+ .close = sdwhid_close,
+ .raw_request = sdwhid_raw_request,
+};
+
+int sdca_add_hid_device(struct device *dev, struct sdca_entity *entity)
+{
+ struct sdw_bus *bus;
+ struct hid_device *hid;
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret;
+
+ bus = slave->bus;
+
+ hid = hid_allocate_device();
+ if (IS_ERR(hid))
+ return PTR_ERR(hid);
+
+ hid->ll_driver = &sdw_hid_driver;
+
+ hid->dev.parent = dev;
+ hid->bus = BUS_SDW;
+ hid->version = le16_to_cpu(entity->hide.hid_desc.bcdHID);
+
+ snprintf(hid->name, sizeof(hid->name),
+ "HID sdw:%01x:%01x:%04x:%04x:%02x",
+ bus->controller_id, bus->link_id, slave->id.mfg_id,
+ slave->id.part_id, slave->id.class_id);
+
+ snprintf(hid->phys, sizeof(hid->phys), "%s", dev->bus->name);
+
+ hid->driver_data = entity;
+
+ ret = hid_add_device(hid);
+ if (ret && ret != -ENODEV) {
+ dev_err(dev, "can't add hid device: %d\n", ret);
+ hid_destroy_device(hid);
+ return ret;
+ }
+
+ entity->hide.hid = hid;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(sdca_add_hid_device, "SND_SOC_SDCA");
+
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_DESCRIPTION("SDCA HID library");
diff --git a/sound/soc/sdca/sdca_interrupts.c b/sound/soc/sdca/sdca_interrupts.c
new file mode 100644
index 000000000000..8018773ee426
--- /dev/null
+++ b/sound/soc/sdca/sdca_interrupts.c
@@ -0,0 +1,444 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2025 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/sdca.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_interrupts.h>
+#include <sound/soc-component.h>
+#include <sound/soc.h>
+
+#define IRQ_SDCA(number) REGMAP_IRQ_REG(number, ((number) / BITS_PER_BYTE), \
+ SDW_SCP_SDCA_INTMASK_SDCA_##number)
+
+static const struct regmap_irq regmap_irqs[SDCA_MAX_INTERRUPTS] = {
+ IRQ_SDCA(0),
+ IRQ_SDCA(1),
+ IRQ_SDCA(2),
+ IRQ_SDCA(3),
+ IRQ_SDCA(4),
+ IRQ_SDCA(5),
+ IRQ_SDCA(6),
+ IRQ_SDCA(7),
+ IRQ_SDCA(8),
+ IRQ_SDCA(9),
+ IRQ_SDCA(10),
+ IRQ_SDCA(11),
+ IRQ_SDCA(12),
+ IRQ_SDCA(13),
+ IRQ_SDCA(14),
+ IRQ_SDCA(15),
+ IRQ_SDCA(16),
+ IRQ_SDCA(17),
+ IRQ_SDCA(18),
+ IRQ_SDCA(19),
+ IRQ_SDCA(20),
+ IRQ_SDCA(21),
+ IRQ_SDCA(22),
+ IRQ_SDCA(23),
+ IRQ_SDCA(24),
+ IRQ_SDCA(25),
+ IRQ_SDCA(26),
+ IRQ_SDCA(27),
+ IRQ_SDCA(28),
+ IRQ_SDCA(29),
+ IRQ_SDCA(30),
+};
+
+static const struct regmap_irq_chip sdca_irq_chip = {
+ .name = "sdca_irq",
+
+ .status_base = SDW_SCP_SDCA_INT1,
+ .unmask_base = SDW_SCP_SDCA_INTMASK1,
+ .ack_base = SDW_SCP_SDCA_INT1,
+ .num_regs = 4,
+
+ .irqs = regmap_irqs,
+ .num_irqs = SDCA_MAX_INTERRUPTS,
+
+ .runtime_pm = true,
+};
+
+static irqreturn_t base_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct device *dev = interrupt->component->dev;
+
+ dev_info(dev, "%s irq without full handling\n", interrupt->name);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t function_status_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct device *dev = interrupt->component->dev;
+ unsigned int reg, val;
+ unsigned long status;
+ unsigned int mask;
+ int ret;
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ interrupt->control->sel, 0);
+
+ ret = regmap_read(interrupt->component->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read function status: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ dev_dbg(dev, "function status: %#x\n", val);
+
+ status = val;
+ for_each_set_bit(mask, &status, BITS_PER_BYTE) {
+ mask = 1 << mask;
+
+ switch (mask) {
+ case SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION:
+ //FIXME: Add init writes
+ break;
+ case SDCA_CTL_ENTITY_0_FUNCTION_FAULT:
+ dev_err(dev, "function fault\n");
+ break;
+ case SDCA_CTL_ENTITY_0_UMP_SEQUENCE_FAULT:
+ dev_err(dev, "ump sequence fault\n");
+ break;
+ case SDCA_CTL_ENTITY_0_FUNCTION_BUSY:
+ dev_info(dev, "unexpected function busy\n");
+ break;
+ case SDCA_CTL_ENTITY_0_DEVICE_NEWLY_ATTACHED:
+ case SDCA_CTL_ENTITY_0_INTS_DISABLED_ABNORMALLY:
+ case SDCA_CTL_ENTITY_0_STREAMING_STOPPED_ABNORMALLY:
+ case SDCA_CTL_ENTITY_0_FUNCTION_HAS_BEEN_RESET:
+ break;
+ }
+ }
+
+ ret = regmap_write(interrupt->component->regmap, reg, val);
+ if (ret < 0) {
+ dev_err(dev, "failed to clear function status: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t detected_mode_handler(int irq, void *data)
+{
+ struct sdca_interrupt *interrupt = data;
+ struct snd_soc_component *component = interrupt->component;
+ struct device *dev = component->dev;
+ struct snd_soc_card *card = component->card;
+ struct rw_semaphore *rwsem = &card->snd_card->controls_rwsem;
+ struct snd_kcontrol *kctl = interrupt->priv;
+ struct snd_ctl_elem_value *ucontrol __free(kfree) = NULL;
+ struct soc_enum *soc_enum;
+ unsigned int reg, val;
+ int ret;
+
+ if (!kctl) {
+ const char *name __free(kfree) = kasprintf(GFP_KERNEL, "%s %s",
+ interrupt->entity->label,
+ SDCA_CTL_SELECTED_MODE_NAME);
+
+ if (!name)
+ return -ENOMEM;
+
+ kctl = snd_soc_component_get_kcontrol(component, name);
+ if (!kctl) {
+ dev_dbg(dev, "control not found: %s\n", name);
+ return IRQ_NONE;
+ }
+
+ interrupt->priv = kctl;
+ }
+
+ soc_enum = (struct soc_enum *)kctl->private_value;
+
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr, interrupt->entity->id,
+ interrupt->control->sel, 0);
+
+ ret = regmap_read(component->regmap, reg, &val);
+ if (ret < 0) {
+ dev_err(dev, "failed to read detected mode: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ switch (val) {
+ case SDCA_DETECTED_MODE_DETECTION_IN_PROGRESS:
+ case SDCA_DETECTED_MODE_JACK_UNKNOWN:
+ reg = SDW_SDCA_CTL(interrupt->function->desc->adr,
+ interrupt->entity->id,
+ SDCA_CTL_GE_SELECTED_MODE, 0);
+
+ /*
+ * Selected mode is not normally marked as volatile register
+ * (RW), but here force a read from the hardware. If the
+ * detected mode is unknown we need to see what the device
+ * selected as a "safe" option.
+ */
+ regcache_drop_region(component->regmap, reg, reg);
+
+ ret = regmap_read(component->regmap, reg, &val);
+ if (ret) {
+ dev_err(dev, "failed to re-check selected mode: %d\n", ret);
+ return IRQ_NONE;
+ }
+ break;
+ default:
+ break;
+ }
+
+ dev_dbg(dev, "%s: %#x\n", interrupt->name, val);
+
+ ucontrol = kzalloc(sizeof(*ucontrol), GFP_KERNEL);
+ if (!ucontrol)
+ return IRQ_NONE;
+
+ ucontrol->value.enumerated.item[0] = snd_soc_enum_val_to_item(soc_enum, val);
+
+ down_write(rwsem);
+ ret = kctl->put(kctl, ucontrol);
+ up_write(rwsem);
+ if (ret < 0) {
+ dev_err(dev, "failed to update selected mode: %d\n", ret);
+ return IRQ_NONE;
+ }
+
+ snd_ctl_notify(card->snd_card, SNDRV_CTL_EVENT_MASK_VALUE, &kctl->id);
+
+ return IRQ_HANDLED;
+}
+
+static int sdca_irq_request_locked(struct device *dev,
+ struct sdca_interrupt_info *info,
+ int sdca_irq, const char *name,
+ irq_handler_t handler, void *data)
+{
+ int irq;
+ int ret;
+
+ irq = regmap_irq_get_virq(info->irq_data, sdca_irq);
+ if (irq < 0)
+ return irq;
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, handler,
+ IRQF_ONESHOT, name, data);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "requested irq %d for %s\n", irq, name);
+
+ return 0;
+}
+
+/**
+ * sdca_request_irq - request an individual SDCA interrupt
+ * @dev: Pointer to the struct device against which things should be allocated.
+ * @interrupt_info: Pointer to the interrupt information structure.
+ * @sdca_irq: SDCA interrupt position.
+ * @name: Name to be given to the IRQ.
+ * @handler: A callback thread function to be called for the IRQ.
+ * @data: Private data pointer that will be passed to the handler.
+ *
+ * Typically this is handled internally by sdca_irq_populate, however if
+ * a device requires custom IRQ handling this can be called manually before
+ * calling sdca_irq_populate, which will then skip that IRQ whilst processing.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_irq_request(struct device *dev, struct sdca_interrupt_info *info,
+ int sdca_irq, const char *name, irq_handler_t handler,
+ void *data)
+{
+ int ret;
+
+ if (sdca_irq < 0 || sdca_irq >= SDCA_MAX_INTERRUPTS) {
+ dev_err(dev, "bad irq request: %d\n", sdca_irq);
+ return -EINVAL;
+ }
+
+ guard(mutex)(&info->irq_lock);
+
+ ret = sdca_irq_request_locked(dev, info, sdca_irq, name, handler, data);
+ if (ret) {
+ dev_err(dev, "failed to request irq %s: %d\n", name, ret);
+ return ret;
+ }
+
+ info->irqs[sdca_irq].externally_requested = true;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_request, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_data_populate - Populate common interrupt data
+ * @component: Pointer to the ASoC component for the Function.
+ * @function: Pointer to the SDCA Function.
+ * @entity: Pointer to the SDCA Entity.
+ * @control: Pointer to the SDCA Control.
+ * @interrupt: Pointer to the SDCA interrupt for this IRQ.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_irq_data_populate(struct snd_soc_component *component,
+ struct sdca_function_data *function,
+ struct sdca_entity *entity,
+ struct sdca_control *control,
+ struct sdca_interrupt *interrupt)
+{
+ struct device *dev = component->dev;
+ const char *name;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s %s %s", function->desc->name,
+ entity->label, control->label);
+ if (!name)
+ return -ENOMEM;
+
+ interrupt->name = name;
+ interrupt->component = component;
+ interrupt->function = function;
+ interrupt->entity = entity;
+ interrupt->control = control;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_data_populate, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_populate - Request all the individual IRQs for an SDCA Function
+ * @function: Pointer to the SDCA Function.
+ * @component: Pointer to the ASoC component for the Function.
+ * @info: Pointer to the SDCA interrupt info for this device.
+ *
+ * Typically this would be called from the driver for a single SDCA Function.
+ *
+ * Return: Zero on success, and a negative error code on failure.
+ */
+int sdca_irq_populate(struct sdca_function_data *function,
+ struct snd_soc_component *component,
+ struct sdca_interrupt_info *info)
+{
+ struct device *dev = component->dev;
+ int i, j;
+
+ guard(mutex)(&info->irq_lock);
+
+ for (i = 0; i < function->num_entities; i++) {
+ struct sdca_entity *entity = &function->entities[i];
+
+ for (j = 0; j < entity->num_controls; j++) {
+ struct sdca_control *control = &entity->controls[j];
+ int irq = control->interrupt_position;
+ struct sdca_interrupt *interrupt;
+ irq_handler_t handler;
+ int ret;
+
+ if (irq == SDCA_NO_INTERRUPT) {
+ continue;
+ } else if (irq < 0 || irq >= SDCA_MAX_INTERRUPTS) {
+ dev_err(dev, "bad irq position: %d\n", irq);
+ return -EINVAL;
+ }
+
+ interrupt = &info->irqs[irq];
+
+ if (interrupt->externally_requested) {
+ dev_dbg(dev,
+ "skipping irq %d, externally requested\n",
+ irq);
+ continue;
+ }
+
+ ret = sdca_irq_data_populate(component, function, entity,
+ control, interrupt);
+ if (ret)
+ return ret;
+
+ handler = base_handler;
+
+ switch (entity->type) {
+ case SDCA_ENTITY_TYPE_ENTITY_0:
+ if (control->sel == SDCA_CTL_ENTITY_0_FUNCTION_STATUS)
+ handler = function_status_handler;
+ break;
+ case SDCA_ENTITY_TYPE_GE:
+ if (control->sel == SDCA_CTL_GE_DETECTED_MODE)
+ handler = detected_mode_handler;
+ break;
+ default:
+ break;
+ }
+
+ ret = sdca_irq_request_locked(dev, info, irq, interrupt->name,
+ handler, interrupt);
+ if (ret) {
+ dev_err(dev, "failed to request irq %s: %d\n",
+ interrupt->name, ret);
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_populate, "SND_SOC_SDCA");
+
+/**
+ * sdca_irq_allocate - allocate an SDCA interrupt structure for a device
+ * @dev: Device pointer against which things should be allocated.
+ * @regmap: regmap to be used for accessing the SDCA IRQ registers.
+ * @irq: The interrupt number.
+ *
+ * Typically this would be called from the top level driver for the whole
+ * SDCA device, as only a single instance is required across all Functions
+ * on the device.
+ *
+ * Return: A pointer to the allocated sdca_interrupt_info struct, or an
+ * error code.
+ */
+struct sdca_interrupt_info *sdca_irq_allocate(struct device *dev,
+ struct regmap *regmap, int irq)
+{
+ struct sdca_interrupt_info *info;
+ int ret;
+
+ info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return ERR_PTR(-ENOMEM);
+
+ info->irq_chip = sdca_irq_chip;
+
+ ret = devm_mutex_init(dev, &info->irq_lock);
+ if (ret)
+ return ERR_PTR(ret);
+
+ ret = devm_regmap_add_irq_chip(dev, regmap, irq, IRQF_ONESHOT, 0,
+ &info->irq_chip, &info->irq_data);
+ if (ret) {
+ dev_err(dev, "failed to register irq chip: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ dev_dbg(dev, "registered on irq %d\n", irq);
+
+ return info;
+}
+EXPORT_SYMBOL_NS_GPL(sdca_irq_allocate, "SND_SOC_SDCA");
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SDCA IRQ library");
diff --git a/sound/soc/sdca/sdca_regmap.c b/sound/soc/sdca/sdca_regmap.c
index 4b78188cfceb..66e7eee7d7f4 100644
--- a/sound/soc/sdca/sdca_regmap.c
+++ b/sound/soc/sdca/sdca_regmap.c
@@ -316,6 +316,3 @@ int sdca_regmap_write_defaults(struct device *dev, struct regmap *regmap,
return 0;
}
EXPORT_SYMBOL_NS(sdca_regmap_write_defaults, "SND_SOC_SDCA");
-
-MODULE_LICENSE("GPL");
-MODULE_DESCRIPTION("SDCA library");
diff --git a/sound/soc/sdw_utils/soc_sdw_cs_amp.c b/sound/soc/sdw_utils/soc_sdw_cs_amp.c
index 35b550bcd4de..520ea36c63ee 100644
--- a/sound/soc/sdw_utils/soc_sdw_cs_amp.c
+++ b/sound/soc/sdw_utils/soc_sdw_cs_amp.c
@@ -14,7 +14,6 @@
#include <sound/soc-dai.h>
#include <sound/soc_sdw_utils.h>
-#define CODEC_NAME_SIZE 8
#define CS_AMP_CHANNELS_PER_AMP 4
#define CS35L56_SPK_VOLUME_0DB 400 /* 0dB Max */
@@ -38,21 +37,12 @@ EXPORT_SYMBOL_NS(asoc_sdw_cs35l56_volume_limit, "SND_SOC_SDW_UTILS");
int asoc_sdw_cs_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
{
- const char *dai_name = rtd->dai_link->codecs->dai_name;
struct snd_soc_card *card = rtd->card;
- char codec_name[CODEC_NAME_SIZE];
char widget_name[16];
struct snd_soc_dapm_route route = { "Speaker", NULL, widget_name };
struct snd_soc_dai *codec_dai;
int i, ret;
- snprintf(codec_name, CODEC_NAME_SIZE, "%s", dai_name);
- card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:%s",
- card->components, codec_name);
- if (!card->components)
- return -ENOMEM;
-
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (!strstr(codec_dai->name, "cs35l56"))
continue;
diff --git a/sound/soc/sdw_utils/soc_sdw_maxim.c b/sound/soc/sdw_utils/soc_sdw_maxim.c
index 5df8d9cae60c..8f9d1ed0725b 100644
--- a/sound/soc/sdw_utils/soc_sdw_maxim.c
+++ b/sound/soc/sdw_utils/soc_sdw_maxim.c
@@ -28,15 +28,6 @@ int asoc_sdw_maxim_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_
struct snd_soc_card *card = rtd->card;
int ret;
- card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:mx%04x",
- card->components, maxim_part_id);
- if (!card->components)
- return -ENOMEM;
-
- dev_dbg(card->dev, "soundwire maxim card components assigned : %s\n",
- card->components);
-
ret = snd_soc_dapm_add_routes(&card->dapm, max_98373_dapm_routes, 2);
if (ret)
dev_err(rtd->dev, "failed to add first SPK map: %d\n", ret);
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_amp.c b/sound/soc/sdw_utils/soc_sdw_rt_amp.c
index 0538c252ba69..76ee24b8eee4 100644
--- a/sound/soc/sdw_utils/soc_sdw_rt_amp.c
+++ b/sound/soc/sdw_utils/soc_sdw_rt_amp.c
@@ -190,17 +190,11 @@ int asoc_sdw_rt_amp_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc
const struct snd_soc_dapm_route *rt_amp_map;
char codec_name[CODEC_NAME_SIZE];
struct snd_soc_dai *codec_dai;
- int ret;
+ int ret = -EINVAL;
int i;
rt_amp_map = get_codec_name_and_route(dai, codec_name);
- card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:%s",
- card->components, codec_name);
- if (!card->components)
- return -ENOMEM;
-
for_each_rtd_codec_dais(rtd, i, codec_dai) {
if (strstr(codec_dai->component->name_prefix, "-1"))
ret = snd_soc_dapm_add_routes(&card->dapm, rt_amp_map, 2);
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c
index 0161b14297d5..224b58de9084 100644
--- a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c
+++ b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c
@@ -73,13 +73,6 @@ int asoc_sdw_rt_mf_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd
return -EINVAL;
}
- /* Update card components */
- card->components = devm_kasprintf(card->dev, GFP_KERNEL,
- "%s spk:%s",
- card->components, codec_name);
- if (!card->components)
- return -ENOMEM;
-
/* Add routes */
ret = snd_soc_dapm_add_routes(&card->dapm, route_map->route_map, route_map->route_size);
if (ret)
diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c
index 5175818ff2c1..1580331cd34c 100644
--- a/sound/soc/sdw_utils/soc_sdw_utils.c
+++ b/sound/soc/sdw_utils/soc_sdw_utils.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
+#include <sound/sdca_function.h>
#include <sound/soc_sdw_utils.h>
static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
@@ -134,6 +135,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "rt712-sdca-aif2",
+ .component_name = "rt712",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
.init = asoc_sdw_rt_amp_init,
@@ -216,6 +218,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "rt1308-aif",
+ .component_name = "rt1308",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
.init = asoc_sdw_rt_amp_init,
@@ -236,6 +239,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, true},
.dai_name = "rt1316-aif",
+ .component_name = "rt1316",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
.init = asoc_sdw_rt_amp_init,
@@ -255,6 +259,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, true},
.dai_name = "rt1318-aif",
+ .component_name = "rt1318",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
.init = asoc_sdw_rt_amp_init,
@@ -274,6 +279,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "rt1320-aif1",
+ .component_name = "rt1320",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
.init = asoc_sdw_rt_amp_init,
@@ -367,6 +373,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "rt721-sdca-aif2",
+ .component_name = "rt721",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
/* No feedback capability is provided by rt721-sdca codec driver*/
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
@@ -408,6 +415,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "rt722-sdca-aif2",
+ .component_name = "rt722",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
/* No feedback capability is provided by rt722-sdca codec driver*/
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
@@ -437,6 +445,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, true},
.dai_name = "max98373-aif1",
+ .component_name = "mx8373",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
.init = asoc_sdw_maxim_init,
@@ -455,6 +464,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "max98363-aif1",
+ .component_name = "mx8363",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
.init = asoc_sdw_maxim_init,
@@ -490,6 +500,33 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "cs35l56-sdw1",
+ .component_name = "cs35l56",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_cs_amp_init,
+ .rtd_init = asoc_sdw_cs_spk_rtd_init,
+ .controls = generic_spk_controls,
+ .num_controls = ARRAY_SIZE(generic_spk_controls),
+ .widgets = generic_spk_widgets,
+ .num_widgets = ARRAY_SIZE(generic_spk_widgets),
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "cs35l56-sdw1c",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_AMP_IN_DAI_ID},
+ .rtd_init = asoc_sdw_cs_spk_feedback_rtd_init,
+ },
+ },
+ .dai_num = 2,
+ },
+ {
+ .part_id = 0x3563,
+ .dais = {
+ {
+ .direction = {true, false},
+ .dai_name = "cs35l56-sdw1",
+ .component_name = "cs35l56",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
.init = asoc_sdw_cs_amp_init,
@@ -563,6 +600,7 @@ struct asoc_sdw_codec_info codec_info_list[] = {
{
.direction = {true, false},
.dai_name = "cs42l43-dp6",
+ .component_name = "cs42l43",
.dai_type = SOC_SDW_DAI_TYPE_AMP,
.dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
.init = asoc_sdw_cs42l43_spk_init,
@@ -707,6 +745,7 @@ int asoc_sdw_rtd_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card = rtd->card;
struct asoc_sdw_codec_info *codec_info;
struct snd_soc_dai *dai;
+ const char *spk_components="";
int dai_index;
int ret;
int i;
@@ -759,7 +798,32 @@ skip_add_controls_widgets:
if (ret)
return ret;
}
+
+ /* Generate the spk component string for card->components string */
+ if (codec_info->dais[dai_index].dai_type == SOC_SDW_DAI_TYPE_AMP &&
+ codec_info->dais[dai_index].component_name) {
+ if (strlen (spk_components) == 0)
+ spk_components =
+ devm_kasprintf(card->dev, GFP_KERNEL, "%s",
+ codec_info->dais[dai_index].component_name);
+ else
+ /* Append component name to spk_components */
+ spk_components =
+ devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s+%s", spk_components,
+ codec_info->dais[dai_index].component_name);
+ }
+
codec_info->dais[dai_index].rtd_init_done = true;
+
+ }
+
+ if (strlen (spk_components) > 0) {
+ /* Update card components for speaker components */
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL, "%s spk:%s",
+ card->components, spk_components);
+ if (!card->components)
+ return -ENOMEM;
}
return 0;
@@ -934,10 +998,10 @@ static bool asoc_sdw_is_unique_device(const struct snd_soc_acpi_link_adr *adr_li
return true;
}
-const char *asoc_sdw_get_codec_name(struct device *dev,
- const struct asoc_sdw_codec_info *codec_info,
- const struct snd_soc_acpi_link_adr *adr_link,
- int adr_index)
+static const char *_asoc_sdw_get_codec_name(struct device *dev,
+ const struct asoc_sdw_codec_info *codec_info,
+ const struct snd_soc_acpi_link_adr *adr_link,
+ int adr_index)
{
u64 adr = adr_link->adr_d[adr_index].adr;
unsigned int sdw_version = SDW_VERSION(adr);
@@ -947,17 +1011,24 @@ const char *asoc_sdw_get_codec_name(struct device *dev,
unsigned int part_id = SDW_PART_ID(adr);
unsigned int class_id = SDW_CLASS_ID(adr);
- if (codec_info->codec_name)
- return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);
- else if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
- class_id, adr_index))
+ if (asoc_sdw_is_unique_device(adr_link, sdw_version, mfg_id, part_id,
+ class_id, adr_index))
return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x",
link_id, mfg_id, part_id, class_id);
- else
- return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
- link_id, mfg_id, part_id, class_id, unique_id);
- return NULL;
+ return devm_kasprintf(dev, GFP_KERNEL, "sdw:0:%01x:%04x:%04x:%02x:%01x",
+ link_id, mfg_id, part_id, class_id, unique_id);
+}
+
+const char *asoc_sdw_get_codec_name(struct device *dev,
+ const struct asoc_sdw_codec_info *codec_info,
+ const struct snd_soc_acpi_link_adr *adr_link,
+ int adr_index)
+{
+ if (codec_info->codec_name)
+ return devm_kstrdup(dev, codec_info->codec_name, GFP_KERNEL);
+
+ return _asoc_sdw_get_codec_name(dev, codec_info, adr_link, adr_index);
}
EXPORT_SYMBOL_NS(asoc_sdw_get_codec_name, "SND_SOC_SDW_UTILS");
@@ -1059,9 +1130,8 @@ EXPORT_SYMBOL_NS(asoc_sdw_init_dai_link, "SND_SOC_SDW_UTILS");
int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *dai_links,
int *be_id, char *name, int playback, int capture,
const char *cpu_dai_name, const char *platform_comp_name,
- int num_platforms, const char *codec_name,
- const char *codec_dai_name, int no_pcm,
- int (*init)(struct snd_soc_pcm_runtime *rtd),
+ const char *codec_name, const char *codec_dai_name,
+ int no_pcm, int (*init)(struct snd_soc_pcm_runtime *rtd),
const struct snd_soc_ops *ops)
{
struct snd_soc_dai_link_component *dlc;
@@ -1078,8 +1148,8 @@ int asoc_sdw_init_simple_dai_link(struct device *dev, struct snd_soc_dai_link *d
dlc[2].dai_name = codec_dai_name;
asoc_sdw_init_dai_link(dev, dai_links, be_id, name, playback, capture,
- &dlc[0], 1, &dlc[1], num_platforms,
- &dlc[2], 1, no_pcm, init, ops);
+ &dlc[0], 1, &dlc[1], 1, &dlc[2], 1,
+ no_pcm, init, ops);
return 0;
}
@@ -1124,6 +1194,108 @@ struct asoc_sdw_dailink *asoc_sdw_find_dailink(struct asoc_sdw_dailink *dailinks
}
EXPORT_SYMBOL_NS(asoc_sdw_find_dailink, "SND_SOC_SDW_UTILS");
+static int asoc_sdw_get_dai_type(u32 type)
+{
+ switch (type) {
+ case SDCA_FUNCTION_TYPE_SMART_AMP:
+ case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
+ return SOC_SDW_DAI_TYPE_AMP;
+ case SDCA_FUNCTION_TYPE_SMART_MIC:
+ case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
+ case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
+ return SOC_SDW_DAI_TYPE_MIC;
+ case SDCA_FUNCTION_TYPE_UAJ:
+ case SDCA_FUNCTION_TYPE_RJ:
+ case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
+ return SOC_SDW_DAI_TYPE_JACK;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Check if the SDCA endpoint is present by the SDW peripheral
+ *
+ * @dev: Device pointer
+ * @codec_info: Codec info pointer
+ * @adr_link: ACPI link address
+ * @adr_index: Index of the ACPI link address
+ * @end_index: Index of the endpoint
+ *
+ * Return: 1 if the endpoint is present,
+ * 0 if the endpoint is not present,
+ * negative error code.
+ */
+
+static int is_sdca_endpoint_present(struct device *dev,
+ struct asoc_sdw_codec_info *codec_info,
+ const struct snd_soc_acpi_link_adr *adr_link,
+ int adr_index, int end_index)
+{
+ const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[adr_index];
+ const struct snd_soc_acpi_endpoint *adr_end;
+ const struct asoc_sdw_dai_info *dai_info;
+ struct snd_soc_dai_link_component *dlc;
+ struct snd_soc_dai *codec_dai;
+ struct sdw_slave *slave;
+ struct device *sdw_dev;
+ const char *sdw_codec_name;
+ int i;
+
+ dlc = kzalloc(sizeof(*dlc), GFP_KERNEL);
+ if (!dlc)
+ return -ENOMEM;
+
+ adr_end = &adr_dev->endpoints[end_index];
+ dai_info = &codec_info->dais[adr_end->num];
+
+ dlc->dai_name = dai_info->dai_name;
+ codec_dai = snd_soc_find_dai_with_mutex(dlc);
+ if (!codec_dai) {
+ dev_warn(dev, "codec dai %s not registered yet\n", dlc->dai_name);
+ kfree(dlc);
+ return -EPROBE_DEFER;
+ }
+ kfree(dlc);
+
+ sdw_codec_name = _asoc_sdw_get_codec_name(dev, codec_info,
+ adr_link, adr_index);
+ if (!sdw_codec_name)
+ return -ENOMEM;
+
+ sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, sdw_codec_name);
+ if (!sdw_dev) {
+ dev_err(dev, "codec %s not found\n", sdw_codec_name);
+ return -EINVAL;
+ }
+
+ slave = dev_to_sdw_dev(sdw_dev);
+ if (!slave)
+ return -EINVAL;
+
+ /* Make sure BIOS provides SDCA properties */
+ if (!slave->sdca_data.interface_revision) {
+ dev_warn(&slave->dev, "SDCA properties not found in the BIOS\n");
+ return 1;
+ }
+
+ for (i = 0; i < slave->sdca_data.num_functions; i++) {
+ int dai_type = asoc_sdw_get_dai_type(slave->sdca_data.function[i].type);
+
+ if (dai_type == dai_info->dai_type) {
+ dev_dbg(&slave->dev, "DAI type %d sdca function %s found\n",
+ dai_type, slave->sdca_data.function[i].name);
+ return 1;
+ }
+ }
+
+ dev_dbg(&slave->dev,
+ "SDCA device function for DAI type %d not supported, skip endpoint\n",
+ dai_info->dai_type);
+
+ return 0;
+}
+
int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
struct asoc_sdw_dailink *soc_dais,
struct asoc_sdw_endpoint *soc_ends,
@@ -1152,6 +1324,7 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
const struct snd_soc_acpi_adr_device *adr_dev = &adr_link->adr_d[i];
struct asoc_sdw_codec_info *codec_info;
const char *codec_name;
+ bool check_sdca = false;
if (!adr_dev->name_prefix) {
dev_err(dev, "codec 0x%llx does not have a name prefix\n",
@@ -1182,6 +1355,9 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
soc_end->include_sidecar = true;
}
+ if (SDW_CLASS_ID(adr_dev->adr) && adr_dev->num_endpoints > 1)
+ check_sdca = true;
+
for (j = 0; j < adr_dev->num_endpoints; j++) {
const struct snd_soc_acpi_endpoint *adr_end;
const struct asoc_sdw_dai_info *dai_info;
@@ -1192,9 +1368,35 @@ int asoc_sdw_parse_sdw_endpoints(struct snd_soc_card *card,
dai_info = &codec_info->dais[adr_end->num];
soc_dai = asoc_sdw_find_dailink(soc_dais, adr_end);
- if (dai_info->quirk &&
- !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
- continue;
+ /*
+ * quirk should have higher priority than the sdca properties
+ * in the BIOS. We can't always check the DAI quirk because we
+ * will set the mc_quirk when the BIOS doesn't provide the right
+ * information. The endpoint will be skipped if the dai_info->
+ * quirk_exclude and mc_quirk are both not set if we always skip
+ * the endpoint according to the quirk information. We need to
+ * keep the endpoint if it is present in the BIOS. So, only
+ * check the DAI quirk when the mc_quirk is set or SDCA endpoint
+ * present check is not needed.
+ */
+ if (dai_info->quirk & ctx->mc_quirk || !check_sdca) {
+ /*
+ * Check the endpoint if a matching quirk is set or SDCA
+ * endpoint check is not necessary
+ */
+ if (dai_info->quirk &&
+ !(dai_info->quirk_exclude ^ !!(dai_info->quirk & ctx->mc_quirk)))
+ continue;
+ } else {
+ /* Check SDCA codec endpoint if there is no matching quirk */
+ ret = is_sdca_endpoint_present(dev, codec_info, adr_link, i, j);
+ if (ret < 0)
+ return ret;
+
+ /* The endpoint is not present, skip */
+ if (!ret)
+ continue;
+ }
dev_dbg(dev,
"Add dev: %d, 0x%llx end: %d, dai: %d, %c/%c to %s: %d\n",
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index 079e4ff5a14e..29790807d785 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -87,8 +87,8 @@ static int snd_soc_ac97_gpio_get(struct gpio_chip *chip, unsigned int offset)
return !!(ret & (1 << offset));
}
-static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned int offset,
- int value)
+static int snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct snd_ac97_gpio_priv *gpio_priv = gpiochip_get_data(chip);
struct snd_soc_component *component = gpio_to_component(chip);
@@ -98,15 +98,22 @@ static void snd_soc_ac97_gpio_set(struct gpio_chip *chip, unsigned int offset,
snd_soc_component_write(component, AC97_GPIO_STATUS,
gpio_priv->gpios_set);
dev_dbg(component->dev, "set gpio %d to %d\n", offset, !!value);
+
+ return 0;
}
static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip,
unsigned offset, int value)
{
struct snd_soc_component *component = gpio_to_component(chip);
+ int ret;
dev_dbg(component->dev, "set gpio %d to output\n", offset);
- snd_soc_ac97_gpio_set(chip, offset, value);
+
+ ret = snd_soc_ac97_gpio_set(chip, offset, value);
+ if (ret)
+ return ret;
+
return snd_soc_component_update_bits(component, AC97_GPIO_CFG,
1 << offset, 0);
}
@@ -118,7 +125,7 @@ static const struct gpio_chip snd_soc_ac97_gpio_chip = {
.direction_input = snd_soc_ac97_gpio_direction_in,
.get = snd_soc_ac97_gpio_get,
.direction_output = snd_soc_ac97_gpio_direction_out,
- .set = snd_soc_ac97_gpio_set,
+ .set_rv = snd_soc_ac97_gpio_set,
.can_sleep = 1,
};
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index 25f5e543ae8d..65c495094024 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -1278,7 +1278,6 @@ void snd_soc_pcm_component_pm_runtime_put(struct snd_soc_pcm_runtime *rtd,
if (rollback && !soc_component_mark_match(component, stream, pm))
continue;
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
/* remove marked stream */
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 3f97d1f132c6..37bc5867f81d 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -112,7 +112,7 @@ static umode_t soc_dev_attr_is_visible(struct kobject *kobj,
}
static const struct attribute_group soc_dapm_dev_group = {
- .attrs = soc_dapm_dev_attrs,
+ .attrs = snd_soc_dapm_dev_attrs,
.is_visible = soc_dev_attr_is_visible,
};
@@ -681,7 +681,7 @@ int snd_soc_suspend(struct device *dev)
soc_dapm_suspend_resume(card, SND_SOC_DAPM_STREAM_SUSPEND);
/* Recheck all endpoints too, their state is affected by suspend */
- dapm_mark_endpoints_dirty(card);
+ snd_soc_dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
/* suspend all COMPONENTs */
@@ -778,7 +778,7 @@ static void soc_resume_deferred(struct work_struct *work)
dev_dbg(card->dev, "ASoC: resume work completed\n");
/* Recheck all endpoints too, their state is affected by suspend */
- dapm_mark_endpoints_dirty(card);
+ snd_soc_dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
/* userspace can access us now we are back as we were before */
@@ -1139,6 +1139,9 @@ sanity_check:
void snd_soc_remove_pcm_runtime(struct snd_soc_card *card,
struct snd_soc_pcm_runtime *rtd)
{
+ if (!rtd)
+ return;
+
lockdep_assert_held(&client_mutex);
/*
@@ -2134,18 +2137,13 @@ static void soc_cleanup_card_resources(struct snd_soc_card *card)
}
}
-static void snd_soc_unbind_card(struct snd_soc_card *card, bool unregister)
+static void snd_soc_unbind_card(struct snd_soc_card *card)
{
if (snd_soc_card_is_instantiated(card)) {
card->instantiated = false;
snd_soc_flush_all_delayed_work(card);
soc_cleanup_card_resources(card);
- if (!unregister)
- list_add(&card->list, &unbind_card_list);
- } else {
- if (unregister)
- list_del(&card->list);
}
}
@@ -2155,9 +2153,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
struct snd_soc_component *component;
int ret;
- mutex_lock(&client_mutex);
snd_soc_card_mutex_lock_root(card);
-
snd_soc_fill_dummy_dai(card);
snd_soc_dapm_init(&card->dapm, card, NULL);
@@ -2293,7 +2289,7 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
}
card->instantiated = 1;
- dapm_mark_endpoints_dirty(card);
+ snd_soc_dapm_mark_endpoints_dirty(card);
snd_soc_dapm_sync(&card->dapm);
/* deactivate pins to sleep state */
@@ -2304,9 +2300,49 @@ static int snd_soc_bind_card(struct snd_soc_card *card)
probe_end:
if (ret < 0)
soc_cleanup_card_resources(card);
-
snd_soc_card_mutex_unlock(card);
- mutex_unlock(&client_mutex);
+
+ return ret;
+}
+
+static void devm_card_bind_release(struct device *dev, void *res)
+{
+ snd_soc_unregister_card(*(struct snd_soc_card **)res);
+}
+
+static int devm_snd_soc_bind_card(struct device *dev, struct snd_soc_card *card)
+{
+ struct snd_soc_card **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_card_bind_release, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ ret = snd_soc_bind_card(card);
+ if (ret == 0 || ret == -EPROBE_DEFER) {
+ *ptr = card;
+ devres_add(dev, ptr);
+ } else {
+ devres_free(ptr);
+ }
+
+ return ret;
+}
+
+static int snd_soc_rebind_card(struct snd_soc_card *card)
+{
+ int ret;
+
+ if (card->devres_dev) {
+ devres_destroy(card->devres_dev, devm_card_bind_release, NULL, NULL);
+ ret = devm_snd_soc_bind_card(card->devres_dev, card);
+ } else {
+ ret = snd_soc_bind_card(card);
+ }
+
+ if (ret != -EPROBE_DEFER)
+ list_del_init(&card->list);
return ret;
}
@@ -2506,6 +2542,8 @@ EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
*/
int snd_soc_register_card(struct snd_soc_card *card)
{
+ int ret;
+
if (!card->name || !card->dev)
return -EINVAL;
@@ -2526,7 +2564,21 @@ int snd_soc_register_card(struct snd_soc_card *card)
mutex_init(&card->dapm_mutex);
mutex_init(&card->pcm_mutex);
- return snd_soc_bind_card(card);
+ mutex_lock(&client_mutex);
+
+ if (card->devres_dev) {
+ ret = devm_snd_soc_bind_card(card->devres_dev, card);
+ if (ret == -EPROBE_DEFER) {
+ list_add(&card->list, &unbind_card_list);
+ ret = 0;
+ }
+ } else {
+ ret = snd_soc_bind_card(card);
+ }
+
+ mutex_unlock(&client_mutex);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_register_card);
@@ -2539,7 +2591,8 @@ EXPORT_SYMBOL_GPL(snd_soc_register_card);
void snd_soc_unregister_card(struct snd_soc_card *card)
{
mutex_lock(&client_mutex);
- snd_soc_unbind_card(card, true);
+ snd_soc_unbind_card(card);
+ list_del(&card->list);
mutex_unlock(&client_mutex);
dev_dbg(card->dev, "ASoC: Unregistered card '%s'\n", card->name);
}
@@ -2554,6 +2607,7 @@ static char *fmt_single_name(struct device *dev, int *id)
const char *devname = dev_name(dev);
char *found, *name;
unsigned int id1, id2;
+ int __id;
if (devname == NULL)
return NULL;
@@ -2566,10 +2620,10 @@ static char *fmt_single_name(struct device *dev, int *id)
found = strstr(name, dev->driver->name);
if (found) {
/* get ID */
- if (sscanf(&found[strlen(dev->driver->name)], ".%d", id) == 1) {
+ if (sscanf(&found[strlen(dev->driver->name)], ".%d", &__id) == 1) {
/* discard ID from name if ID == -1 */
- if (*id == -1)
+ if (__id == -1)
found[strlen(dev->driver->name)] = '\0';
}
@@ -2577,16 +2631,19 @@ static char *fmt_single_name(struct device *dev, int *id)
} else if (sscanf(name, "%x-%x", &id1, &id2) == 2) {
/* create unique ID number from I2C addr and bus */
- *id = ((id1 & 0xffff) << 16) + id2;
+ __id = ((id1 & 0xffff) << 16) + id2;
devm_kfree(dev, name);
/* sanitize component name for DAI link creation */
name = devm_kasprintf(dev, GFP_KERNEL, "%s.%s", dev->driver->name, devname);
} else {
- *id = 0;
+ __id = 0;
}
+ if (id)
+ *id = __id;
+
return name;
}
@@ -2753,23 +2810,19 @@ static void convert_endianness_formats(struct snd_soc_pcm_stream *stream)
stream->formats |= endianness_format_map[i];
}
-static void snd_soc_try_rebind_card(void)
-{
- struct snd_soc_card *card, *c;
-
- list_for_each_entry_safe(card, c, &unbind_card_list, list)
- if (!snd_soc_bind_card(card))
- list_del(&card->list);
-}
-
static void snd_soc_del_component_unlocked(struct snd_soc_component *component)
{
struct snd_soc_card *card = component->card;
+ bool instantiated;
snd_soc_unregister_dais(component);
- if (card)
- snd_soc_unbind_card(card, false);
+ if (card) {
+ instantiated = card->instantiated;
+ snd_soc_unbind_card(card);
+ if (instantiated)
+ list_add(&card->list, &unbind_card_list);
+ }
list_del(&component->list);
}
@@ -2785,7 +2838,7 @@ int snd_soc_component_initialize(struct snd_soc_component *component,
mutex_init(&component->io_mutex);
if (!component->name) {
- component->name = fmt_single_name(dev, &component->id);
+ component->name = fmt_single_name(dev, NULL);
if (!component->name) {
dev_err(dev, "ASoC: Failed to allocate name\n");
return -ENOMEM;
@@ -2808,6 +2861,7 @@ int snd_soc_add_component(struct snd_soc_component *component,
struct snd_soc_dai_driver *dai_drv,
int num_dai)
{
+ struct snd_soc_card *card, *c;
int ret;
int i;
@@ -2838,15 +2892,14 @@ int snd_soc_add_component(struct snd_soc_component *component,
/* see for_each_component */
list_add(&component->list, &component_list);
+ list_for_each_entry_safe(card, c, &unbind_card_list, list)
+ snd_soc_rebind_card(card);
+
err_cleanup:
if (ret < 0)
snd_soc_del_component_unlocked(component);
mutex_unlock(&client_mutex);
-
- if (ret == 0)
- snd_soc_try_rebind_card();
-
return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_add_component);
@@ -2881,34 +2934,14 @@ EXPORT_SYMBOL_GPL(snd_soc_register_component);
void snd_soc_unregister_component_by_driver(struct device *dev,
const struct snd_soc_component_driver *component_driver)
{
- struct snd_soc_component *component;
+ const char *driver_name = NULL;
- if (!component_driver)
- return;
+ if (component_driver)
+ driver_name = component_driver->name;
mutex_lock(&client_mutex);
- component = snd_soc_lookup_component_nolocked(dev, component_driver->name);
- if (!component)
- goto out;
-
- snd_soc_del_component_unlocked(component);
-
-out:
- mutex_unlock(&client_mutex);
-}
-EXPORT_SYMBOL_GPL(snd_soc_unregister_component_by_driver);
-
-/**
- * snd_soc_unregister_component - Unregister all related component
- * from the ASoC core
- *
- * @dev: The device to unregister
- */
-void snd_soc_unregister_component(struct device *dev)
-{
- mutex_lock(&client_mutex);
while (1) {
- struct snd_soc_component *component = snd_soc_lookup_component_nolocked(dev, NULL);
+ struct snd_soc_component *component = snd_soc_lookup_component_nolocked(dev, driver_name);
if (!component)
break;
@@ -2917,7 +2950,7 @@ void snd_soc_unregister_component(struct device *dev)
}
mutex_unlock(&client_mutex);
}
-EXPORT_SYMBOL_GPL(snd_soc_unregister_component);
+EXPORT_SYMBOL_GPL(snd_soc_unregister_component_by_driver);
/* Retrieve a card's name from device tree */
int snd_soc_of_parse_card_name(struct snd_soc_card *card,
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c
index a210089747d0..32f46a38682b 100644
--- a/sound/soc/soc-dai.c
+++ b/sound/soc/soc-dai.c
@@ -259,13 +259,15 @@ int snd_soc_dai_set_tdm_slot(struct snd_soc_dai *dai,
&rx_mask,
};
- if (dai->driver->ops &&
- dai->driver->ops->xlate_tdm_slot_mask)
- ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
- else
- ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
- if (ret)
- goto err;
+ if (slots) {
+ if (dai->driver->ops &&
+ dai->driver->ops->xlate_tdm_slot_mask)
+ ret = dai->driver->ops->xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
+ else
+ ret = snd_soc_xlate_tdm_slot_mask(slots, &tx_mask, &rx_mask);
+ if (ret)
+ goto err;
+ }
for_each_pcm_streams(stream)
snd_soc_dai_tdm_mask_set(dai, stream, *tdm_mask[stream]);
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index b7818388984e..a37d44cd04c6 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -49,22 +49,6 @@
for ((dir) = SND_SOC_DAPM_DIR_IN; (dir) <= SND_SOC_DAPM_DIR_OUT; \
(dir)++)
-static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
- const char *control,
- int (*connected)(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink));
-
-struct snd_soc_dapm_widget *
-snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
- const struct snd_soc_dapm_widget *widget);
-
-struct snd_soc_dapm_widget *
-snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
- const struct snd_soc_dapm_widget *widget);
-
-static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg);
-
/* dapm power sequences - make this per codec in the future */
static int dapm_up_seq[] = {
[snd_soc_dapm_pre] = 1,
@@ -222,7 +206,7 @@ static __always_inline void dapm_widget_invalidate_paths(
list_for_each_entry(w, &list, work_list) {
snd_soc_dapm_widget_for_each_path(w, dir, p) {
- if (p->is_supply || p->weak || !p->connect)
+ if (p->is_supply || !p->connect)
continue;
node = p->node[rdir];
if (node->endpoints[dir] != -1) {
@@ -284,7 +268,7 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
* Weak paths or supply paths do not influence the number of input or
* output paths of their neighbors.
*/
- if (p->weak || p->is_supply)
+ if (p->is_supply)
return;
/*
@@ -299,7 +283,7 @@ static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
dapm_widget_invalidate_output_paths(p->source);
}
-void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
+void snd_soc_dapm_mark_endpoints_dirty(struct snd_soc_card *card)
{
struct snd_soc_dapm_widget *w;
@@ -354,6 +338,320 @@ struct dapm_kcontrol_data {
struct snd_soc_dapm_widget_list *wlist;
};
+static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg)
+{
+ if (!dapm->component)
+ return -EIO;
+ return snd_soc_component_read(dapm->component, reg);
+}
+
+/* set up initial codec paths */
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
+ int nth_path)
+{
+ struct soc_mixer_control *mc = (struct soc_mixer_control *)
+ p->sink->kcontrol_news[i].private_value;
+ unsigned int reg = mc->reg;
+ unsigned int invert = mc->invert;
+
+ if (reg != SND_SOC_NOPM) {
+ unsigned int shift = mc->shift;
+ unsigned int max = mc->max;
+ unsigned int mask = (1 << fls(max)) - 1;
+ unsigned int val = soc_dapm_read(p->sink->dapm, reg);
+
+ /*
+ * The nth_path argument allows this function to know
+ * which path of a kcontrol it is setting the initial
+ * status for. Ideally this would support any number
+ * of paths and channels. But since kcontrols only come
+ * in mono and stereo variants, we are limited to 2
+ * channels.
+ *
+ * The following code assumes for stereo controls the
+ * first path is the left channel, and all remaining
+ * paths are the right channel.
+ */
+ if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
+ if (reg != mc->rreg)
+ val = soc_dapm_read(p->sink->dapm, mc->rreg);
+ val = (val >> mc->rshift) & mask;
+ } else {
+ val = (val >> shift) & mask;
+ }
+ if (invert)
+ val = max - val;
+ p->connect = !!val;
+ } else {
+ /* since a virtual mixer has no backing registers to
+ * decide which path to connect, it will try to match
+ * with initial state. This is to ensure
+ * that the default mixer choice will be
+ * correctly powered up during initialization.
+ */
+ p->connect = invert;
+ }
+}
+
+/* connect mux widget to its interconnecting audio paths */
+static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_path *path, const char *control_name,
+ struct snd_soc_dapm_widget *w)
+{
+ const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int item;
+ int i;
+
+ if (e->reg != SND_SOC_NOPM) {
+ unsigned int val;
+
+ val = soc_dapm_read(dapm, e->reg);
+ val = (val >> e->shift_l) & e->mask;
+ item = snd_soc_enum_val_to_item(e, val);
+ } else {
+ /* since a virtual mux has no backing registers to
+ * decide which path to connect, it will try to match
+ * with the first enumeration. This is to ensure
+ * that the default mux choice (the first) will be
+ * correctly powered up during initialization.
+ */
+ item = 0;
+ }
+
+ i = match_string(e->texts, e->items, control_name);
+ if (i < 0)
+ return -ENODEV;
+
+ path->name = e->texts[i];
+ path->connect = (i == item);
+ return 0;
+
+}
+
+/* connect mixer widget to its interconnecting audio paths */
+static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_path *path, const char *control_name)
+{
+ int i, nth_path = 0;
+
+ /* search for mixer kcontrol */
+ for (i = 0; i < path->sink->num_kcontrols; i++) {
+ if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
+ path->name = path->sink->kcontrol_news[i].name;
+ dapm_set_mixer_path_status(path, i, nth_path++);
+ return 0;
+ }
+ }
+ return -ENODEV;
+}
+
+/*
+ * dapm_update_widget_flags() - Re-compute widget sink and source flags
+ * @w: The widget for which to update the flags
+ *
+ * Some widgets have a dynamic category which depends on which neighbors they
+ * are connected to. This function update the category for these widgets.
+ *
+ * This function must be called whenever a path is added or removed to a widget.
+ */
+static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
+{
+ enum snd_soc_dapm_direction dir;
+ struct snd_soc_dapm_path *p;
+ unsigned int ep;
+
+ switch (w->id) {
+ case snd_soc_dapm_input:
+ /* On a fully routed card an input is never a source */
+ if (w->dapm->card->fully_routed)
+ return;
+ ep = SND_SOC_DAPM_EP_SOURCE;
+ snd_soc_dapm_widget_for_each_source_path(w, p) {
+ if (p->source->id == snd_soc_dapm_micbias ||
+ p->source->id == snd_soc_dapm_mic ||
+ p->source->id == snd_soc_dapm_line ||
+ p->source->id == snd_soc_dapm_output) {
+ ep = 0;
+ break;
+ }
+ }
+ break;
+ case snd_soc_dapm_output:
+ /* On a fully routed card a output is never a sink */
+ if (w->dapm->card->fully_routed)
+ return;
+ ep = SND_SOC_DAPM_EP_SINK;
+ snd_soc_dapm_widget_for_each_sink_path(w, p) {
+ if (p->sink->id == snd_soc_dapm_spk ||
+ p->sink->id == snd_soc_dapm_hp ||
+ p->sink->id == snd_soc_dapm_line ||
+ p->sink->id == snd_soc_dapm_input) {
+ ep = 0;
+ break;
+ }
+ }
+ break;
+ case snd_soc_dapm_line:
+ ep = 0;
+ snd_soc_dapm_for_each_direction(dir) {
+ if (!list_empty(&w->edges[dir]))
+ ep |= SND_SOC_DAPM_DIR_TO_EP(dir);
+ }
+ break;
+ default:
+ return;
+ }
+
+ w->is_ep = ep;
+}
+
+static int snd_soc_dapm_check_dynamic_path(
+ struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
+ const char *control)
+{
+ bool dynamic_source = false;
+ bool dynamic_sink = false;
+
+ if (!control)
+ return 0;
+
+ switch (source->id) {
+ case snd_soc_dapm_demux:
+ dynamic_source = true;
+ break;
+ default:
+ break;
+ }
+
+ switch (sink->id) {
+ case snd_soc_dapm_mux:
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
+ dynamic_sink = true;
+ break;
+ default:
+ break;
+ }
+
+ if (dynamic_source && dynamic_sink) {
+ dev_err(dapm->dev,
+ "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n",
+ source->name, control, sink->name);
+ return -EINVAL;
+ } else if (!dynamic_source && !dynamic_sink) {
+ dev_err(dapm->dev,
+ "Control not supported for path %s -> [%s] -> %s\n",
+ source->name, control, sink->name);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int snd_soc_dapm_add_path(
+ struct snd_soc_dapm_context *dapm,
+ struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
+ const char *control,
+ int (*connected)(struct snd_soc_dapm_widget *source,
+ struct snd_soc_dapm_widget *sink))
+{
+ enum snd_soc_dapm_direction dir;
+ struct snd_soc_dapm_path *path;
+ int ret;
+
+ if (wsink->is_supply && !wsource->is_supply) {
+ dev_err(dapm->dev,
+ "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ if (connected && !wsource->is_supply) {
+ dev_err(dapm->dev,
+ "connected() callback only supported for supply widgets (%s -> %s)\n",
+ wsource->name, wsink->name);
+ return -EINVAL;
+ }
+
+ if (wsource->is_supply && control) {
+ dev_err(dapm->dev,
+ "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
+ wsource->name, control, wsink->name);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
+ if (ret)
+ return ret;
+
+ path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
+ if (!path)
+ return -ENOMEM;
+
+ path->node[SND_SOC_DAPM_DIR_IN] = wsource;
+ path->node[SND_SOC_DAPM_DIR_OUT] = wsink;
+
+ path->connected = connected;
+ INIT_LIST_HEAD(&path->list);
+ INIT_LIST_HEAD(&path->list_kcontrol);
+
+ if (wsource->is_supply || wsink->is_supply)
+ path->is_supply = 1;
+
+ /* connect static paths */
+ if (control == NULL) {
+ path->connect = 1;
+ } else {
+ switch (wsource->id) {
+ case snd_soc_dapm_demux:
+ ret = dapm_connect_mux(dapm, path, control, wsource);
+ if (ret)
+ goto err;
+ break;
+ default:
+ break;
+ }
+
+ switch (wsink->id) {
+ case snd_soc_dapm_mux:
+ ret = dapm_connect_mux(dapm, path, control, wsink);
+ if (ret != 0)
+ goto err;
+ break;
+ case snd_soc_dapm_switch:
+ case snd_soc_dapm_mixer:
+ case snd_soc_dapm_mixer_named_ctl:
+ ret = dapm_connect_mixer(dapm, path, control);
+ if (ret != 0)
+ goto err;
+ break;
+ default:
+ break;
+ }
+ }
+
+ list_add(&path->list, &dapm->card->paths);
+
+ snd_soc_dapm_for_each_direction(dir)
+ list_add(&path->list_node[dir], &path->node[dir]->edges[dir]);
+
+ snd_soc_dapm_for_each_direction(dir) {
+ dapm_update_widget_flags(path->node[dir]);
+ dapm_mark_dirty(path->node[dir], "Route added");
+ }
+
+ if (snd_soc_card_is_instantiated(dapm->card) && path->connect)
+ dapm_path_invalidate(path);
+
+ return 0;
+err:
+ kfree(path);
+ return ret;
+}
+
static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *kcontrol, const char *ctrl_name)
{
@@ -626,13 +924,6 @@ static const char *soc_dapm_prefix(struct snd_soc_dapm_context *dapm)
return dapm->component->name_prefix;
}
-static unsigned int soc_dapm_read(struct snd_soc_dapm_context *dapm, int reg)
-{
- if (!dapm->component)
- return -EIO;
- return snd_soc_component_read(dapm->component, reg);
-}
-
static int soc_dapm_update_bits(struct snd_soc_dapm_context *dapm,
int reg, unsigned int mask, unsigned int value)
{
@@ -739,107 +1030,11 @@ static int snd_soc_dapm_set_bias_level(struct snd_soc_dapm_context *dapm,
out:
trace_snd_soc_bias_level_done(dapm, level);
- return ret;
-}
-
-/* connect mux widget to its interconnecting audio paths */
-static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_path *path, const char *control_name,
- struct snd_soc_dapm_widget *w)
-{
- const struct snd_kcontrol_new *kcontrol = &w->kcontrol_news[0];
- struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- unsigned int item;
- int i;
-
- if (e->reg != SND_SOC_NOPM) {
- unsigned int val;
- val = soc_dapm_read(dapm, e->reg);
- val = (val >> e->shift_l) & e->mask;
- item = snd_soc_enum_val_to_item(e, val);
- } else {
- /* since a virtual mux has no backing registers to
- * decide which path to connect, it will try to match
- * with the first enumeration. This is to ensure
- * that the default mux choice (the first) will be
- * correctly powered up during initialization.
- */
- item = 0;
- }
-
- i = match_string(e->texts, e->items, control_name);
- if (i < 0)
- return -ENODEV;
-
- path->name = e->texts[i];
- path->connect = (i == item);
- return 0;
-
-}
-
-/* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i,
- int nth_path)
-{
- struct soc_mixer_control *mc = (struct soc_mixer_control *)
- p->sink->kcontrol_news[i].private_value;
- unsigned int reg = mc->reg;
- unsigned int invert = mc->invert;
-
- if (reg != SND_SOC_NOPM) {
- unsigned int shift = mc->shift;
- unsigned int max = mc->max;
- unsigned int mask = (1 << fls(max)) - 1;
- unsigned int val = soc_dapm_read(p->sink->dapm, reg);
-
- /*
- * The nth_path argument allows this function to know
- * which path of a kcontrol it is setting the initial
- * status for. Ideally this would support any number
- * of paths and channels. But since kcontrols only come
- * in mono and stereo variants, we are limited to 2
- * channels.
- *
- * The following code assumes for stereo controls the
- * first path is the left channel, and all remaining
- * paths are the right channel.
- */
- if (snd_soc_volsw_is_stereo(mc) && nth_path > 0) {
- if (reg != mc->rreg)
- val = soc_dapm_read(p->sink->dapm, mc->rreg);
- val = (val >> mc->rshift) & mask;
- } else {
- val = (val >> shift) & mask;
- }
- if (invert)
- val = max - val;
- p->connect = !!val;
- } else {
- /* since a virtual mixer has no backing registers to
- * decide which path to connect, it will try to match
- * with initial state. This is to ensure
- * that the default mixer choice will be
- * correctly powered up during initialization.
- */
- p->connect = invert;
- }
-}
-
-/* connect mixer widget to its interconnecting audio paths */
-static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_path *path, const char *control_name)
-{
- int i, nth_path = 0;
+ /* success */
+ if (ret == 0)
+ snd_soc_dapm_init_bias_level(dapm, level);
- /* search for mixer kcontrol */
- for (i = 0; i < path->sink->num_kcontrols; i++) {
- if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
- path->name = path->sink->kcontrol_news[i].name;
- dapm_set_mixer_path_status(path, i, nth_path++);
- return 0;
- }
- }
- return -ENODEV;
+ return ret;
}
static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
@@ -1170,7 +1365,7 @@ static void invalidate_paths_ep(struct snd_soc_dapm_widget *widget,
widget->endpoints[dir] = -1;
snd_soc_dapm_widget_for_each_path(widget, rdir, path) {
- if (path->weak || path->is_supply)
+ if (path->is_supply)
continue;
if (path->walking)
@@ -1225,7 +1420,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
snd_soc_dapm_widget_for_each_path(widget, rdir, path) {
DAPM_UPDATE_STAT(widget, neighbour_checks);
- if (path->weak || path->is_supply)
+ if (path->is_supply)
continue;
if (path->walking)
@@ -1346,8 +1541,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_dai_free_widgets);
/*
* Handler for regulator supply widget.
*/
-int dapm_regulator_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+int snd_soc_dapm_regulator_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
int ret;
@@ -1375,13 +1570,13 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
return regulator_disable_deferred(w->regulator, w->shift);
}
}
-EXPORT_SYMBOL_GPL(dapm_regulator_event);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_regulator_event);
/*
* Handler for pinctrl widget.
*/
-int dapm_pinctrl_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+int snd_soc_dapm_pinctrl_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_dapm_pinctrl_priv *priv = w->priv;
struct pinctrl *p = w->pinctrl;
@@ -1400,13 +1595,13 @@ int dapm_pinctrl_event(struct snd_soc_dapm_widget *w,
return pinctrl_select_state(p, s);
}
-EXPORT_SYMBOL_GPL(dapm_pinctrl_event);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_pinctrl_event);
/*
* Handler for clock supply widget.
*/
-int dapm_clock_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event)
+int snd_soc_dapm_clock_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
{
if (!w->clk)
return -EIO;
@@ -1422,7 +1617,7 @@ int dapm_clock_event(struct snd_soc_dapm_widget *w,
return 0;
}
-EXPORT_SYMBOL_GPL(dapm_clock_event);
+EXPORT_SYMBOL_GPL(snd_soc_dapm_clock_event);
static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
{
@@ -1462,9 +1657,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
snd_soc_dapm_widget_for_each_sink_path(w, path) {
DAPM_UPDATE_STAT(w, neighbour_checks);
- if (path->weak)
- continue;
-
if (path->connected &&
!path->connected(path->source, path->sink))
continue;
@@ -1797,29 +1989,29 @@ static void dapm_widget_update(struct snd_soc_card *card, struct snd_soc_dapm_up
*/
static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
{
- struct snd_soc_dapm_context *d = data;
+ struct snd_soc_dapm_context *dapm = data;
int ret;
/* If we're off and we're not supposed to go into STANDBY */
- if (d->bias_level == SND_SOC_BIAS_OFF &&
- d->target_bias_level != SND_SOC_BIAS_OFF) {
- if (d->dev && cookie)
- pm_runtime_get_sync(d->dev);
+ if (dapm->bias_level == SND_SOC_BIAS_OFF &&
+ dapm->target_bias_level != SND_SOC_BIAS_OFF) {
+ if (dapm->dev && cookie)
+ pm_runtime_get_sync(dapm->dev);
- ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
+ ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY);
if (ret != 0)
- dev_err(d->dev,
+ dev_err(dapm->dev,
"ASoC: Failed to turn on bias: %d\n", ret);
}
/* Prepare for a transition to ON or away from ON */
- if ((d->target_bias_level == SND_SOC_BIAS_ON &&
- d->bias_level != SND_SOC_BIAS_ON) ||
- (d->target_bias_level != SND_SOC_BIAS_ON &&
- d->bias_level == SND_SOC_BIAS_ON)) {
- ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_PREPARE);
+ if ((dapm->target_bias_level == SND_SOC_BIAS_ON &&
+ dapm->bias_level != SND_SOC_BIAS_ON) ||
+ (dapm->target_bias_level != SND_SOC_BIAS_ON &&
+ dapm->bias_level == SND_SOC_BIAS_ON)) {
+ ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_PREPARE);
if (ret != 0)
- dev_err(d->dev,
+ dev_err(dapm->dev,
"ASoC: Failed to prepare bias: %d\n", ret);
}
}
@@ -1829,37 +2021,37 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
*/
static void dapm_post_sequence_async(void *data, async_cookie_t cookie)
{
- struct snd_soc_dapm_context *d = data;
+ struct snd_soc_dapm_context *dapm = data;
int ret;
/* If we just powered the last thing off drop to standby bias */
- if (d->bias_level == SND_SOC_BIAS_PREPARE &&
- (d->target_bias_level == SND_SOC_BIAS_STANDBY ||
- d->target_bias_level == SND_SOC_BIAS_OFF)) {
- ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_STANDBY);
+ if (dapm->bias_level == SND_SOC_BIAS_PREPARE &&
+ (dapm->target_bias_level == SND_SOC_BIAS_STANDBY ||
+ dapm->target_bias_level == SND_SOC_BIAS_OFF)) {
+ ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_STANDBY);
if (ret != 0)
- dev_err(d->dev, "ASoC: Failed to apply standby bias: %d\n",
+ dev_err(dapm->dev, "ASoC: Failed to apply standby bias: %d\n",
ret);
}
/* If we're in standby and can support bias off then do that */
- if (d->bias_level == SND_SOC_BIAS_STANDBY &&
- d->target_bias_level == SND_SOC_BIAS_OFF) {
- ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_OFF);
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY &&
+ dapm->target_bias_level == SND_SOC_BIAS_OFF) {
+ ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_OFF);
if (ret != 0)
- dev_err(d->dev, "ASoC: Failed to turn off bias: %d\n",
+ dev_err(dapm->dev, "ASoC: Failed to turn off bias: %d\n",
ret);
- if (d->dev && cookie)
- pm_runtime_put(d->dev);
+ if (dapm->dev && cookie)
+ pm_runtime_put(dapm->dev);
}
/* If we just powered up then move to active bias */
- if (d->bias_level == SND_SOC_BIAS_PREPARE &&
- d->target_bias_level == SND_SOC_BIAS_ON) {
- ret = snd_soc_dapm_set_bias_level(d, SND_SOC_BIAS_ON);
+ if (dapm->bias_level == SND_SOC_BIAS_PREPARE &&
+ dapm->target_bias_level == SND_SOC_BIAS_ON) {
+ ret = snd_soc_dapm_set_bias_level(dapm, SND_SOC_BIAS_ON);
if (ret != 0)
- dev_err(d->dev, "ASoC: Failed to apply active bias: %d\n",
+ dev_err(dapm->dev, "ASoC: Failed to apply active bias: %d\n",
ret);
}
}
@@ -2440,10 +2632,10 @@ int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
-static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
+static ssize_t dapm_widget_show_component(struct snd_soc_component *component,
char *buf, int count)
{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
struct snd_soc_dapm_widget *w;
char *state = "not set";
@@ -2451,10 +2643,10 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
* we're checking for that case specifically here but in future
* we will ensure that the dummy component looks like others.
*/
- if (!cmpnt->card)
+ if (!component->card)
return 0;
- for_each_card_widgets(cmpnt->card, w) {
+ for_each_card_widgets(component->card, w) {
if (w->dapm != dapm)
continue;
@@ -2515,9 +2707,9 @@ static ssize_t dapm_widget_show(struct device *dev,
snd_soc_dapm_mutex_lock_root(rtd->card);
for_each_rtd_codec_dais(rtd, i, codec_dai) {
- struct snd_soc_component *cmpnt = codec_dai->component;
+ struct snd_soc_component *component = codec_dai->component;
- count = dapm_widget_show_component(cmpnt, buf, count);
+ count = dapm_widget_show_component(component, buf, count);
}
snd_soc_dapm_mutex_unlock(rtd->card);
@@ -2527,7 +2719,7 @@ static ssize_t dapm_widget_show(struct device *dev,
static DEVICE_ATTR_RO(dapm_widget);
-struct attribute *soc_dapm_dev_attrs[] = {
+struct attribute *snd_soc_dapm_dev_attrs[] = {
&dev_attr_dapm_widget.attr,
NULL
};
@@ -2794,210 +2986,6 @@ int snd_soc_dapm_widget_name_cmp(struct snd_soc_dapm_widget *widget, const char
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_widget_name_cmp);
-/*
- * dapm_update_widget_flags() - Re-compute widget sink and source flags
- * @w: The widget for which to update the flags
- *
- * Some widgets have a dynamic category which depends on which neighbors they
- * are connected to. This function update the category for these widgets.
- *
- * This function must be called whenever a path is added or removed to a widget.
- */
-static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
-{
- enum snd_soc_dapm_direction dir;
- struct snd_soc_dapm_path *p;
- unsigned int ep;
-
- switch (w->id) {
- case snd_soc_dapm_input:
- /* On a fully routed card an input is never a source */
- if (w->dapm->card->fully_routed)
- return;
- ep = SND_SOC_DAPM_EP_SOURCE;
- snd_soc_dapm_widget_for_each_source_path(w, p) {
- if (p->source->id == snd_soc_dapm_micbias ||
- p->source->id == snd_soc_dapm_mic ||
- p->source->id == snd_soc_dapm_line ||
- p->source->id == snd_soc_dapm_output) {
- ep = 0;
- break;
- }
- }
- break;
- case snd_soc_dapm_output:
- /* On a fully routed card a output is never a sink */
- if (w->dapm->card->fully_routed)
- return;
- ep = SND_SOC_DAPM_EP_SINK;
- snd_soc_dapm_widget_for_each_sink_path(w, p) {
- if (p->sink->id == snd_soc_dapm_spk ||
- p->sink->id == snd_soc_dapm_hp ||
- p->sink->id == snd_soc_dapm_line ||
- p->sink->id == snd_soc_dapm_input) {
- ep = 0;
- break;
- }
- }
- break;
- case snd_soc_dapm_line:
- ep = 0;
- snd_soc_dapm_for_each_direction(dir) {
- if (!list_empty(&w->edges[dir]))
- ep |= SND_SOC_DAPM_DIR_TO_EP(dir);
- }
- break;
- default:
- return;
- }
-
- w->is_ep = ep;
-}
-
-static int snd_soc_dapm_check_dynamic_path(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_widget *source, struct snd_soc_dapm_widget *sink,
- const char *control)
-{
- bool dynamic_source = false;
- bool dynamic_sink = false;
-
- if (!control)
- return 0;
-
- switch (source->id) {
- case snd_soc_dapm_demux:
- dynamic_source = true;
- break;
- default:
- break;
- }
-
- switch (sink->id) {
- case snd_soc_dapm_mux:
- case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer:
- case snd_soc_dapm_mixer_named_ctl:
- dynamic_sink = true;
- break;
- default:
- break;
- }
-
- if (dynamic_source && dynamic_sink) {
- dev_err(dapm->dev,
- "Direct connection between demux and mixer/mux not supported for path %s -> [%s] -> %s\n",
- source->name, control, sink->name);
- return -EINVAL;
- } else if (!dynamic_source && !dynamic_sink) {
- dev_err(dapm->dev,
- "Control not supported for path %s -> [%s] -> %s\n",
- source->name, control, sink->name);
- return -EINVAL;
- }
-
- return 0;
-}
-
-static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
- struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
- const char *control,
- int (*connected)(struct snd_soc_dapm_widget *source,
- struct snd_soc_dapm_widget *sink))
-{
- enum snd_soc_dapm_direction dir;
- struct snd_soc_dapm_path *path;
- int ret;
-
- if (wsink->is_supply && !wsource->is_supply) {
- dev_err(dapm->dev,
- "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
- wsource->name, wsink->name);
- return -EINVAL;
- }
-
- if (connected && !wsource->is_supply) {
- dev_err(dapm->dev,
- "connected() callback only supported for supply widgets (%s -> %s)\n",
- wsource->name, wsink->name);
- return -EINVAL;
- }
-
- if (wsource->is_supply && control) {
- dev_err(dapm->dev,
- "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
- wsource->name, control, wsink->name);
- return -EINVAL;
- }
-
- ret = snd_soc_dapm_check_dynamic_path(dapm, wsource, wsink, control);
- if (ret)
- return ret;
-
- path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
- if (!path)
- return -ENOMEM;
-
- path->node[SND_SOC_DAPM_DIR_IN] = wsource;
- path->node[SND_SOC_DAPM_DIR_OUT] = wsink;
-
- path->connected = connected;
- INIT_LIST_HEAD(&path->list);
- INIT_LIST_HEAD(&path->list_kcontrol);
-
- if (wsource->is_supply || wsink->is_supply)
- path->is_supply = 1;
-
- /* connect static paths */
- if (control == NULL) {
- path->connect = 1;
- } else {
- switch (wsource->id) {
- case snd_soc_dapm_demux:
- ret = dapm_connect_mux(dapm, path, control, wsource);
- if (ret)
- goto err;
- break;
- default:
- break;
- }
-
- switch (wsink->id) {
- case snd_soc_dapm_mux:
- ret = dapm_connect_mux(dapm, path, control, wsink);
- if (ret != 0)
- goto err;
- break;
- case snd_soc_dapm_switch:
- case snd_soc_dapm_mixer:
- case snd_soc_dapm_mixer_named_ctl:
- ret = dapm_connect_mixer(dapm, path, control);
- if (ret != 0)
- goto err;
- break;
- default:
- break;
- }
- }
-
- list_add(&path->list, &dapm->card->paths);
-
- snd_soc_dapm_for_each_direction(dir)
- list_add(&path->list_node[dir], &path->node[dir]->edges[dir]);
-
- snd_soc_dapm_for_each_direction(dir) {
- dapm_update_widget_flags(path->node[dir]);
- dapm_mark_dirty(path->node[dir], "Route added");
- }
-
- if (snd_soc_card_is_instantiated(dapm->card) && path->connect)
- dapm_path_invalidate(path);
-
- return 0;
-err:
- kfree(path);
- return ret;
-}
-
static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_route *route)
{
@@ -3210,86 +3198,6 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);
-static int snd_soc_dapm_weak_route(struct snd_soc_dapm_context *dapm,
- const struct snd_soc_dapm_route *route)
-{
- struct snd_soc_dapm_widget *source = dapm_find_widget(dapm,
- route->source,
- true);
- struct snd_soc_dapm_widget *sink = dapm_find_widget(dapm,
- route->sink,
- true);
- struct snd_soc_dapm_path *path;
- int count = 0;
-
- if (!source) {
- dev_err(dapm->dev, "ASoC: Unable to find source %s for weak route\n",
- route->source);
- return -ENODEV;
- }
-
- if (!sink) {
- dev_err(dapm->dev, "ASoC: Unable to find sink %s for weak route\n",
- route->sink);
- return -ENODEV;
- }
-
- if (route->control || route->connected)
- dev_warn(dapm->dev, "ASoC: Ignoring control for weak route %s->%s\n",
- route->source, route->sink);
-
- snd_soc_dapm_widget_for_each_sink_path(source, path) {
- if (path->sink == sink) {
- path->weak = 1;
- count++;
- }
- }
-
- if (count == 0)
- dev_err(dapm->dev, "ASoC: No path found for weak route %s->%s\n",
- route->source, route->sink);
- if (count > 1)
- dev_warn(dapm->dev, "ASoC: %d paths found for weak route %s->%s\n",
- count, route->source, route->sink);
-
- return 0;
-}
-
-/**
- * snd_soc_dapm_weak_routes - Mark routes between DAPM widgets as weak
- * @dapm: DAPM context
- * @route: audio routes
- * @num: number of routes
- *
- * Mark existing routes matching those specified in the passed array
- * as being weak, meaning that they are ignored for the purpose of
- * power decisions. The main intended use case is for sidetone paths
- * which couple audio between other independent paths if they are both
- * active in order to make the combination work better at the user
- * level but which aren't intended to be "used".
- *
- * Note that CODEC drivers should not use this as sidetone type paths
- * can frequently also be used as bypass paths.
- */
-int snd_soc_dapm_weak_routes(struct snd_soc_dapm_context *dapm,
- const struct snd_soc_dapm_route *route, int num)
-{
- int i;
- int ret = 0;
-
- snd_soc_dapm_mutex_lock_root(dapm);
- for (i = 0; i < num; i++) {
- int err = snd_soc_dapm_weak_route(dapm, route);
- if (err)
- ret = err;
- route++;
- }
- snd_soc_dapm_mutex_unlock(dapm);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
-
/**
* snd_soc_dapm_new_widgets - add new dapm widgets
* @card: card to be checked for new dapm widgets
@@ -3626,11 +3534,25 @@ int snd_soc_dapm_info_pin_switch(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_info_pin_switch);
+static int __snd_soc_dapm_get_pin_switch(struct snd_soc_dapm_context *dapm,
+ const char *pin,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ snd_soc_dapm_mutex_lock(dapm);
+ ucontrol->value.integer.value[0] = snd_soc_dapm_get_pin_status(dapm, pin);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
/**
* snd_soc_dapm_get_pin_switch - Get information for a pin switch
*
* @kcontrol: mixer control
* @ucontrol: Value
+ *
+ * Callback to provide information for a pin switch added at the card
+ * level.
*/
int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -3638,40 +3560,82 @@ int snd_soc_dapm_get_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
const char *pin = (const char *)kcontrol->private_value;
- snd_soc_dapm_mutex_lock(card);
+ return __snd_soc_dapm_get_pin_switch(&card->dapm, pin, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch);
+
+/**
+ * snd_soc_dapm_get_component_pin_switch - Get information for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @ucontrol: Value
+ *
+ * Callback to provide information for a pin switch added at the component
+ * level.
+ */
+int snd_soc_dapm_get_component_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ const char *pin = (const char *)kcontrol->private_value;
- ucontrol->value.integer.value[0] =
- snd_soc_dapm_get_pin_status(&card->dapm, pin);
+ return __snd_soc_dapm_get_pin_switch(&component->dapm, pin, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_get_component_pin_switch);
- snd_soc_dapm_mutex_unlock(card);
+static int __snd_soc_dapm_put_pin_switch(struct snd_soc_dapm_context *dapm,
+ const char *pin,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ int ret;
- return 0;
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = __snd_soc_dapm_set_pin(dapm, pin, !!ucontrol->value.integer.value[0]);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ snd_soc_dapm_sync(dapm);
+
+ return ret;
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_get_pin_switch);
/**
* snd_soc_dapm_put_pin_switch - Set information for a pin switch
*
* @kcontrol: mixer control
* @ucontrol: Value
+ *
+ * Callback to provide information for a pin switch added at the card
+ * level.
*/
int snd_soc_dapm_put_pin_switch(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
const char *pin = (const char *)kcontrol->private_value;
- int ret;
- snd_soc_dapm_mutex_lock(card);
- ret = __snd_soc_dapm_set_pin(&card->dapm, pin,
- !!ucontrol->value.integer.value[0]);
- snd_soc_dapm_mutex_unlock(card);
-
- snd_soc_dapm_sync(&card->dapm);
- return ret;
+ return __snd_soc_dapm_put_pin_switch(&card->dapm, pin, ucontrol);
}
EXPORT_SYMBOL_GPL(snd_soc_dapm_put_pin_switch);
+/**
+ * snd_soc_dapm_put_component_pin_switch - Set information for a pin switch
+ *
+ * @kcontrol: mixer control
+ * @ucontrol: Value
+ *
+ * Callback to provide information for a pin switch added at the component
+ * level.
+ */
+int snd_soc_dapm_put_component_pin_switch(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ const char *pin = (const char *)kcontrol->private_value;
+
+ return __snd_soc_dapm_put_pin_switch(&component->dapm, pin, ucontrol);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_put_component_pin_switch);
+
struct snd_soc_dapm_widget *
snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
const struct snd_soc_dapm_widget *widget)
@@ -3708,7 +3672,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
}
/* set to sleep_state when initializing */
- dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD);
+ snd_soc_dapm_pinctrl_event(w, NULL, SND_SOC_DAPM_POST_PMD);
break;
case snd_soc_dapm_clock_supply:
w->clk = devm_clk_get(dapm->dev, widget->name);
@@ -4725,57 +4689,6 @@ int snd_soc_dapm_disable_pin(struct snd_soc_dapm_context *dapm,
EXPORT_SYMBOL_GPL(snd_soc_dapm_disable_pin);
/**
- * snd_soc_dapm_nc_pin_unlocked - permanently disable pin.
- * @dapm: DAPM context
- * @pin: pin name
- *
- * Marks the specified pin as being not connected, disabling it along
- * any parent or child widgets. At present this is identical to
- * snd_soc_dapm_disable_pin() but in future it will be extended to do
- * additional things such as disabling controls which only affect
- * paths through the pin.
- *
- * Requires external locking.
- *
- * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
- * do any widget power switching.
- */
-int snd_soc_dapm_nc_pin_unlocked(struct snd_soc_dapm_context *dapm,
- const char *pin)
-{
- return snd_soc_dapm_set_pin(dapm, pin, 0);
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin_unlocked);
-
-/**
- * snd_soc_dapm_nc_pin - permanently disable pin.
- * @dapm: DAPM context
- * @pin: pin name
- *
- * Marks the specified pin as being not connected, disabling it along
- * any parent or child widgets. At present this is identical to
- * snd_soc_dapm_disable_pin() but in future it will be extended to do
- * additional things such as disabling controls which only affect
- * paths through the pin.
- *
- * NOTE: snd_soc_dapm_sync() needs to be called after this for DAPM to
- * do any widget power switching.
- */
-int snd_soc_dapm_nc_pin(struct snd_soc_dapm_context *dapm, const char *pin)
-{
- int ret;
-
- snd_soc_dapm_mutex_lock(dapm);
-
- ret = snd_soc_dapm_set_pin(dapm, pin, 0);
-
- snd_soc_dapm_mutex_unlock(dapm);
-
- return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_nc_pin);
-
-/**
* snd_soc_dapm_get_pin_status - get audio pin status
* @dapm: DAPM context
* @pin: audio signal pin endpoint (or start point)
@@ -4835,7 +4748,6 @@ void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm)
dapm_free_widgets(dapm);
list_del(&dapm->list);
}
-EXPORT_SYMBOL_GPL(snd_soc_dapm_free);
void snd_soc_dapm_init(struct snd_soc_dapm_context *dapm,
struct snd_soc_card *card,
diff --git a/sound/soc/soc-devres.c b/sound/soc/soc-devres.c
index c6364caabc0e..d33f83ec24f2 100644
--- a/sound/soc/soc-devres.c
+++ b/sound/soc/soc-devres.c
@@ -83,6 +83,13 @@ int devm_snd_soc_register_card(struct device *dev, struct snd_soc_card *card)
}
EXPORT_SYMBOL_GPL(devm_snd_soc_register_card);
+int devm_snd_soc_register_deferrable_card(struct device *dev, struct snd_soc_card *card)
+{
+ card->devres_dev = dev;
+ return snd_soc_register_card(card);
+}
+EXPORT_SYMBOL_GPL(devm_snd_soc_register_deferrable_card);
+
#ifdef CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM
static void devm_dmaengine_pcm_release(struct device *dev, void *res)
diff --git a/sound/soc/soc-ops-test.c b/sound/soc/soc-ops-test.c
index dc1e482bba6a..1bafa5626d48 100644
--- a/sound/soc/soc-ops-test.c
+++ b/sound/soc/soc-ops-test.c
@@ -481,22 +481,27 @@ static void soc_ops_test_access(struct kunit *test)
.private_data = &priv->component,
.private_value = (unsigned long)&param->mc,
};
- struct snd_ctl_elem_value result;
unsigned int val;
int ret;
+ /* it is too large struct. use kzalloc() */
+ struct snd_ctl_elem_value *result;
+
+ result = kzalloc(sizeof(*result), GFP_KERNEL);
+ if (!result)
+ return;
ret = regmap_write(priv->component.regmap, 0x0, param->init);
KUNIT_ASSERT_FALSE(test, ret);
ret = regmap_write(priv->component.regmap, 0x1, param->init);
KUNIT_ASSERT_FALSE(test, ret);
- result.value.integer.value[0] = param->lctl;
- result.value.integer.value[1] = param->rctl;
+ result->value.integer.value[0] = param->lctl;
+ result->value.integer.value[1] = param->rctl;
- ret = param->put(&kctl, &result);
+ ret = param->put(&kctl, result);
KUNIT_ASSERT_EQ(test, ret, param->ret);
if (ret < 0)
- return;
+ goto end;
ret = regmap_read(priv->component.regmap, 0x0, &val);
KUNIT_ASSERT_FALSE(test, ret);
@@ -506,17 +511,19 @@ static void soc_ops_test_access(struct kunit *test)
KUNIT_ASSERT_FALSE(test, ret);
KUNIT_EXPECT_EQ(test, val, (param->init & ~param->rmask) | param->rreg);
- result.value.integer.value[0] = 0;
- result.value.integer.value[1] = 0;
+ result->value.integer.value[0] = 0;
+ result->value.integer.value[1] = 0;
- ret = param->get(&kctl, &result);
+ ret = param->get(&kctl, result);
KUNIT_ASSERT_GE(test, ret, 0);
- KUNIT_EXPECT_EQ(test, result.value.integer.value[0], param->lctl);
+ KUNIT_EXPECT_EQ(test, result->value.integer.value[0], param->lctl);
if (param->layout != SOC_OPS_TEST_SINGLE)
- KUNIT_EXPECT_EQ(test, result.value.integer.value[1], param->rctl);
+ KUNIT_EXPECT_EQ(test, result->value.integer.value[1], param->rctl);
else
- KUNIT_EXPECT_EQ(test, result.value.integer.value[1], 0);
+ KUNIT_EXPECT_EQ(test, result->value.integer.value[1], 0);
+end:
+ kfree(result);
}
KUNIT_ARRAY_PARAM(all_info_tests, all_info_test_params, info_test_desc);
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index 8d4dd11c9aef..a629e0eacb20 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -399,28 +399,32 @@ EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
static int snd_soc_clip_to_platform_max(struct snd_kcontrol *kctl)
{
struct soc_mixer_control *mc = (struct soc_mixer_control *)kctl->private_value;
- struct snd_ctl_elem_value uctl;
+ struct snd_ctl_elem_value *uctl;
int ret;
if (!mc->platform_max)
return 0;
- ret = kctl->get(kctl, &uctl);
+ uctl = kzalloc(sizeof(*uctl), GFP_KERNEL);
+ if (!uctl)
+ return -ENOMEM;
+
+ ret = kctl->get(kctl, uctl);
if (ret < 0)
- return ret;
+ goto out;
- if (uctl.value.integer.value[0] > mc->platform_max)
- uctl.value.integer.value[0] = mc->platform_max;
+ if (uctl->value.integer.value[0] > mc->platform_max)
+ uctl->value.integer.value[0] = mc->platform_max;
if (snd_soc_volsw_is_stereo(mc) &&
- uctl.value.integer.value[1] > mc->platform_max)
- uctl.value.integer.value[1] = mc->platform_max;
+ uctl->value.integer.value[1] > mc->platform_max)
+ uctl->value.integer.value[1] = mc->platform_max;
- ret = kctl->put(kctl, &uctl);
- if (ret < 0)
- return ret;
+ ret = kctl->put(kctl, uctl);
- return 0;
+out:
+ kfree(uctl);
+ return ret;
}
/**
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 43835197d1fe..2c21fd528afd 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -2510,17 +2510,6 @@ static int dpcm_fe_dai_prepare(struct snd_pcm_substream *substream)
dpcm_set_fe_update_state(fe, stream, SND_SOC_DPCM_UPDATE_FE);
- /* there is no point preparing this FE if there are no BEs */
- if (list_empty(&fe->dpcm[stream].be_clients)) {
- /* dev_err_once() for visibility, dev_dbg() for debugging UCM profiles */
- dev_err_once(fe->dev, "ASoC: no backend DAIs enabled for %s, possibly missing ALSA mixer-based routing or UCM profile\n",
- fe->dai_link->name);
- dev_dbg(fe->dev, "ASoC: no backend DAIs enabled for %s\n",
- fe->dai_link->name);
- ret = -EINVAL;
- goto out;
- }
-
ret = dpcm_be_dai_prepare(fe, stream);
if (ret < 0)
goto out;
@@ -2776,11 +2765,23 @@ static int dpcm_fe_dai_open(struct snd_pcm_substream *fe_substream)
/* calculate valid and active FE <-> BE dpcms */
dpcm_add_paths(fe, stream, &list);
+ /* There is no point starting up this FE if there are no BEs. */
+ if (list_empty(&fe->dpcm[stream].be_clients)) {
+ /* dev_err_once() for visibility, dev_dbg() for debugging UCM profiles. */
+ dev_err_once(fe->dev, "ASoC: no backend DAIs enabled for %s, possibly missing ALSA mixer-based routing or UCM profile\n",
+ fe->dai_link->name);
+ dev_dbg(fe->dev, "ASoC: no backend DAIs enabled for %s\n", fe->dai_link->name);
+
+ ret = -EINVAL;
+ goto put_path;
+ }
+
ret = dpcm_fe_dai_startup(fe_substream);
if (ret < 0)
dpcm_fe_dai_cleanup(fe_substream);
dpcm_clear_pending_state(fe, stream);
+put_path:
dpcm_path_put(&list);
open_end:
snd_soc_dpcm_mutex_unlock(fe);
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 7b0b8531bb32..44b60324eaa2 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -429,8 +429,11 @@ static void soc_tplg_remove_link(struct snd_soc_component *comp,
dobj->unload(comp, dobj);
list_del(&dobj->list);
- snd_soc_remove_pcm_runtime(comp->card,
- snd_soc_get_pcm_runtime(comp->card, link));
+
+ /* Ignored links do not need to be removed, they are not added */
+ if (!link->ignore)
+ snd_soc_remove_pcm_runtime(comp->card,
+ snd_soc_get_pcm_runtime(comp->card, link));
}
/* unload dai link */
diff --git a/sound/soc/soc-usb.c b/sound/soc/soc-usb.c
new file mode 100644
index 000000000000..26baa66d29a8
--- /dev/null
+++ b/sound/soc/soc-usb.c
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved.
+ */
+#include <linux/of.h>
+#include <linux/usb.h>
+
+#include <sound/jack.h>
+#include <sound/soc-usb.h>
+
+#include "../usb/card.h"
+
+static DEFINE_MUTEX(ctx_mutex);
+static LIST_HEAD(usb_ctx_list);
+
+static struct device_node *snd_soc_find_phandle(struct device *dev)
+{
+ struct device_node *node;
+
+ node = of_parse_phandle(dev->of_node, "usb-soc-be", 0);
+ if (!node)
+ return ERR_PTR(-ENODEV);
+
+ return node;
+}
+
+static struct snd_soc_usb *snd_soc_usb_ctx_lookup(struct device_node *node)
+{
+ struct snd_soc_usb *ctx;
+
+ if (!node)
+ return NULL;
+
+ list_for_each_entry(ctx, &usb_ctx_list, list) {
+ if (ctx->component->dev->of_node == node)
+ return ctx;
+ }
+
+ return NULL;
+}
+
+static struct snd_soc_usb *snd_soc_find_usb_ctx(struct device *dev)
+{
+ struct snd_soc_usb *ctx;
+ struct device_node *node;
+
+ node = snd_soc_find_phandle(dev);
+ if (!IS_ERR(node)) {
+ ctx = snd_soc_usb_ctx_lookup(node);
+ of_node_put(node);
+ } else {
+ ctx = snd_soc_usb_ctx_lookup(dev->of_node);
+ }
+
+ return ctx ? ctx : NULL;
+}
+
+/* SOC USB sound kcontrols */
+/**
+ * snd_soc_usb_setup_offload_jack() - Create USB offloading jack
+ * @component: USB DPCM backend DAI component
+ * @jack: jack structure to create
+ *
+ * Creates a jack device for notifying userspace of the availability
+ * of an offload capable device.
+ *
+ * Returns 0 on success, negative on error.
+ *
+ */
+int snd_soc_usb_setup_offload_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack)
+{
+ int ret;
+
+ ret = snd_soc_card_jack_new(component->card, "USB Offload Jack",
+ SND_JACK_USB, jack);
+ if (ret < 0) {
+ dev_err(component->card->dev, "Unable to add USB offload jack: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+ if (ret) {
+ dev_err(component->card->dev, "Failed to set jack: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_setup_offload_jack);
+
+/**
+ * snd_soc_usb_update_offload_route - Find active USB offload path
+ * @dev: USB device to get offload status
+ * @card: USB card index
+ * @pcm: USB PCM device index
+ * @direction: playback or capture direction
+ * @path: pcm or card index
+ * @route: pointer to route output array
+ *
+ * Fetch the current status for the USB SND card and PCM device indexes
+ * specified. The "route" argument should be an array of integers being
+ * used for a kcontrol output. The first element should have the selected
+ * card index, and the second element should have the selected pcm device
+ * index.
+ */
+int snd_soc_usb_update_offload_route(struct device *dev, int card, int pcm,
+ int direction, enum snd_soc_usb_kctl path,
+ long *route)
+{
+ struct snd_soc_usb *ctx;
+ int ret = -ENODEV;
+
+ mutex_lock(&ctx_mutex);
+ ctx = snd_soc_find_usb_ctx(dev);
+ if (!ctx)
+ goto exit;
+
+ if (ctx->update_offload_route_info)
+ ret = ctx->update_offload_route_info(ctx->component, card, pcm,
+ direction, path, route);
+exit:
+ mutex_unlock(&ctx_mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_update_offload_route);
+
+/**
+ * snd_soc_usb_find_priv_data() - Retrieve private data stored
+ * @usbdev: device reference
+ *
+ * Fetch the private data stored in the USB SND SoC structure.
+ *
+ */
+void *snd_soc_usb_find_priv_data(struct device *usbdev)
+{
+ struct snd_soc_usb *ctx;
+
+ mutex_lock(&ctx_mutex);
+ ctx = snd_soc_find_usb_ctx(usbdev);
+ mutex_unlock(&ctx_mutex);
+
+ return ctx ? ctx->priv_data : NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_find_priv_data);
+
+/**
+ * snd_soc_usb_find_supported_format() - Check if audio format is supported
+ * @card_idx: USB sound chip array index
+ * @params: PCM parameters
+ * @direction: capture or playback
+ *
+ * Ensure that a requested audio profile from the ASoC side is able to be
+ * supported by the USB device.
+ *
+ * Return 0 on success, negative on error.
+ *
+ */
+int snd_soc_usb_find_supported_format(int card_idx,
+ struct snd_pcm_hw_params *params,
+ int direction)
+{
+ struct snd_usb_stream *as;
+
+ as = snd_usb_find_suppported_substream(card_idx, params, direction);
+ if (!as)
+ return -EOPNOTSUPP;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_find_supported_format);
+
+/**
+ * snd_soc_usb_allocate_port() - allocate a SoC USB port for offloading support
+ * @component: USB DPCM backend DAI component
+ * @data: private data
+ *
+ * Allocate and initialize a SoC USB port. The SoC USB port is used to communicate
+ * different USB audio devices attached, in order to start audio offloading handled
+ * by an ASoC entity. USB device plug in/out events are signaled with a
+ * notification, but don't directly impact the memory allocated for the SoC USB
+ * port.
+ *
+ */
+struct snd_soc_usb *snd_soc_usb_allocate_port(struct snd_soc_component *component,
+ void *data)
+{
+ struct snd_soc_usb *usb;
+
+ usb = kzalloc(sizeof(*usb), GFP_KERNEL);
+ if (!usb)
+ return ERR_PTR(-ENOMEM);
+
+ usb->component = component;
+ usb->priv_data = data;
+
+ return usb;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_allocate_port);
+
+/**
+ * snd_soc_usb_free_port() - free a SoC USB port used for offloading support
+ * @usb: allocated SoC USB port
+ *
+ * Free and remove the SoC USB port from the available list of ports. This will
+ * ensure that the communication between USB SND and ASoC is halted.
+ *
+ */
+void snd_soc_usb_free_port(struct snd_soc_usb *usb)
+{
+ snd_soc_usb_remove_port(usb);
+ kfree(usb);
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_free_port);
+
+/**
+ * snd_soc_usb_add_port() - Add a USB backend port
+ * @usb: soc usb port to add
+ *
+ * Register a USB backend DAI link to the USB SoC framework. Memory is allocated
+ * as part of the USB backend DAI link.
+ *
+ */
+void snd_soc_usb_add_port(struct snd_soc_usb *usb)
+{
+ mutex_lock(&ctx_mutex);
+ list_add_tail(&usb->list, &usb_ctx_list);
+ mutex_unlock(&ctx_mutex);
+
+ snd_usb_rediscover_devices();
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_add_port);
+
+/**
+ * snd_soc_usb_remove_port() - Remove a USB backend port
+ * @usb: soc usb port to remove
+ *
+ * Remove a USB backend DAI link from USB SoC. Memory is freed when USB backend
+ * DAI is removed, or when snd_soc_usb_free_port() is called.
+ *
+ */
+void snd_soc_usb_remove_port(struct snd_soc_usb *usb)
+{
+ struct snd_soc_usb *ctx, *tmp;
+
+ mutex_lock(&ctx_mutex);
+ list_for_each_entry_safe(ctx, tmp, &usb_ctx_list, list) {
+ if (ctx == usb) {
+ list_del(&ctx->list);
+ break;
+ }
+ }
+ mutex_unlock(&ctx_mutex);
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_remove_port);
+
+/**
+ * snd_soc_usb_connect() - Notification of USB device connection
+ * @usbdev: USB bus device
+ * @sdev: USB SND device to add
+ *
+ * Notify of a new USB SND device connection. The sdev->card_idx can be used to
+ * handle how the DPCM backend selects, which device to enable USB offloading
+ * on.
+ *
+ */
+int snd_soc_usb_connect(struct device *usbdev, struct snd_soc_usb_device *sdev)
+{
+ struct snd_soc_usb *ctx;
+
+ if (!usbdev)
+ return -ENODEV;
+
+ mutex_lock(&ctx_mutex);
+ ctx = snd_soc_find_usb_ctx(usbdev);
+ if (!ctx)
+ goto exit;
+
+ if (ctx->connection_status_cb)
+ ctx->connection_status_cb(ctx, sdev, true);
+
+exit:
+ mutex_unlock(&ctx_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_connect);
+
+/**
+ * snd_soc_usb_disconnect() - Notification of USB device disconnection
+ * @usbdev: USB bus device
+ * @sdev: USB SND device to remove
+ *
+ * Notify of a new USB SND device disconnection to the USB backend.
+ *
+ */
+int snd_soc_usb_disconnect(struct device *usbdev, struct snd_soc_usb_device *sdev)
+{
+ struct snd_soc_usb *ctx;
+
+ if (!usbdev)
+ return -ENODEV;
+
+ mutex_lock(&ctx_mutex);
+ ctx = snd_soc_find_usb_ctx(usbdev);
+ if (!ctx)
+ goto exit;
+
+ if (ctx->connection_status_cb)
+ ctx->connection_status_cb(ctx, sdev, false);
+
+exit:
+ mutex_unlock(&ctx_mutex);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_usb_disconnect);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SoC USB driver for offloading");
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 5e3e4f14c392..c8adfff826bd 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -262,6 +262,19 @@ struct snd_soc_dai_link_component snd_soc_dummy_dlc = {
};
EXPORT_SYMBOL_GPL(snd_soc_dummy_dlc);
+int snd_soc_dlc_is_dummy(struct snd_soc_dai_link_component *dlc)
+{
+ if (dlc == &snd_soc_dummy_dlc)
+ return true;
+
+ if ((dlc->name && strcmp(dlc->name, snd_soc_dummy_dlc.name) == 0) ||
+ (dlc->dai_name && strcmp(dlc->dai_name, snd_soc_dummy_dlc.dai_name) == 0))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dlc_is_dummy);
+
static int snd_soc_dummy_probe(struct faux_device *fdev)
{
int ret;
diff --git a/sound/soc/sof/Kconfig b/sound/soc/sof/Kconfig
index 32ffd345e07f..a487ab0b51c7 100644
--- a/sound/soc/sof/Kconfig
+++ b/sound/soc/sof/Kconfig
@@ -1,6 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
menuconfig SND_SOC_SOF_TOPLEVEL
- bool "Sound Open Firmware Support"
+ bool "Sound Open Firmware (SOF) platforms"
help
This adds support for Sound Open Firmware (SOF). SOF is free and
generic open source audio DSP firmware for multiple devices.
diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig
index 3ea82fa72e35..05faf1c6d6fc 100644
--- a/sound/soc/sof/amd/Kconfig
+++ b/sound/soc/sof/amd/Kconfig
@@ -94,13 +94,14 @@ config SND_SOC_SOF_AMD_ACP63
If unsure select "N".
config SND_SOC_SOF_AMD_ACP70
- tristate "SOF support for ACP7.0 platform"
+ tristate "SOF support for ACP7.0/ACP7.1 platforms"
depends on SND_SOC_SOF_PCI
depends on AMD_NODE
select SND_SOC_SOF_AMD_COMMON
+ select SND_SOC_SOF_AMD_SOUNDWIRE_LINK_BASELINE
help
Select this option for SOF support on
- AMD ACP7.0 version based platforms.
- Say Y if you want to enable SOF on ACP7.0 based platform.
+ AMD ACP7.0/ACP7.1 version based platforms.
+ Say Y if you want to enable SOF on ACP7.0/ACP7.1 based platforms.
endif
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
index ecdcae07ace7..08583a91afbc 100644
--- a/sound/soc/sof/amd/acp-dsp-offset.h
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -130,4 +130,14 @@
#define ACP_SW0_EN 0x3000
#define ACP_SW1_EN 0x3C00
+#define ACP70_PME_EN 0x1400
+#define ACP70_EXTERNAL_INTR_CNTL1 0x1A08
+#define ACP70_SW0_WAKE_EN 0x1458
+#define ACP70_SW1_WAKE_EN 0x1460
+#define ACP70_SDW_HOST_WAKE_MASK 0x0C00000
+#define ACP70_SDW0_HOST_WAKE_STAT BIT(24)
+#define ACP70_SDW1_HOST_WAKE_STAT BIT(25)
+#define ACP70_SDW0_PME_STAT BIT(26)
+#define ACP70_SDW1_PME_STAT BIT(27)
+
#endif
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index 7e6f10726ff0..7132916aa253 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -58,6 +58,7 @@ static void init_dma_descriptor(struct acp_dev_data *adata)
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
acp_dma_desc_base_addr = ACP70_DMA_DESC_BASE_ADDR;
acp_dma_desc_max_num_dscr = ACP70_DMA_DESC_MAX_NUM_DSCR;
break;
@@ -97,6 +98,7 @@ static int config_dma_channel(struct acp_dev_data *adata, unsigned int ch,
switch (acp_data->pci_rev) {
case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
acp_dma_cntl_0 = ACP70_DMA_CNTL_0;
acp_dma_ch_rst_sts = ACP70_DMA_CH_RST_STS;
acp_dma_dscr_err_sts_0 = ACP70_DMA_ERR_STS_0;
@@ -336,6 +338,7 @@ int acp_dma_status(struct acp_dev_data *adata, unsigned char ch)
switch (adata->pci_rev) {
case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
acp_dma_ch_sts = ACP70_DMA_CH_STS;
break;
default:
@@ -383,6 +386,69 @@ static int acp_memory_init(struct snd_sof_dev *sdev)
return 0;
}
+static void amd_sof_handle_acp70_sdw_wake_event(struct acp_dev_data *adata)
+{
+ struct amd_sdw_manager *amd_manager;
+
+ if (adata->acp70_sdw0_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[0]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw0_wake_event = 0;
+ }
+
+ if (adata->acp70_sdw1_wake_event) {
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[1]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp70_sdw1_wake_event = 0;
+ }
+}
+
+static int amd_sof_check_and_handle_acp70_sdw_wake_irq(struct snd_sof_dev *sdev)
+{
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+ u32 ext_intr_stat1;
+ int irq_flag = 0;
+ bool sdw_wake_irq = false;
+
+ ext_intr_stat1 = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);
+ if (ext_intr_stat1 & ACP70_SDW0_HOST_WAKE_STAT) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
+ ACP70_SDW0_HOST_WAKE_STAT);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_HOST_WAKE_STAT) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
+ ACP70_SDW1_HOST_WAKE_STAT);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW0_PME_STAT) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_SW0_WAKE_EN, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, ACP70_SDW0_PME_STAT);
+ adata->acp70_sdw0_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (ext_intr_stat1 & ACP70_SDW1_PME_STAT) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_SW1_WAKE_EN, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1, ACP70_SDW1_PME_STAT);
+ adata->acp70_sdw1_wake_event = true;
+ sdw_wake_irq = true;
+ }
+
+ if (sdw_wake_irq) {
+ amd_sof_handle_acp70_sdw_wake_event(adata);
+ irq_flag = 1;
+ }
+ return irq_flag;
+}
+
static irqreturn_t acp_irq_thread(int irq, void *context)
{
struct snd_sof_dev *sdev = context;
@@ -415,7 +481,7 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id)
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
unsigned int base = desc->dsp_intr_base;
unsigned int val;
- int irq_flag = 0;
+ int irq_flag = 0, wake_irq_flag = 0;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
if (val & ACP_DSP_TO_HOST_IRQ) {
@@ -453,8 +519,14 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id)
schedule_work(&amd_manager->amd_sdw_irq_thread);
irq_flag = 1;
}
+ switch (adata->pci_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ wake_irq_flag = amd_sof_check_and_handle_acp70_sdw_wake_irq(sdev);
+ break;
+ }
}
- if (irq_flag)
+ if (irq_flag || wake_irq_flag)
return IRQ_HANDLED;
else
return IRQ_NONE;
@@ -486,6 +558,7 @@ static int acp_power_on(struct snd_sof_dev *sdev)
acp_pgfsm_cntl_mask = ACP6X_PGFSM_CNTL_POWER_ON_MASK;
break;
case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
acp_pgfsm_status_mask = ACP70_PGFSM_STATUS_MASK;
acp_pgfsm_cntl_mask = ACP70_PGFSM_CNTL_POWER_ON_MASK;
break;
@@ -507,7 +580,6 @@ static int acp_power_on(struct snd_sof_dev *sdev)
static int acp_reset(struct snd_sof_dev *sdev)
{
- const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int val;
int ret;
@@ -528,14 +600,6 @@ static int acp_reset(struct snd_sof_dev *sdev)
if (ret < 0)
dev_err(sdev->dev, "timeout in releasing reset\n");
- if (desc->acp_clkmux_sel)
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK);
-
- if (desc->ext_intr_enb)
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01);
-
- if (desc->ext_intr_cntl)
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_cntl, ACP_ERROR_IRQ_MASK);
return ret;
}
@@ -566,9 +630,13 @@ static int acp_dsp_reset(struct snd_sof_dev *sdev)
static int acp_init(struct snd_sof_dev *sdev)
{
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+ struct acp_dev_data *acp_data;
+ unsigned int sdw0_wake_en, sdw1_wake_en;
int ret;
/* power on */
+ acp_data = sdev->pdata->hw_pdata;
ret = acp_power_on(sdev);
if (ret) {
dev_err(sdev->dev, "ACP power on failed\n");
@@ -577,7 +645,32 @@ static int acp_init(struct snd_sof_dev *sdev)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, 0x01);
/* Reset */
- return acp_reset(sdev);
+ ret = acp_reset(sdev);
+ if (ret)
+ return ret;
+
+ if (desc->acp_clkmux_sel)
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_clkmux_sel, ACP_CLOCK_ACLK);
+
+ if (desc->ext_intr_enb)
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_enb, 0x01);
+
+ if (desc->ext_intr_cntl)
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_cntl, ACP_ERROR_IRQ_MASK);
+
+ switch (acp_data->pci_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ sdw0_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP70_SW0_WAKE_EN);
+ sdw1_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP70_SW1_WAKE_EN);
+ if (sdw0_wake_en || sdw1_wake_en)
+ snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR, ACP70_EXTERNAL_INTR_CNTL1,
+ ACP70_SDW_HOST_WAKE_MASK, ACP70_SDW_HOST_WAKE_MASK);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1);
+ break;
+ }
+ return 0;
}
static bool check_acp_sdw_enable_status(struct snd_sof_dev *sdev)
@@ -616,8 +709,12 @@ int amd_sof_acp_suspend(struct snd_sof_dev *sdev, u32 target_state)
dev_err(sdev->dev, "ACP Reset failed\n");
return ret;
}
- if (acp_data->pci_rev == ACP70_PCI_ID)
+ switch (acp_data->pci_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
enable = true;
+ break;
+ }
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable);
return 0;
@@ -637,9 +734,15 @@ int amd_sof_acp_resume(struct snd_sof_dev *sdev)
return ret;
}
return acp_memory_init(sdev);
- } else {
- return acp_dsp_reset(sdev);
}
+ switch (acp_data->pci_rev) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP70_PME_EN, 1);
+ break;
+ }
+
+ return acp_dsp_reset(sdev);
}
EXPORT_SYMBOL_NS(amd_sof_acp_resume, "SND_SOC_SOF_AMD_COMMON");
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index d084db34eed8..d3c5b2386cdf 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -74,6 +74,7 @@
#define ACP_RMB_PCI_ID 0x6F
#define ACP63_PCI_ID 0x63
#define ACP70_PCI_ID 0x70
+#define ACP71_PCI_ID 0x71
#define HOST_BRIDGE_CZN 0x1630
#define HOST_BRIDGE_VGH 0x1645
@@ -109,9 +110,11 @@
#define ACP_SDW0_IRQ_MASK BIT(21)
#define ACP_SDW1_IRQ_MASK BIT(2)
#define SDW_ACPI_ADDR_ACP63 5
+#define SDW_ACPI_ADDR_ACP70 SDW_ACPI_ADDR_ACP63
#define ACP_DEFAULT_SRAM_LENGTH 0x00080000
#define ACP_SRAM_PAGE_COUNT 128
#define ACP6X_SDW_MAX_MANAGER_COUNT 2
+#define ACP70_SDW_MAX_MANAGER_COUNT ACP6X_SDW_MAX_MANAGER_COUNT
enum clock_source {
ACP_CLOCK_96M = 0,
@@ -260,6 +263,10 @@ struct acp_dev_data {
bool is_dram_in_use;
bool is_sram_in_use;
bool sdw_en_stat;
+ /* acp70_sdw0_wake_event flag set to true when wake irq asserted for SW0 instance */
+ bool acp70_sdw0_wake_event;
+ /* acp70_sdw1_wake_event flag set to true when wake irq asserted for SW1 instance */
+ bool acp70_sdw1_wake_event;
unsigned int pci_rev;
};
diff --git a/sound/soc/sof/amd/pci-acp70.c b/sound/soc/sof/amd/pci-acp70.c
index 8fa1170a2161..51d36d43c42b 100644
--- a/sound/soc/sof/amd/pci-acp70.c
+++ b/sound/soc/sof/amd/pci-acp70.c
@@ -33,18 +33,22 @@ static const struct sof_amd_acp_desc acp70_chip_info = {
.ext_intr_cntl = ACP70_EXTERNAL_INTR_CNTL,
.ext_intr_stat = ACP70_EXT_INTR_STAT,
.ext_intr_stat1 = ACP70_EXT_INTR_STAT1,
+ .acp_error_stat = ACP70_ERROR_STATUS,
.dsp_intr_base = ACP70_DSP_SW_INTR_BASE,
.acp_sw0_i2s_err_reason = ACP7X_SW0_I2S_ERROR_REASON,
.sram_pte_offset = ACP70_SRAM_PTE_OFFSET,
.hw_semaphore_offset = ACP70_AXI2DAGB_SEM_0,
.fusion_dsp_offset = ACP70_DSP_FUSION_RUNSTALL,
.probe_reg_offset = ACP70_FUTURE_REG_ACLK_0,
+ .sdw_max_link_count = ACP70_SDW_MAX_MANAGER_COUNT,
+ .sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP70,
.reg_start_addr = ACP70_REG_START,
.reg_end_addr = ACP70_REG_END,
};
static const struct sof_dev_desc acp70_desc = {
.machines = snd_soc_acpi_amd_acp70_sof_machines,
+ .alt_machines = snd_soc_acpi_amd_acp70_sof_sdw_machines,
.resindex_lpe_base = 0,
.resindex_pcicfg_base = -1,
.resindex_imr_base = -1,
@@ -70,8 +74,13 @@ static int acp70_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_
{
unsigned int flag;
- if (pci->revision != ACP70_PCI_ID)
+ switch (pci->revision) {
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ break;
+ default:
return -ENODEV;
+ }
flag = snd_amd_acp_find_config(pci);
if (flag != FLAG_AMD_SOF && flag != FLAG_AMD_SOF_ONLY_DMIC)
diff --git a/sound/soc/sof/control.c b/sound/soc/sof/control.c
index 463d418e7200..a3fd1d523c09 100644
--- a/sound/soc/sof/control.c
+++ b/sound/soc/sof/control.c
@@ -196,7 +196,6 @@ int snd_sof_bytes_ext_volatile_get(struct snd_kcontrol *kcontrol, unsigned int _
if (tplg_ops && tplg_ops->control && tplg_ops->control->bytes_ext_volatile_get)
ret = tplg_ops->control->bytes_ext_volatile_get(scontrol, binary_data, size);
- pm_runtime_mark_last_busy(scomp->dev);
err = pm_runtime_put_autosuspend(scomp->dev);
if (err < 0)
dev_err_ratelimited(scomp->dev, "%s: failed to idle %d\n", __func__, err);
diff --git a/sound/soc/sof/core.c b/sound/soc/sof/core.c
index aed834d03e10..b11f408f1366 100644
--- a/sound/soc/sof/core.c
+++ b/sound/soc/sof/core.c
@@ -607,7 +607,8 @@ static void sof_probe_work(struct work_struct *work)
}
static void
-sof_apply_profile_override(struct sof_loadable_file_profile *path_override)
+sof_apply_profile_override(struct sof_loadable_file_profile *path_override,
+ struct snd_sof_pdata *plat_data)
{
if (override_ipc_type >= 0 && override_ipc_type < SOF_IPC_TYPE_COUNT)
path_override->ipc_type = override_ipc_type;
@@ -619,8 +620,11 @@ sof_apply_profile_override(struct sof_loadable_file_profile *path_override)
path_override->fw_lib_path = override_lib_path;
if (override_tplg_path)
path_override->tplg_path = override_tplg_path;
- if (override_tplg_filename)
+ if (override_tplg_filename) {
path_override->tplg_name = override_tplg_filename;
+ /* User requested a specific topology file and expect it to be loaded */
+ plat_data->disable_function_topology = true;
+ }
}
int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
@@ -654,7 +658,7 @@ int snd_sof_device_probe(struct device *dev, struct snd_sof_pdata *plat_data)
}
}
- sof_apply_profile_override(&plat_data->ipc_file_profile_base);
+ sof_apply_profile_override(&plat_data->ipc_file_profile_base, plat_data);
/* Initialize sof_ops based on the initial selected IPC version */
ret = sof_init_sof_ops(sdev);
diff --git a/sound/soc/sof/debug.c b/sound/soc/sof/debug.c
index d0ffa1d71145..b24943a65c89 100644
--- a/sound/soc/sof/debug.c
+++ b/sound/soc/sof/debug.c
@@ -217,7 +217,6 @@ static int memory_info_update(struct snd_sof_dev *sdev, char *buf, size_t buff_s
}
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) {
ret = min(ret, reply->rhdr.error);
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index 62bf707aa909..f00b381cec3b 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -282,11 +282,8 @@ static int imx_region_name_to_blk_type(const char *region_name)
static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev)
{
const struct imx_chip_info *chip_info;
- struct reserved_mem *reserved;
struct platform_device *pdev;
- struct device_node *res_np;
- phys_addr_t base, size;
- struct resource *res;
+ struct resource *res, _res;
int i, blk_type, ret;
pdev = to_platform_device(sdev->dev);
@@ -307,37 +304,18 @@ static int imx_parse_ioremap_memory(struct snd_sof_dev *sdev)
"failed to fetch %s resource\n",
chip_info->memory[i].name);
- base = res->start;
- size = resource_size(res);
} else {
- ret = of_property_match_string(pdev->dev.of_node,
- "memory-region-names",
- chip_info->memory[i].name);
+ ret = of_reserved_mem_region_to_resource_byname(pdev->dev.of_node,
+ chip_info->memory[i].name,
+ &_res);
if (ret < 0)
return dev_err_probe(sdev->dev, ret,
- "no valid index for %s\n",
+ "no valid entry for %s\n",
chip_info->memory[i].name);
-
- res_np = of_parse_phandle(pdev->dev.of_node,
- "memory-region",
- ret);
- if (!res_np)
- return dev_err_probe(sdev->dev, -ENODEV,
- "failed to parse phandle %s\n",
- chip_info->memory[i].name);
-
- reserved = of_reserved_mem_lookup(res_np);
- of_node_put(res_np);
- if (!reserved)
- return dev_err_probe(sdev->dev, -ENODEV,
- "failed to get %s reserved\n",
- chip_info->memory[i].name);
-
- base = reserved->base;
- size = reserved->size;
+ res = &_res;
}
- sdev->bar[blk_type] = devm_ioremap(sdev->dev, base, size);
+ sdev->bar[blk_type] = devm_ioremap_resource(sdev->dev, res);
if (!sdev->bar[blk_type])
return dev_err_probe(sdev->dev,
-ENOMEM,
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index ab07512e511d..b73dd91bd529 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -11,6 +11,7 @@
#include <linux/arm-smccc.h>
#include <linux/firmware/imx/svc/misc.h>
#include <linux/mfd/syscon.h>
+#include <linux/reset.h>
#include "imx-common.h"
@@ -23,13 +24,6 @@
#define IMX8M_DAP_PWRCTL (0x4000 + 0x3020)
#define IMX8M_PWRCTL_CORERESET BIT(16)
-#define AudioDSP_REG0 0x100
-#define AudioDSP_REG1 0x104
-#define AudioDSP_REG2 0x108
-#define AudioDSP_REG3 0x10c
-
-#define AudioDSP_REG2_RUNSTALL BIT(5)
-
/* imx8ulp macros */
#define FSL_SIP_HIFI_XRDC 0xc200000e
#define SYSCTRL0 0x8
@@ -43,8 +37,22 @@
struct imx8m_chip_data {
void __iomem *dap;
struct regmap *regmap;
+ struct reset_control *run_stall;
};
+static int imx8_shutdown(struct snd_sof_dev *sdev)
+{
+ /*
+ * Force the DSP to stall. After the firmware image is loaded,
+ * the stall will be removed during run() by a matching
+ * imx_sc_pm_cpu_start() call.
+ */
+ imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, false,
+ RESET_VECTOR_VADDR);
+
+ return 0;
+}
+
/*
* DSP control.
*/
@@ -137,8 +145,7 @@ static int imx8m_reset(struct snd_sof_dev *sdev)
/* keep reset asserted for 10 cycles */
usleep_range(1, 2);
- regmap_update_bits(chip->regmap, AudioDSP_REG2,
- AudioDSP_REG2_RUNSTALL, AudioDSP_REG2_RUNSTALL);
+ reset_control_assert(chip->run_stall);
/* take the DSP out of reset and keep stalled for FW loading */
pwrctl = readl(chip->dap + IMX8M_DAP_PWRCTL);
@@ -152,9 +159,7 @@ static int imx8m_run(struct snd_sof_dev *sdev)
{
struct imx8m_chip_data *chip = get_chip_pdata(sdev);
- regmap_update_bits(chip->regmap, AudioDSP_REG2, AudioDSP_REG2_RUNSTALL, 0);
-
- return 0;
+ return reset_control_deassert(chip->run_stall);
}
static int imx8m_probe(struct snd_sof_dev *sdev)
@@ -174,10 +179,10 @@ static int imx8m_probe(struct snd_sof_dev *sdev)
return dev_err_probe(sdev->dev, -ENODEV,
"failed to ioremap DAP\n");
- chip->regmap = syscon_regmap_lookup_by_phandle(sdev->dev->of_node, "fsl,dsp-ctrl");
- if (IS_ERR(chip->regmap))
- return dev_err_probe(sdev->dev, PTR_ERR(chip->regmap),
- "failed to fetch dsp ctrl regmap\n");
+ chip->run_stall = devm_reset_control_get_exclusive(sdev->dev, "runstall");
+ if (IS_ERR(chip->run_stall))
+ return dev_err_probe(sdev->dev, PTR_ERR(chip->run_stall),
+ "failed to get dsp runstall reset control\n");
common->chip_pdata = chip;
@@ -289,11 +294,13 @@ static int imx8_ops_init(struct snd_sof_dev *sdev)
static const struct imx_chip_ops imx8_chip_ops = {
.probe = imx8_probe,
.core_kick = imx8_run,
+ .core_shutdown = imx8_shutdown,
};
static const struct imx_chip_ops imx8x_chip_ops = {
.probe = imx8_probe,
.core_kick = imx8x_run,
+ .core_shutdown = imx8_shutdown,
};
static const struct imx_chip_ops imx8m_chip_ops = {
diff --git a/sound/soc/sof/intel/hda-loader.c b/sound/soc/sof/intel/hda-loader.c
index 49085ca7b46b..2cc11d8b0f70 100644
--- a/sound/soc/sof/intel/hda-loader.c
+++ b/sound/soc/sof/intel/hda-loader.c
@@ -579,8 +579,11 @@ int hda_dsp_ipc4_load_library(struct snd_sof_dev *sdev,
struct sof_ipc4_msg msg = {};
int ret, ret1;
- /* if IMR booting is enabled and fw context is saved for D3 state, skip the loading */
- if (reload && hda->booted_from_imr && ipc4_data->fw_context_save)
+ /*
+ * if IMR booting is enabled and libraries have been restored during fw
+ * boot, skip the loading
+ */
+ if (reload && hda->booted_from_imr && ipc4_data->libraries_restored)
return 0;
/* the fw_lib has been verified during loading, we can trust the validity here */
diff --git a/sound/soc/sof/intel/hda.c b/sound/soc/sof/intel/hda.c
index 6a3932d90b43..c387efec41e9 100644
--- a/sound/soc/sof/intel/hda.c
+++ b/sound/soc/sof/intel/hda.c
@@ -192,6 +192,9 @@ static int hda_sdw_probe(struct snd_sof_dev *sdev)
res.ext = true;
res.ops = &sdw_ace2x_callback;
+ /* ACE3+ supports microphone privacy */
+ if (chip->hw_ip_version >= SOF_INTEL_ACE_3_0)
+ res.mic_privacy = true;
}
res.irq = sdev->ipc_irq;
res.handle = hdev->info.handle;
@@ -626,6 +629,11 @@ static int hda_init_caps(struct snd_sof_dev *sdev)
if (!(interface_mask & BIT(SOF_DAI_INTEL_ALH)))
goto skip_soundwire;
+ /* Skip SoundWire in nocodec mode */
+ if (IS_ENABLED(CONFIG_SND_SOC_SOF_NOCODEC_DEBUG_SUPPORT) &&
+ sof_debug_check_flag(SOF_DBG_FORCE_NOCODEC))
+ goto skip_soundwire;
+
/* scan SoundWire capabilities exposed by DSDT */
ret = hda_sdw_acpi_scan(sdev);
if (ret < 0) {
@@ -1254,11 +1262,11 @@ static int check_tplg_quirk_mask(struct snd_soc_acpi_mach *mach)
return 0;
}
-static char *remove_file_ext(const char *tplg_filename)
+static char *remove_file_ext(struct device *dev, const char *tplg_filename)
{
char *filename, *tmp;
- filename = kstrdup(tplg_filename, GFP_KERNEL);
+ filename = devm_kstrdup(dev, tplg_filename, GFP_KERNEL);
if (!filename)
return NULL;
@@ -1342,7 +1350,7 @@ struct snd_soc_acpi_mach *hda_machine_select(struct snd_sof_dev *sdev)
*/
if (!sof_pdata->tplg_filename) {
/* remove file extension if it exists */
- tplg_filename = remove_file_ext(mach->sof_tplg_filename);
+ tplg_filename = remove_file_ext(sdev->dev, mach->sof_tplg_filename);
if (!tplg_filename)
return NULL;
diff --git a/sound/soc/sof/intel/hda.h b/sound/soc/sof/intel/hda.h
index 108cad04879e..e14f82c0831f 100644
--- a/sound/soc/sof/intel/hda.h
+++ b/sound/soc/sof/intel/hda.h
@@ -935,6 +935,7 @@ extern const struct sof_intel_dsp_desc mtl_chip_info;
extern const struct sof_intel_dsp_desc arl_s_chip_info;
extern const struct sof_intel_dsp_desc lnl_chip_info;
extern const struct sof_intel_dsp_desc ptl_chip_info;
+extern const struct sof_intel_dsp_desc wcl_chip_info;
/* Probes support */
#if IS_ENABLED(CONFIG_SND_SOC_SOF_HDA_PROBES)
diff --git a/sound/soc/sof/intel/pci-ptl.c b/sound/soc/sof/intel/pci-ptl.c
index 7d4c46f56931..68f6a9841633 100644
--- a/sound/soc/sof/intel/pci-ptl.c
+++ b/sound/soc/sof/intel/pci-ptl.c
@@ -55,10 +55,40 @@ static const struct sof_dev_desc ptl_desc = {
.ops_init = sof_ptl_ops_init,
};
+static const struct sof_dev_desc wcl_desc = {
+ .use_acpi_target_states = true,
+ .machines = snd_soc_acpi_intel_ptl_machines,
+ .alt_machines = snd_soc_acpi_intel_ptl_sdw_machines,
+ .resindex_lpe_base = 0,
+ .resindex_pcicfg_base = -1,
+ .resindex_imr_base = -1,
+ .irqindex_host_ipc = -1,
+ .chip_info = &wcl_chip_info,
+ .ipc_supported_mask = BIT(SOF_IPC_TYPE_4),
+ .ipc_default = SOF_IPC_TYPE_4,
+ .dspless_mode_supported = true,
+ .default_fw_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4/wcl",
+ },
+ .default_lib_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-lib/wcl",
+ },
+ .default_tplg_path = {
+ [SOF_IPC_TYPE_4] = "intel/sof-ipc4-tplg",
+ },
+ .default_fw_filename = {
+ [SOF_IPC_TYPE_4] = "sof-wcl.ri",
+ },
+ .nocodec_tplg_filename = "sof-ptl-nocodec.tplg",
+ .ops = &sof_ptl_ops,
+ .ops_init = sof_ptl_ops_init,
+};
+
/* PCI IDs */
static const struct pci_device_id sof_pci_ids[] = {
{ PCI_DEVICE_DATA(INTEL, HDA_PTL, &ptl_desc) }, /* PTL */
{ PCI_DEVICE_DATA(INTEL, HDA_PTL_H, &ptl_desc) }, /* PTL-H */
+ { PCI_DEVICE_DATA(INTEL, HDA_WCL, &wcl_desc) }, /* WCL */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, sof_pci_ids);
diff --git a/sound/soc/sof/intel/ptl.c b/sound/soc/sof/intel/ptl.c
index aa0b772178bc..1bc1f54c470d 100644
--- a/sound/soc/sof/intel/ptl.c
+++ b/sound/soc/sof/intel/ptl.c
@@ -117,6 +117,7 @@ const struct sof_intel_dsp_desc ptl_chip_info = {
.read_sdw_lcount = hda_sdw_check_lcount_ext,
.check_sdw_irq = lnl_dsp_check_sdw_irq,
.check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .sdw_process_wakeen = hda_sdw_process_wakeen_common,
.check_ipc_irq = mtl_dsp_check_ipc_irq,
.check_mic_privacy_irq = sof_ptl_check_mic_privacy_irq,
.process_mic_privacy = sof_ptl_process_mic_privacy,
@@ -126,6 +127,29 @@ const struct sof_intel_dsp_desc ptl_chip_info = {
.hw_ip_version = SOF_INTEL_ACE_3_0,
};
+const struct sof_intel_dsp_desc wcl_chip_info = {
+ .cores_num = 3,
+ .init_core_mask = BIT(0),
+ .host_managed_cores_mask = BIT(0),
+ .ipc_req = MTL_DSP_REG_HFIPCXIDR,
+ .ipc_req_mask = MTL_DSP_REG_HFIPCXIDR_BUSY,
+ .ipc_ack = MTL_DSP_REG_HFIPCXIDA,
+ .ipc_ack_mask = MTL_DSP_REG_HFIPCXIDA_DONE,
+ .ipc_ctl = MTL_DSP_REG_HFIPCXCTL,
+ .rom_status_reg = LNL_DSP_REG_HFDSC,
+ .rom_init_timeout = 300,
+ .ssp_count = MTL_SSP_COUNT,
+ .d0i3_offset = MTL_HDA_VS_D0I3C,
+ .read_sdw_lcount = hda_sdw_check_lcount_ext,
+ .check_sdw_irq = lnl_dsp_check_sdw_irq,
+ .check_sdw_wakeen_irq = lnl_sdw_check_wakeen_irq,
+ .check_ipc_irq = mtl_dsp_check_ipc_irq,
+ .cl_init = mtl_dsp_cl_init,
+ .power_down_dsp = mtl_power_down_dsp,
+ .disable_interrupts = lnl_dsp_disable_interrupts,
+ .hw_ip_version = SOF_INTEL_ACE_3_0,
+};
+
MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_MTL");
MODULE_IMPORT_NS("SND_SOC_SOF_INTEL_LNL");
MODULE_IMPORT_NS("SND_SOC_SOF_HDA_MLINK");
diff --git a/sound/soc/sof/ipc3-dtrace.c b/sound/soc/sof/ipc3-dtrace.c
index 744a91a150bc..e5c8fec173c4 100644
--- a/sound/soc/sof/ipc3-dtrace.c
+++ b/sound/soc/sof/ipc3-dtrace.c
@@ -172,7 +172,6 @@ static int ipc3_trace_update_filter(struct snd_sof_dev *sdev, int num_elems,
goto error;
}
ret = sof_ipc_tx_message_no_reply(sdev->ipc, msg, msg->hdr.size);
- pm_runtime_mark_last_busy(sdev->dev);
pm_runtime_put_autosuspend(sdev->dev);
error:
diff --git a/sound/soc/sof/ipc4-loader.c b/sound/soc/sof/ipc4-loader.c
index d2f534d65edf..b0d293f62d1c 100644
--- a/sound/soc/sof/ipc4-loader.c
+++ b/sound/soc/sof/ipc4-loader.c
@@ -236,7 +236,6 @@ static int sof_ipc4_load_library(struct snd_sof_dev *sdev, unsigned long lib_id,
ret = ipc4_data->load_library(sdev, fw_lib, false);
- pm_runtime_mark_last_busy(sdev->dev);
err = pm_runtime_put_autosuspend(sdev->dev);
if (err < 0)
dev_err_ratelimited(sdev->dev, "%s: pm_runtime idle failed: %d\n",
@@ -494,6 +493,12 @@ int sof_ipc4_query_fw_configuration(struct snd_sof_dev *sdev)
break;
case SOF_IPC4_FW_CONTEXT_SAVE:
ipc4_data->fw_context_save = *tuple->value;
+ /*
+ * Set the default libraries_restored value - if full
+ * context save is supported then it means that
+ * libraries are restored
+ */
+ ipc4_data->libraries_restored = ipc4_data->fw_context_save;
break;
default:
break;
diff --git a/sound/soc/sof/ipc4-pcm.c b/sound/soc/sof/ipc4-pcm.c
index c09b424ab863..374dc10d10fd 100644
--- a/sound/soc/sof/ipc4-pcm.c
+++ b/sound/soc/sof/ipc4-pcm.c
@@ -56,17 +56,41 @@ sof_ipc4_sps_to_time_info(struct snd_sof_pcm_stream *sps)
return stream_priv->time_info;
}
+static
+char *sof_ipc4_set_multi_pipeline_state_debug(struct snd_sof_dev *sdev, char *buf, size_t size,
+ struct ipc4_pipeline_set_state_data *trigger_list)
+{
+ int i, offset = 0;
+
+ for (i = 0; i < trigger_list->count; i++) {
+ offset += snprintf(buf + offset, size - offset, " %d",
+ trigger_list->pipeline_instance_ids[i]);
+
+ if (offset >= size - 1) {
+ buf[size - 1] = '\0';
+ break;
+ }
+ }
+ return buf;
+}
+
static int sof_ipc4_set_multi_pipeline_state(struct snd_sof_dev *sdev, u32 state,
struct ipc4_pipeline_set_state_data *trigger_list)
{
struct sof_ipc4_msg msg = {{ 0 }};
u32 primary, ipc_size;
+ char debug_buf[32];
/* trigger a single pipeline */
if (trigger_list->count == 1)
return sof_ipc4_set_pipeline_state(sdev, trigger_list->pipeline_instance_ids[0],
state);
+ dev_dbg(sdev->dev, "Set pipelines %s to state %d%s",
+ sof_ipc4_set_multi_pipeline_state_debug(sdev, debug_buf, sizeof(debug_buf),
+ trigger_list),
+ state, sof_ipc4_pipeline_state_str(state));
+
primary = state;
primary |= SOF_IPC4_MSG_TYPE_SET(SOF_IPC4_GLB_SET_PIPELINE_STATE);
primary |= SOF_IPC4_MSG_DIR(SOF_IPC4_MSG_REQUEST);
@@ -89,7 +113,8 @@ int sof_ipc4_set_pipeline_state(struct snd_sof_dev *sdev, u32 instance_id, u32 s
struct sof_ipc4_msg msg = {{ 0 }};
u32 primary;
- dev_dbg(sdev->dev, "ipc4 set pipeline instance %d state %d", instance_id, state);
+ dev_dbg(sdev->dev, "Set pipeline %d to state %d%s", instance_id, state,
+ sof_ipc4_pipeline_state_str(state));
primary = state;
primary |= SOF_IPC4_GLB_PIPE_STATE_ID(instance_id);
@@ -409,9 +434,33 @@ static int sof_ipc4_trigger_pipelines(struct snd_soc_component *component,
* If use_chain_dma attribute is set we proceed to chained DMA
* trigger function that handles the rest for the substream.
*/
- if (pipeline->use_chain_dma)
- return sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream,
- pipeline_list, state, cmd);
+ if (pipeline->use_chain_dma) {
+ struct sof_ipc4_timestamp_info *time_info;
+
+ time_info = sof_ipc4_sps_to_time_info(&spcm->stream[substream->stream]);
+
+ ret = sof_ipc4_chain_dma_trigger(sdev, spcm, substream->stream,
+ pipeline_list, state, cmd);
+ if (ret || !time_info)
+ return ret;
+
+ if (state == SOF_IPC4_PIPE_PAUSED) {
+ /*
+ * Record the DAI position for delay reporting
+ * To handle multiple pause/resume/xrun we need to add
+ * the positions to simulate how the firmware behaves
+ */
+ u64 pos = snd_sof_pcm_get_dai_frame_counter(sdev, component,
+ substream);
+
+ time_info->stream_end_offset += pos;
+ } else if (state == SOF_IPC4_PIPE_RESET) {
+ /* Reset the end offset as the stream is stopped */
+ time_info->stream_end_offset = 0;
+ }
+
+ return 0;
+ }
/* allocate memory for the pipeline data */
trigger_list = kzalloc(struct_size(trigger_list, pipeline_instance_ids,
@@ -557,12 +606,15 @@ static int sof_ipc4_pcm_hw_free(struct snd_soc_component *component,
return sof_ipc4_trigger_pipelines(component, substream, SOF_IPC4_PIPE_RESET, 0);
}
-static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const char *link_name,
- struct snd_pcm_hw_params *params)
+static int ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev,
+ const char *link_name,
+ struct snd_pcm_hw_params *params)
{
struct snd_sof_dai_link *slink;
struct snd_sof_dai *dai;
bool dai_link_found = false;
+ int current_config = -1;
+ bool partial_match;
int i;
list_for_each_entry(slink, &sdev->dai_link_list, list) {
@@ -573,19 +625,50 @@ static void ipc4_ssp_dai_config_pcm_params_match(struct snd_sof_dev *sdev, const
}
if (!dai_link_found)
- return;
+ return 0;
+ /*
+ * Find the first best matching hardware config:
+ * rate + format + channels are matching
+ * rate + channel are matching
+ *
+ * The copier cannot do rate and/or channel conversion.
+ */
for (i = 0; i < slink->num_hw_configs; i++) {
struct snd_soc_tplg_hw_config *hw_config = &slink->hw_configs[i];
- if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate)) {
- /* set current config for all DAI's with matching name */
- list_for_each_entry(dai, &sdev->dai_list, list)
- if (!strcmp(slink->link->name, dai->name))
- dai->current_config = le32_to_cpu(hw_config->id);
+ if (params_rate(params) == le32_to_cpu(hw_config->fsync_rate) &&
+ params_width(params) == le32_to_cpu(hw_config->tdm_slot_width) &&
+ params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) {
+ current_config = le32_to_cpu(hw_config->id);
+ partial_match = false;
+ /* best match found */
break;
+ } else if (current_config < 0 &&
+ params_rate(params) == le32_to_cpu(hw_config->fsync_rate) &&
+ params_channels(params) == le32_to_cpu(hw_config->tdm_slots)) {
+ current_config = le32_to_cpu(hw_config->id);
+ partial_match = true;
+ /* keep looking for better match */
}
}
+
+ if (current_config < 0) {
+ dev_err(sdev->dev,
+ "%s: No suitable hw_config found for %s (num_hw_configs: %d)\n",
+ __func__, slink->link->name, slink->num_hw_configs);
+ return -EINVAL;
+ }
+
+ dev_dbg(sdev->dev,
+ "hw_config for %s: %d (num_hw_configs: %d) with %s match\n",
+ slink->link->name, current_config, slink->num_hw_configs,
+ partial_match ? "partial" : "full");
+ list_for_each_entry(dai, &sdev->dai_list, list)
+ if (!strcmp(slink->link->name, dai->name))
+ dai->current_config = current_config;
+
+ return 0;
}
/*
@@ -728,13 +811,10 @@ static int sof_ipc4_pcm_dai_link_fixup(struct snd_soc_pcm_runtime *rtd,
break;
}
- switch (ipc4_copier->dai_type) {
- case SOF_DAI_INTEL_SSP:
- ipc4_ssp_dai_config_pcm_params_match(sdev, (char *)rtd->dai_link->name, params);
- break;
- default:
- break;
- }
+ if (ipc4_copier->dai_type == SOF_DAI_INTEL_SSP)
+ return ipc4_ssp_dai_config_pcm_params_match(sdev,
+ (char *)rtd->dai_link->name,
+ params);
return 0;
}
@@ -784,7 +864,8 @@ static int sof_ipc4_pcm_setup(struct snd_sof_dev *sdev, struct snd_sof_pcm *spcm
/* allocate memory for max number of pipeline IDs */
pipeline_list->pipelines = kcalloc(ipc4_data->max_num_pipelines,
- sizeof(struct snd_sof_widget *), GFP_KERNEL);
+ sizeof(*pipeline_list->pipelines),
+ GFP_KERNEL);
if (!pipeline_list->pipelines) {
sof_ipc4_pcm_free(sdev, spcm);
return -ENOMEM;
@@ -927,8 +1008,24 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
if (!host_copier || !dai_copier)
return -EINVAL;
- if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID)
+ if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_INVALID_NODE_ID) {
return -EINVAL;
+ } else if (host_copier->data.gtw_cfg.node_id == SOF_IPC4_CHAIN_DMA_NODE_ID) {
+ /*
+ * While the firmware does not supports time_info reporting for
+ * streams using ChainDMA, it is granted that ChainDMA can only
+ * be used on Host+Link pairs where the link position is
+ * accessible from the host side.
+ *
+ * Enable delay calculation in case of ChainDMA via host
+ * accessible registers.
+ *
+ * The ChainDMA uses 2x 1ms ping-pong buffer, dai side starts
+ * when 1ms data is available
+ */
+ time_info->stream_start_offset = substream->runtime->rate / MSEC_PER_SEC;
+ goto out;
+ }
node_index = SOF_IPC4_NODE_INDEX(host_copier->data.gtw_cfg.node_id);
offset = offsetof(struct sof_ipc4_fw_registers, pipeline_regs) + node_index * sizeof(ppl_reg);
@@ -946,6 +1043,7 @@ static int sof_ipc4_get_stream_start_offset(struct snd_sof_dev *sdev,
time_info->stream_end_offset = ppl_reg.stream_end_offset;
do_div(time_info->stream_end_offset, dai_sample_size);
+out:
/*
* Calculate the wrap boundary need to be used for delay calculation
* The host counter is in bytes, it will wrap earlier than the frames
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index 58b032820683..a8cdf9bc750b 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -72,6 +72,8 @@ struct sof_ipc4_fw_library {
* @max_num_pipelines: max number of pipelines
* @max_libs_count: Maximum number of libraries support by the FW including the
* base firmware
+ * @fw_context_save: Firmware supports full context save and restore
+ * @libraries_restored: The libraries have been retained during firmware boot
*
* @load_library: Callback function for platform dependent library loading
* @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion
@@ -87,6 +89,7 @@ struct sof_ipc4_fw_data {
int max_num_pipelines;
u32 max_libs_count;
bool fw_context_save;
+ bool libraries_restored;
int (*load_library)(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
@@ -123,4 +126,7 @@ size_t sof_ipc4_find_debug_slot_offset_by_type(struct snd_sof_dev *sdev,
void sof_ipc4_mic_privacy_state_change(struct snd_sof_dev *sdev, bool state);
+enum sof_ipc4_pipeline_state;
+const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state);
+
#endif
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 540ba140e155..591ee30551ba 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -145,6 +145,14 @@ static const struct sof_topology_token src_tokens[] = {
offsetof(struct sof_ipc4_src_data, sink_rate)},
};
+/* ASRC */
+static const struct sof_topology_token asrc_tokens[] = {
+ {SOF_TKN_ASRC_RATE_OUT, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_asrc_data, out_freq)},
+ {SOF_TKN_ASRC_OPERATION_MODE, SND_SOC_TPLG_TUPLE_TYPE_WORD, get_token_u32,
+ offsetof(struct sof_ipc4_asrc_data, asrc_mode)},
+};
+
static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
[SOF_DAI_TOKENS] = {"DAI tokens", dai_tokens, ARRAY_SIZE(dai_tokens)},
[SOF_PIPELINE_TOKENS] = {"Pipeline tokens", pipeline_tokens, ARRAY_SIZE(pipeline_tokens)},
@@ -166,6 +174,7 @@ static const struct sof_token_info ipc4_token_list[SOF_TOKEN_COUNT] = {
ipc4_audio_fmt_num_tokens, ARRAY_SIZE(ipc4_audio_fmt_num_tokens)},
[SOF_GAIN_TOKENS] = {"Gain tokens", gain_tokens, ARRAY_SIZE(gain_tokens)},
[SOF_SRC_TOKENS] = {"SRC tokens", src_tokens, ARRAY_SIZE(src_tokens)},
+ [SOF_ASRC_TOKENS] = {"ASRC tokens", asrc_tokens, ARRAY_SIZE(asrc_tokens)},
};
struct snd_sof_widget *sof_ipc4_find_swidget_by_ids(struct snd_sof_dev *sdev,
@@ -1045,6 +1054,50 @@ err:
return ret;
}
+static int sof_ipc4_widget_setup_comp_asrc(struct snd_sof_widget *swidget)
+{
+ struct snd_soc_component *scomp = swidget->scomp;
+ struct snd_sof_pipeline *spipe = swidget->spipe;
+ struct sof_ipc4_asrc *asrc;
+ int ret;
+
+ dev_dbg(scomp->dev, "Updating IPC structure for %s\n", swidget->widget->name);
+
+ asrc = kzalloc(sizeof(*asrc), GFP_KERNEL);
+ if (!asrc)
+ return -ENOMEM;
+
+ swidget->private = asrc;
+
+ ret = sof_ipc4_get_audio_fmt(scomp, swidget, &asrc->available_fmt,
+ &asrc->data.base_config);
+ if (ret)
+ goto err;
+
+ ret = sof_update_ipc_object(scomp, &asrc->data, SOF_ASRC_TOKENS, swidget->tuples,
+ swidget->num_tuples, sizeof(*asrc), 1);
+ if (ret) {
+ dev_err(scomp->dev, "Parsing ASRC tokens failed\n");
+ goto err;
+ }
+
+ spipe->core_mask |= BIT(swidget->core);
+
+ dev_dbg(scomp->dev, "ASRC sink rate %d, mode 0x%08x\n",
+ asrc->data.out_freq, asrc->data.asrc_mode);
+
+ ret = sof_ipc4_widget_setup_msg(swidget, &asrc->msg);
+ if (ret)
+ goto err;
+
+ return 0;
+err:
+ sof_ipc4_free_audio_fmt(&asrc->available_fmt);
+ kfree(asrc);
+ swidget->private = NULL;
+ return ret;
+}
+
static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget)
{
struct sof_ipc4_src *src = swidget->private;
@@ -1057,6 +1110,18 @@ static void sof_ipc4_widget_free_comp_src(struct snd_sof_widget *swidget)
swidget->private = NULL;
}
+static void sof_ipc4_widget_free_comp_asrc(struct snd_sof_widget *swidget)
+{
+ struct sof_ipc4_asrc *asrc = swidget->private;
+
+ if (!asrc)
+ return;
+
+ sof_ipc4_free_audio_fmt(&asrc->available_fmt);
+ kfree(swidget->private);
+ swidget->private = NULL;
+}
+
static void sof_ipc4_widget_free_comp_mixer(struct snd_sof_widget *swidget)
{
struct sof_ipc4_mixer *mixer = swidget->private;
@@ -1873,10 +1938,10 @@ sof_ipc4_prepare_copier_module(struct snd_sof_widget *swidget,
pipeline->msg.extension |= SOF_IPC4_GLB_EXT_CHAIN_DMA_FIFO_SIZE(fifo_size);
/*
- * Chain DMA does not support stream timestamping, set node_id to invalid
- * to skip the code in sof_ipc4_get_stream_start_offset().
+ * Chain DMA does not support stream timestamping, but it
+ * can use the host side registers for delay calculation.
*/
- copier_data->gtw_cfg.node_id = SOF_IPC4_INVALID_NODE_ID;
+ copier_data->gtw_cfg.node_id = SOF_IPC4_CHAIN_DMA_NODE_ID;
return 0;
}
@@ -2817,6 +2882,16 @@ static int sof_ipc4_widget_setup(struct snd_sof_dev *sdev, struct snd_sof_widget
msg = &src->msg;
break;
}
+ case snd_soc_dapm_asrc:
+ {
+ struct sof_ipc4_asrc *asrc = swidget->private;
+
+ ipc_size = sizeof(asrc->data);
+ ipc_data = &asrc->data;
+
+ msg = &asrc->msg;
+ break;
+ }
case snd_soc_dapm_effect:
{
struct sof_ipc4_process *process = swidget->private;
@@ -3503,6 +3578,15 @@ static enum sof_tokens src_token_list[] = {
SOF_COMP_EXT_TOKENS,
};
+static enum sof_tokens asrc_token_list[] = {
+ SOF_COMP_TOKENS,
+ SOF_ASRC_TOKENS,
+ SOF_AUDIO_FMT_NUM_TOKENS,
+ SOF_IN_AUDIO_FORMAT_TOKENS,
+ SOF_OUT_AUDIO_FORMAT_TOKENS,
+ SOF_COMP_EXT_TOKENS,
+};
+
static enum sof_tokens process_token_list[] = {
SOF_COMP_TOKENS,
SOF_AUDIO_FMT_NUM_TOKENS,
@@ -3548,6 +3632,10 @@ static const struct sof_ipc_tplg_widget_ops tplg_ipc4_widget_ops[SND_SOC_DAPM_TY
src_token_list, ARRAY_SIZE(src_token_list),
NULL, sof_ipc4_prepare_src_module,
NULL},
+ [snd_soc_dapm_asrc] = {sof_ipc4_widget_setup_comp_asrc, sof_ipc4_widget_free_comp_asrc,
+ asrc_token_list, ARRAY_SIZE(asrc_token_list),
+ NULL, sof_ipc4_prepare_src_module, /* Common prepare with SRC */
+ NULL},
[snd_soc_dapm_effect] = {sof_ipc4_widget_setup_comp_process,
sof_ipc4_widget_free_comp_process,
process_token_list, ARRAY_SIZE(process_token_list),
diff --git a/sound/soc/sof/ipc4-topology.h b/sound/soc/sof/ipc4-topology.h
index f4dc499c0ffe..14ba58d2be03 100644
--- a/sound/soc/sof/ipc4-topology.h
+++ b/sound/soc/sof/ipc4-topology.h
@@ -58,6 +58,7 @@
#define SOF_IPC4_DMA_DEVICE_MAX_COUNT 16
+#define SOF_IPC4_CHAIN_DMA_NODE_ID 0x7fffffff
#define SOF_IPC4_INVALID_NODE_ID 0xffffffff
/* FW requires minimum 2ms DMA buffer size */
@@ -435,6 +436,30 @@ struct sof_ipc4_src {
struct sof_ipc4_msg msg;
};
+/*
+ * struct sof_ipc4_asrc_data - IPC data for ASRC
+ * @base_config: IPC base config data
+ * @out_freq: Output rate for sink module, passed as such from topology to FW.
+ * @asrc_mode: Control for ASRC features with bit-fields, passed as such from topolgy to FW.
+ */
+struct sof_ipc4_asrc_data {
+ struct sof_ipc4_base_module_cfg base_config;
+ uint32_t out_freq;
+ uint32_t asrc_mode;
+} __packed __aligned(4);
+
+/**
+ * struct sof_ipc4_asrc - ASRC config data
+ * @data: IPC base config data
+ * @available_fmt: Available audio format
+ * @msg: IPC4 message struct containing header and data info
+ */
+struct sof_ipc4_asrc {
+ struct sof_ipc4_asrc_data data;
+ struct sof_ipc4_available_audio_format available_fmt;
+ struct sof_ipc4_msg msg;
+};
+
/**
* struct sof_ipc4_base_module_cfg_ext - base module config extension containing the pin format
* information for the module. Both @num_input_pin_fmts and @num_output_pin_fmts cannot be 0 for a
diff --git a/sound/soc/sof/ipc4.c b/sound/soc/sof/ipc4.c
index 37e837b22ac8..a4a090e6724a 100644
--- a/sound/soc/sof/ipc4.c
+++ b/sound/soc/sof/ipc4.c
@@ -237,6 +237,26 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms
msg->extension, str);
}
}
+
+const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state)
+{
+ switch (state) {
+ case SOF_IPC4_PIPE_INVALID_STATE:
+ return " (INVALID_STATE)";
+ case SOF_IPC4_PIPE_UNINITIALIZED:
+ return " (UNINITIALIZED)";
+ case SOF_IPC4_PIPE_RESET:
+ return " (RESET)";
+ case SOF_IPC4_PIPE_PAUSED:
+ return " (PAUSED)";
+ case SOF_IPC4_PIPE_RUNNING:
+ return " (RUNNING)";
+ case SOF_IPC4_PIPE_EOS:
+ return " (EOS)";
+ default:
+ return " (<unknown>)";
+ }
+}
#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)
@@ -254,6 +274,11 @@ static void sof_ipc4_log_header(struct device *dev, u8 *text, struct sof_ipc4_ms
else
dev_dbg(dev, "%s: %#x|%#x\n", text, msg->primary, msg->extension);
}
+
+const char *sof_ipc4_pipeline_state_str(enum sof_ipc4_pipeline_state state)
+{
+ return "";
+}
#endif
static void sof_ipc4_dump_payload(struct snd_sof_dev *sdev,
@@ -576,9 +601,19 @@ EXPORT_SYMBOL(sof_ipc4_find_debug_slot_offset_by_type);
static int ipc4_fw_ready(struct snd_sof_dev *sdev, struct sof_ipc4_msg *ipc4_msg)
{
- /* no need to re-check version/ABI for subsequent boots */
- if (!sdev->first_boot)
+ if (!sdev->first_boot) {
+ struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+
+ /*
+ * After the initial boot only check if the libraries have been
+ * restored when full context save is not enabled
+ */
+ if (!ipc4_data->fw_context_save)
+ ipc4_data->libraries_restored = !!(ipc4_msg->primary &
+ SOF_IPC4_FW_READY_LIB_RESTORED);
+
return 0;
+ }
sof_ipc4_create_exception_debugfs_node(sdev);
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
index 7ff080452cbe..c1bea967737d 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -12,7 +12,6 @@
#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>
@@ -46,7 +45,6 @@ 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;
@@ -57,14 +55,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
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);
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 1, &res);
if (ret) {
dev_err(dev, "of_address_to_resource sysmem failed\n");
return ret;
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
index 3b3582d74510..4d6e9300a9c0 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -12,7 +12,6 @@
#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>
@@ -46,7 +45,6 @@ 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;
@@ -57,14 +55,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
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);
+ ret = of_reserved_mem_region_to_resource(dev->of_node, 1, &res);
if (ret) {
dev_err(dev, "of_address_to_resource sysmem failed\n");
return ret;
diff --git a/sound/soc/sof/pcm.c b/sound/soc/sof/pcm.c
index d584a72e6f52..fe43de1fe96c 100644
--- a/sound/soc/sof/pcm.c
+++ b/sound/soc/sof/pcm.c
@@ -20,24 +20,6 @@
#include "sof-utils.h"
#include "ops.h"
-/* Create DMA buffer page table for DSP */
-static int create_page_table(struct snd_soc_component *component,
- struct snd_pcm_substream *substream,
- unsigned char *dma_area, size_t size)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_sof_pcm *spcm;
- struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
- int stream = substream->stream;
-
- spcm = snd_sof_find_spcm_dai(component, rtd);
- if (!spcm)
- return -EINVAL;
-
- return snd_sof_create_page_table(component->dev, dmab,
- spcm->stream[stream].page_table.area, size);
-}
-
/*
* sof pcm period elapse work
*/
@@ -144,7 +126,7 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
* Handle repeated calls to hw_params() without free_pcm() in
* between. At least ALSA OSS emulation depends on this.
*/
- if (pcm_ops && pcm_ops->hw_free && spcm->prepared[substream->stream]) {
+ if (spcm->prepared[substream->stream] && pcm_ops && pcm_ops->hw_free) {
ret = pcm_ops->hw_free(component, substream);
if (ret < 0)
return ret;
@@ -168,9 +150,11 @@ static int sof_pcm_hw_params(struct snd_soc_component *component,
/* create compressed page table for audio firmware */
if (runtime->buffer_changed) {
- ret = create_page_table(component, substream, runtime->dma_area,
- runtime->dma_bytes);
+ struct snd_dma_buffer *dmab = snd_pcm_get_dma_buf(substream);
+ ret = snd_sof_create_page_table(component->dev, dmab,
+ spcm->stream[substream->stream].page_table.area,
+ runtime->dma_bytes);
if (ret < 0)
return ret;
}
@@ -728,7 +712,6 @@ static int sof_pcm_probe(struct snd_soc_component *component)
ret);
pm_error:
- pm_runtime_mark_last_busy(component->dev);
pm_runtime_put_autosuspend(component->dev);
return ret;
diff --git a/sound/soc/sof/sof-client-ipc-flood-test.c b/sound/soc/sof/sof-client-ipc-flood-test.c
index 11b6f7da2882..373f3a125372 100644
--- a/sound/soc/sof/sof-client-ipc-flood-test.c
+++ b/sound/soc/sof/sof-client-ipc-flood-test.c
@@ -223,7 +223,6 @@ static ssize_t sof_ipc_flood_dfs_write(struct file *file, const char __user *buf
ret = sof_debug_ipc_flood_test(cdev, flood_duration_test,
ipc_duration_ms, ipc_count);
- 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);
diff --git a/sound/soc/sof/sof-client-ipc-kernel-injector.c b/sound/soc/sof/sof-client-ipc-kernel-injector.c
index 8b28c3dc920c..249bd2d6c8d2 100644
--- a/sound/soc/sof/sof-client-ipc-kernel-injector.c
+++ b/sound/soc/sof/sof-client-ipc-kernel-injector.c
@@ -65,7 +65,6 @@ static ssize_t sof_kernel_msg_inject_dfs_write(struct file *file, const char __u
sof_client_ipc_rx_message(cdev, hdr, priv->kernel_buffer);
- pm_runtime_mark_last_busy(dev);
ret = pm_runtime_put_autosuspend(dev);
if (ret < 0)
dev_err_ratelimited(dev, "debugfs write failed to idle %d\n", ret);
diff --git a/sound/soc/sof/sof-client-ipc-msg-injector.c b/sound/soc/sof/sof-client-ipc-msg-injector.c
index ba7ca1c5027f..9c8a0fbfb8df 100644
--- a/sound/soc/sof/sof-client-ipc-msg-injector.c
+++ b/sound/soc/sof/sof-client-ipc-msg-injector.c
@@ -137,7 +137,6 @@ static int sof_msg_inject_send_message(struct sof_client_dev *cdev)
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);
diff --git a/sound/soc/sof/sof-client-probes.c b/sound/soc/sof/sof-client-probes.c
index aff9ce980429..663c0d3c314c 100644
--- a/sound/soc/sof/sof-client-probes.c
+++ b/sound/soc/sof/sof-client-probes.c
@@ -238,7 +238,6 @@ static ssize_t sof_probes_dfs_points_read(struct file *file, char __user *to,
kfree(desc);
pm_error:
- pm_runtime_mark_last_busy(dev);
err = pm_runtime_put_autosuspend(dev);
if (err < 0)
dev_err_ratelimited(dev, "debugfs read failed to idle %d\n", err);
@@ -289,7 +288,6 @@ sof_probes_dfs_points_write(struct file *file, const char __user *from,
if (!ret)
ret = count;
- 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);
@@ -337,7 +335,6 @@ sof_probes_dfs_points_remove_write(struct file *file, const char __user *from,
if (!ret)
ret = count;
- 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);
diff --git a/sound/soc/sof/sof-pci-dev.c b/sound/soc/sof/sof-pci-dev.c
index 2fc14b9a33d4..c50249aadea9 100644
--- a/sound/soc/sof/sof-pci-dev.c
+++ b/sound/soc/sof/sof-pci-dev.c
@@ -216,7 +216,7 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
if (ret < 0)
return ret;
- ret = pci_request_regions(pci, "Audio DSP");
+ ret = pcim_request_all_regions(pci, "Audio DSP");
if (ret < 0)
return ret;
@@ -240,8 +240,7 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
path_override->ipc_type = sof_pci_ipc_type;
} else {
dev_err(dev, "Invalid IPC type requested: %d\n", sof_pci_ipc_type);
- ret = -EINVAL;
- goto out;
+ return -EINVAL;
}
path_override->fw_path = fw_path;
@@ -271,13 +270,7 @@ int sof_pci_probe(struct pci_dev *pci, const struct pci_device_id *pci_id)
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);
-
- return ret;
+ return snd_sof_device_probe(dev, sof_pdata);
}
EXPORT_SYMBOL_NS(sof_pci_probe, "SND_SOC_SOF_PCI_DEV");
@@ -290,9 +283,6 @@ void sof_pci_remove(struct pci_dev *pci)
if (snd_sof_device_probe_completed(&pci->dev) &&
!(sof_pci_debug & SOF_PCI_DISABLE_PM_RUNTIME))
pm_runtime_get_noresume(&pci->dev);
-
- /* release pci regions and disable device */
- pci_release_regions(pci);
}
EXPORT_SYMBOL_NS(sof_pci_remove, "SND_SOC_SOF_PCI_DEV");
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 14aa8ecc4bc4..b6d5c8024f8c 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -19,6 +19,10 @@
#include "sof-audio.h"
#include "ops.h"
+static bool disable_function_topology;
+module_param(disable_function_topology, bool, 0444);
+MODULE_PARM_DESC(disable_function_topology, "Disable function topology loading");
+
#define COMP_ID_UNASSIGNED 0xffffffff
/*
* Constants used in the computation of linear volume gain
@@ -571,7 +575,11 @@ static int sof_copy_tuples(struct snd_sof_dev *sdev, struct snd_soc_tplg_vendor_
continue;
tuples[*num_copied_tuples].token = tokens[j].token;
- tuples[*num_copied_tuples].value.s = elem->string;
+ tuples[*num_copied_tuples].value.s =
+ devm_kasprintf(sdev->dev, GFP_KERNEL,
+ "%s", elem->string);
+ if (!tuples[*num_copied_tuples].value.s)
+ return -ENOMEM;
} else {
struct snd_soc_tplg_vendor_value_elem *elem;
@@ -2316,8 +2324,10 @@ static const struct snd_soc_tplg_ops sof_tplg_ops = {
.link_load = sof_link_load,
.link_unload = sof_link_unload,
- /* completion - called at completion of firmware loading */
- .complete = sof_complete,
+ /*
+ * No need to set the complete callback. sof_complete will be called explicitly after
+ * topology loading is complete.
+ */
/* manifest - optional to inform component of manifest */
.manifest = sof_manifest,
@@ -2368,14 +2378,25 @@ static int sof_dspless_widget_ready(struct snd_soc_component *scomp, int index,
struct snd_soc_dapm_widget *w,
struct snd_soc_tplg_dapm_widget *tw)
{
+ struct snd_soc_tplg_private *priv = &tw->priv;
+ int ret;
+
+ /* for snd_soc_dapm_widget.no_wname_in_kcontrol_name */
+ ret = sof_parse_tokens(scomp, w, dapm_widget_tokens,
+ ARRAY_SIZE(dapm_widget_tokens),
+ priv->array, le32_to_cpu(priv->size));
+ if (ret < 0) {
+ dev_err(scomp->dev, "failed to parse dapm widget tokens for %s\n",
+ w->name);
+ return ret;
+ }
+
if (WIDGET_IS_DAI(w->id)) {
static const struct sof_topology_token dai_tokens[] = {
{SOF_TKN_DAI_TYPE, SND_SOC_TPLG_TUPLE_TYPE_STRING, get_token_dai_type, 0}};
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
- struct snd_soc_tplg_private *priv = &tw->priv;
struct snd_sof_widget *swidget;
struct snd_sof_dai *sdai;
- int ret;
swidget = kzalloc(sizeof(*swidget), GFP_KERNEL);
if (!swidget)
@@ -2473,36 +2494,83 @@ static const struct snd_soc_tplg_ops sof_dspless_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);
+ struct snd_sof_pdata *sof_pdata = sdev->pdata;
+ const char *tplg_filename_prefix = sof_pdata->tplg_filename_prefix;
const struct firmware *fw;
+ const char **tplg_files;
+ int tplg_cnt = 0;
int ret;
+ int i;
- dev_dbg(scomp->dev, "loading topology:%s\n", file);
+ tplg_files = kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL);
+ if (!tplg_files)
+ return -ENOMEM;
- ret = request_firmware(&fw, file, scomp->dev);
- if (ret < 0) {
- dev_err(scomp->dev, "error: tplg request firmware %s failed err: %d\n",
- file, ret);
- dev_err(scomp->dev,
- "you may need to download the firmware from https://github.com/thesofproject/sof-bin/\n");
- return ret;
+ if (!sof_pdata->disable_function_topology && !disable_function_topology &&
+ sof_pdata->machine && sof_pdata->machine->get_function_tplg_files) {
+ tplg_cnt = sof_pdata->machine->get_function_tplg_files(scomp->card,
+ sof_pdata->machine,
+ tplg_filename_prefix,
+ &tplg_files);
+ if (tplg_cnt < 0) {
+ kfree(tplg_files);
+ return tplg_cnt;
+ }
}
- if (sdev->dspless_mode_selected)
- ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw);
- else
- ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+ /*
+ * The monolithic topology will be used if there is no get_function_tplg_files
+ * callback or the callback returns 0.
+ */
+ if (!tplg_cnt) {
+ tplg_files[0] = file;
+ tplg_cnt = 1;
+ dev_dbg(scomp->dev, "loading topology: %s\n", file);
+ } else {
+ dev_info(scomp->dev, "Using function topologies instead %s\n", file);
+ }
- if (ret < 0) {
- dev_err(scomp->dev, "error: tplg component load failed %d\n",
- ret);
- ret = -EINVAL;
+ for (i = 0; i < tplg_cnt; i++) {
+ /* Only print the file names if the function topologies are used */
+ if (tplg_files[0] != file)
+ dev_info(scomp->dev, "loading topology %d: %s\n", i, tplg_files[i]);
+
+ ret = request_firmware(&fw, tplg_files[i], scomp->dev);
+ if (ret < 0) {
+ /*
+ * snd_soc_tplg_component_remove(scomp) will be called
+ * if snd_soc_tplg_component_load(scomp) failed and all
+ * objects in the scomp will be removed. No need to call
+ * snd_soc_tplg_component_remove(scomp) here.
+ */
+ dev_err(scomp->dev, "tplg request firmware %s failed err: %d\n",
+ tplg_files[i], ret);
+ goto out;
+ }
+
+ if (sdev->dspless_mode_selected)
+ ret = snd_soc_tplg_component_load(scomp, &sof_dspless_tplg_ops, fw);
+ else
+ ret = snd_soc_tplg_component_load(scomp, &sof_tplg_ops, fw);
+
+ release_firmware(fw);
+
+ if (ret < 0) {
+ dev_err(scomp->dev, "tplg %s component load failed %d\n",
+ tplg_files[i], ret);
+ goto out;
+ }
}
- release_firmware(fw);
+ /* call sof_complete when topologies are loaded successfully */
+ ret = sof_complete(scomp);
+out:
if (ret >= 0 && sdev->led_present)
ret = snd_ctl_led_request();
+ kfree(tplg_files);
+
return ret;
}
EXPORT_SYMBOL(snd_sof_load_topology);
diff --git a/sound/soc/spear/Kconfig b/sound/soc/spear/Kconfig
index 1b053de1ae7c..84a52fd4c3ea 100644
--- a/sound/soc/spear/Kconfig
+++ b/sound/soc/spear/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "ST SPEAr"
+
config SND_SPEAR_SOC
tristate
select SND_SOC_GENERIC_DMAENGINE_PCM
@@ -8,3 +10,5 @@ config SND_SPEAR_SPDIF_OUT
config SND_SPEAR_SPDIF_IN
tristate
+
+endmenu
diff --git a/sound/soc/sprd/Kconfig b/sound/soc/sprd/Kconfig
index 5e0ac8278572..8ddfe55aa21f 100644
--- a/sound/soc/sprd/Kconfig
+++ b/sound/soc/sprd/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Spreadtrum"
+
config SND_SOC_SPRD
tristate "SoC Audio for the Spreadtrum SoC chips"
depends on ARCH_SPRD || COMPILE_TEST
@@ -14,3 +16,5 @@ config SND_SOC_SPRD_MCDT
Say y here to enable multi-channel data transfer support. It
is used for sound stream transmission between audio subsystem
and other AP/CP subsystem.
+
+endmenu
diff --git a/sound/soc/starfive/Kconfig b/sound/soc/starfive/Kconfig
index 279ac5c1d309..1e11aa74594d 100644
--- a/sound/soc/starfive/Kconfig
+++ b/sound/soc/starfive/Kconfig
@@ -1,15 +1,10 @@
# SPDX-License-Identifier: GPL-2.0-only
-config SND_SOC_STARFIVE
- tristate "Audio support for StarFive SoC"
+menu "StarFive"
depends on COMPILE_TEST || ARCH_STARFIVE
- help
- Say Y or M if you want to add support for codecs attached to
- the Starfive SoCs' Audio interfaces. You will also need to
- select the audio interfaces to support below.
+ depends on HAVE_CLK
config SND_SOC_JH7110_PWMDAC
tristate "JH7110 PWM-DAC device driver"
- depends on HAVE_CLK && SND_SOC_STARFIVE
select SND_SOC_GENERIC_DMAENGINE_PCM
select SND_SOC_SPDIF
help
@@ -18,7 +13,8 @@ config SND_SOC_JH7110_PWMDAC
config SND_SOC_JH7110_TDM
tristate "JH7110 TDM device driver"
- depends on HAVE_CLK && SND_SOC_STARFIVE
select SND_SOC_GENERIC_DMAENGINE_PCM
help
Say Y or M if you want to add support for StarFive TDM driver.
+
+endmenu
diff --git a/sound/soc/starfive/jh7110_tdm.c b/sound/soc/starfive/jh7110_tdm.c
index d38090e68df5..afdcde7df91a 100644
--- a/sound/soc/starfive/jh7110_tdm.c
+++ b/sound/soc/starfive/jh7110_tdm.c
@@ -10,6 +10,7 @@
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/dmaengine.h>
+#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
@@ -187,16 +188,8 @@ static int jh7110_tdm_syncdiv(struct jh7110_tdm_dev *tdm)
{
u32 sl, sscale, syncdiv;
- if (tdm->rx.sl >= tdm->tx.sl)
- sl = tdm->rx.sl;
- else
- sl = tdm->tx.sl;
-
- if (tdm->rx.sscale >= tdm->tx.sscale)
- sscale = tdm->rx.sscale;
- else
- sscale = tdm->tx.sscale;
-
+ sl = max(tdm->rx.sl, tdm->tx.sl);
+ sscale = max(tdm->rx.sscale, tdm->tx.sscale);
syncdiv = tdm->pcmclk / tdm->samplerate - 1;
if ((syncdiv + 1) < (sl * sscale)) {
diff --git a/sound/soc/sti/Kconfig b/sound/soc/sti/Kconfig
index f881da4b6aea..8e12356a4490 100644
--- a/sound/soc/sti/Kconfig
+++ b/sound/soc/sti/Kconfig
@@ -3,7 +3,7 @@
# STM SoC audio configuration
#
menuconfig SND_SOC_STI
- tristate "SoC Audio support for STI System-On-Chip"
+ tristate "STI"
depends on SND_SOC
depends on ARCH_STI || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
index da1f7a16605b..2753d6c2a826 100644
--- a/sound/soc/stm/Kconfig
+++ b/sound/soc/stm/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-menu "STMicroelectronics STM32 SOC audio support"
+menu "STMicroelectronics STM32"
config SND_SOC_STM32_SAI
tristate "STM32 SAI interface (Serial Audio Interface) support"
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
index 6037b7a9c97b..0e489097d9c1 100644
--- a/sound/soc/stm/stm32_i2s.c
+++ b/sound/soc/stm/stm32_i2s.c
@@ -461,20 +461,25 @@ err:
return -EINVAL;
}
-static long stm32_i2smclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int stm32_i2smclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct stm32_i2smclk_data *mclk = to_mclk_data(hw);
struct stm32_i2s_data *i2s = mclk->i2s_data;
int ret;
- ret = stm32_i2s_calc_clk_div(i2s, *prate, rate);
- if (ret)
- return ret;
+ ret = stm32_i2s_calc_clk_div(i2s, req->best_parent_rate, req->rate);
+ if (ret) {
+ req->rate = ret;
- mclk->freq = *prate / i2s->divider;
+ return 0;
+ }
- return mclk->freq;
+ mclk->freq = req->best_parent_rate / i2s->divider;
+
+ req->rate = mclk->freq;
+
+ return 0;
}
static unsigned long stm32_i2smclk_recalc_rate(struct clk_hw *hw,
@@ -530,7 +535,7 @@ static const struct clk_ops mclk_ops = {
.enable = stm32_i2smclk_enable,
.disable = stm32_i2smclk_disable,
.recalc_rate = stm32_i2smclk_recalc_rate,
- .round_rate = stm32_i2smclk_round_rate,
+ .determine_rate = stm32_i2smclk_determine_rate,
.set_rate = stm32_i2smclk_set_rate,
};
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
index 504a14584765..fa821e3fb427 100644
--- a/sound/soc/stm/stm32_sai.c
+++ b/sound/soc/stm/stm32_sai.c
@@ -169,20 +169,14 @@ static int stm32_sai_get_parent_clk(struct stm32_sai_data *sai)
struct device *dev = &sai->pdev->dev;
sai->clk_x8k = devm_clk_get(dev, "x8k");
- if (IS_ERR(sai->clk_x8k)) {
- if (PTR_ERR(sai->clk_x8k) != -EPROBE_DEFER)
- dev_err(dev, "missing x8k parent clock: %ld\n",
- PTR_ERR(sai->clk_x8k));
- return PTR_ERR(sai->clk_x8k);
- }
+ if (IS_ERR(sai->clk_x8k))
+ return dev_err_probe(dev, PTR_ERR(sai->clk_x8k),
+ "missing x8k parent clock\n");
sai->clk_x11k = devm_clk_get(dev, "x11k");
- if (IS_ERR(sai->clk_x11k)) {
- if (PTR_ERR(sai->clk_x11k) != -EPROBE_DEFER)
- dev_err(dev, "missing x11k parent clock: %ld\n",
- PTR_ERR(sai->clk_x11k));
- return PTR_ERR(sai->clk_x11k);
- }
+ if (IS_ERR(sai->clk_x11k))
+ return dev_err_probe(dev, PTR_ERR(sai->clk_x11k),
+ "missing x11k parent clock\n");
return 0;
}
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index bf5299ba11c3..463a2b7d023b 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -489,20 +489,22 @@ err:
return -EINVAL;
}
-static long stm32_sai_mclk_round_rate(struct clk_hw *hw, unsigned long rate,
- unsigned long *prate)
+static int stm32_sai_mclk_determine_rate(struct clk_hw *hw,
+ struct clk_rate_request *req)
{
struct stm32_sai_mclk_data *mclk = to_mclk_data(hw);
struct stm32_sai_sub_data *sai = mclk->sai_data;
int div;
- div = stm32_sai_get_clk_div(sai, *prate, rate);
+ div = stm32_sai_get_clk_div(sai, req->best_parent_rate, req->rate);
if (div <= 0)
return -EINVAL;
- mclk->freq = *prate / div;
+ mclk->freq = req->best_parent_rate / div;
- return mclk->freq;
+ req->rate = mclk->freq;
+
+ return 0;
}
static unsigned long stm32_sai_mclk_recalc_rate(struct clk_hw *hw,
@@ -558,7 +560,7 @@ static const struct clk_ops mclk_ops = {
.enable = stm32_sai_mclk_enable,
.disable = stm32_sai_mclk_disable,
.recalc_rate = stm32_sai_mclk_recalc_rate,
- .round_rate = stm32_sai_mclk_round_rate,
+ .determine_rate = stm32_sai_mclk_determine_rate,
.set_rate = stm32_sai_mclk_set_rate,
};
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
index 1f18f016acbb..205f422d9ded 100644
--- a/sound/soc/sunxi/Kconfig
+++ b/sound/soc/sunxi/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-menu "Allwinner SoC Audio support"
+menu "Allwinner"
depends on ARCH_SUNXI || COMPILE_TEST
config SND_SUN4I_CODEC
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 8b9eb1a202f7..7b3496caa31e 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -248,12 +248,10 @@ static int sun8i_codec_runtime_resume(struct device *dev)
struct sun8i_codec *scodec = dev_get_drvdata(dev);
int ret;
- if (scodec->clk_bus) {
- ret = clk_prepare_enable(scodec->clk_bus);
- if (ret) {
- dev_err(dev, "Failed to enable the bus clock\n");
- return ret;
- }
+ ret = clk_prepare_enable(scodec->clk_bus);
+ if (ret) {
+ dev_err(dev, "Failed to enable the bus clock\n");
+ return ret;
}
regcache_cache_only(scodec->regmap, false);
@@ -274,8 +272,7 @@ static int sun8i_codec_runtime_suspend(struct device *dev)
regcache_cache_only(scodec->regmap, true);
regcache_mark_dirty(scodec->regmap);
- if (scodec->clk_bus)
- clk_disable_unprepare(scodec->clk_bus);
+ clk_disable_unprepare(scodec->clk_bus);
return 0;
}
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index 2463c22e9cf6..71203a9197ed 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Tegra"
+
config SND_SOC_TEGRA
tristate "SoC Audio for the Tegra System-on-Chip"
depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
@@ -293,3 +295,5 @@ config SND_SOC_TEGRA_SGTL5000
Colibri T30.
endif
+
+endmenu
diff --git a/sound/soc/tegra/tegra186_asrc.c b/sound/soc/tegra/tegra186_asrc.c
index 5c67e1f01d9b..851509ae07f5 100644
--- a/sound/soc/tegra/tegra186_asrc.c
+++ b/sound/soc/tegra/tegra186_asrc.c
@@ -1,8 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2022-2025 NVIDIA CORPORATION. All rights reserved.
//
// tegra186_asrc.c - Tegra186 ASRC driver
-//
-// Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/delay.h>
@@ -99,7 +98,7 @@ static int tegra186_asrc_runtime_resume(struct device *dev)
* sync is done after this to restore other settings.
*/
regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_SCRATCH_ADDR,
- TEGRA186_ASRC_ARAM_START_ADDR);
+ asrc->soc_data->aram_start_addr);
regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_ENB,
TEGRA186_ASRC_GLOBAL_EN);
@@ -954,8 +953,17 @@ static const struct regmap_config tegra186_asrc_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static const struct tegra_asrc_soc_data soc_data_tegra186 = {
+ .aram_start_addr = TEGRA186_ASRC_ARAM_START_ADDR,
+};
+
+static const struct tegra_asrc_soc_data soc_data_tegra264 = {
+ .aram_start_addr = TEGRA264_ASRC_ARAM_START_ADDR,
+};
+
static const struct of_device_id tegra186_asrc_of_match[] = {
- { .compatible = "nvidia,tegra186-asrc" },
+ { .compatible = "nvidia,tegra186-asrc", .data = &soc_data_tegra186 },
+ { .compatible = "nvidia,tegra264-asrc", .data = &soc_data_tegra264 },
{},
};
MODULE_DEVICE_TABLE(of, tegra186_asrc_of_match);
@@ -985,6 +993,8 @@ static int tegra186_asrc_platform_probe(struct platform_device *pdev)
return PTR_ERR(asrc->regmap);
}
+ asrc->soc_data = of_device_get_match_data(&pdev->dev);
+
regcache_cache_only(asrc->regmap, true);
regmap_write(asrc->regmap, TEGRA186_ASRC_GLOBAL_CFG,
diff --git a/sound/soc/tegra/tegra186_asrc.h b/sound/soc/tegra/tegra186_asrc.h
index 094fcc723c02..0c98e26d5e72 100644
--- a/sound/soc/tegra/tegra186_asrc.h
+++ b/sound/soc/tegra/tegra186_asrc.h
@@ -1,9 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2022-2024 NVIDIA CORPORATION. All rights reserved.
* tegra186_asrc.h - Definitions for Tegra186 ASRC driver
*
- * Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
- *
*/
#ifndef __TEGRA186_ASRC_H__
@@ -94,6 +92,7 @@
#define TEGRA186_ASRC_RATIO_SOURCE_SW 0x1
#define TEGRA186_ASRC_ARAM_START_ADDR 0x3f800000
+#define TEGRA264_ASRC_ARAM_START_ADDR 0x8a080000
struct tegra186_asrc_lane {
unsigned int int_part;
@@ -104,7 +103,12 @@ struct tegra186_asrc_lane {
unsigned int output_thresh;
};
+struct tegra_asrc_soc_data {
+ unsigned int aram_start_addr;
+};
+
struct tegra186_asrc {
+ const struct tegra_asrc_soc_data *soc_data;
struct tegra186_asrc_lane lane[TEGRA186_ASRC_STREAM_MAX];
struct regmap *regmap;
};
diff --git a/sound/soc/tegra/tegra210_admaif.c b/sound/soc/tegra/tegra210_admaif.c
index 76ff4fe40f65..f88d6a2356e0 100644
--- a/sound/soc/tegra/tegra210_admaif.c
+++ b/sound/soc/tegra/tegra210_admaif.c
@@ -25,12 +25,12 @@
#define CH_RX_REG(reg, id) CH_REG(admaif->soc_data->rx_base, reg, id)
-#define REG_DEFAULTS(id, rx_ctrl, tx_ctrl, tx_base, rx_base) \
+#define REG_DEFAULTS(id, rx_ctrl, tx_ctrl, tx_base, rx_base, cif_ctrl) \
{ CH_REG(rx_base, TEGRA_ADMAIF_RX_INT_MASK, id), 0x00000001 }, \
- { CH_REG(rx_base, TEGRA_ADMAIF_CH_ACIF_RX_CTRL, id), 0x00007700 }, \
+ { CH_REG(rx_base, TEGRA_ADMAIF_CH_ACIF_RX_CTRL, id), cif_ctrl }, \
{ CH_REG(rx_base, TEGRA_ADMAIF_RX_FIFO_CTRL, id), rx_ctrl }, \
{ CH_REG(tx_base, TEGRA_ADMAIF_TX_INT_MASK, id), 0x00000001 }, \
- { CH_REG(tx_base, TEGRA_ADMAIF_CH_ACIF_TX_CTRL, id), 0x00007700 }, \
+ { CH_REG(tx_base, TEGRA_ADMAIF_CH_ACIF_TX_CTRL, id), cif_ctrl }, \
{ CH_REG(tx_base, TEGRA_ADMAIF_TX_FIFO_CTRL, id), tx_ctrl }
#define ADMAIF_REG_DEFAULTS(id, chip) \
@@ -38,7 +38,8 @@
chip ## _ADMAIF_RX ## id ## _FIFO_CTRL_REG_DEFAULT, \
chip ## _ADMAIF_TX ## id ## _FIFO_CTRL_REG_DEFAULT, \
chip ## _ADMAIF_TX_BASE, \
- chip ## _ADMAIF_RX_BASE)
+ chip ## _ADMAIF_RX_BASE, \
+ chip ## _ADMAIF_CIF_REG_DEFAULT)
static const struct reg_default tegra186_admaif_reg_defaults[] = {
{(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA186_ADMAIF_GLOBAL_BASE), 0x00000003},
@@ -78,6 +79,42 @@ static const struct reg_default tegra210_admaif_reg_defaults[] = {
ADMAIF_REG_DEFAULTS(10, TEGRA210)
};
+static const struct reg_default tegra264_admaif_reg_defaults[] = {
+ {(TEGRA_ADMAIF_GLOBAL_CG_0 + TEGRA264_ADMAIF_GLOBAL_BASE), 0x00000003},
+ ADMAIF_REG_DEFAULTS(1, TEGRA264),
+ ADMAIF_REG_DEFAULTS(2, TEGRA264),
+ ADMAIF_REG_DEFAULTS(3, TEGRA264),
+ ADMAIF_REG_DEFAULTS(4, TEGRA264),
+ ADMAIF_REG_DEFAULTS(5, TEGRA264),
+ ADMAIF_REG_DEFAULTS(6, TEGRA264),
+ ADMAIF_REG_DEFAULTS(7, TEGRA264),
+ ADMAIF_REG_DEFAULTS(8, TEGRA264),
+ ADMAIF_REG_DEFAULTS(9, TEGRA264),
+ ADMAIF_REG_DEFAULTS(10, TEGRA264),
+ ADMAIF_REG_DEFAULTS(11, TEGRA264),
+ ADMAIF_REG_DEFAULTS(12, TEGRA264),
+ ADMAIF_REG_DEFAULTS(13, TEGRA264),
+ ADMAIF_REG_DEFAULTS(14, TEGRA264),
+ ADMAIF_REG_DEFAULTS(15, TEGRA264),
+ ADMAIF_REG_DEFAULTS(16, TEGRA264),
+ ADMAIF_REG_DEFAULTS(17, TEGRA264),
+ ADMAIF_REG_DEFAULTS(18, TEGRA264),
+ ADMAIF_REG_DEFAULTS(19, TEGRA264),
+ ADMAIF_REG_DEFAULTS(20, TEGRA264),
+ ADMAIF_REG_DEFAULTS(21, TEGRA264),
+ ADMAIF_REG_DEFAULTS(22, TEGRA264),
+ ADMAIF_REG_DEFAULTS(23, TEGRA264),
+ ADMAIF_REG_DEFAULTS(24, TEGRA264),
+ ADMAIF_REG_DEFAULTS(25, TEGRA264),
+ ADMAIF_REG_DEFAULTS(26, TEGRA264),
+ ADMAIF_REG_DEFAULTS(27, TEGRA264),
+ ADMAIF_REG_DEFAULTS(28, TEGRA264),
+ ADMAIF_REG_DEFAULTS(29, TEGRA264),
+ ADMAIF_REG_DEFAULTS(30, TEGRA264),
+ ADMAIF_REG_DEFAULTS(31, TEGRA264),
+ ADMAIF_REG_DEFAULTS(32, TEGRA264)
+};
+
static bool tegra_admaif_wr_reg(struct device *dev, unsigned int reg)
{
struct tegra_admaif *admaif = dev_get_drvdata(dev);
@@ -220,6 +257,19 @@ static const struct regmap_config tegra186_admaif_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static const struct regmap_config tegra264_admaif_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA264_ADMAIF_LAST_REG,
+ .writeable_reg = tegra_admaif_wr_reg,
+ .readable_reg = tegra_admaif_rd_reg,
+ .volatile_reg = tegra_admaif_volatile_reg,
+ .reg_defaults = tegra264_admaif_reg_defaults,
+ .num_reg_defaults = TEGRA264_ADMAIF_CHANNEL_COUNT * 6 + 1,
+ .cache_type = REGCACHE_FLAT,
+};
+
static int tegra_admaif_runtime_suspend(struct device *dev)
{
struct tegra_admaif *admaif = dev_get_drvdata(dev);
@@ -330,7 +380,10 @@ static int tegra_admaif_hw_params(struct snd_pcm_substream *substream,
tegra_admaif_set_pack_mode(admaif->regmap, reg, valid_bit);
- tegra_set_cif(admaif->regmap, reg, &cif_conf);
+ if (admaif->soc_data->max_stream_ch == TEGRA264_ADMAIF_MAX_CHANNEL)
+ tegra264_set_cif(admaif->regmap, reg, &cif_conf);
+ else
+ tegra_set_cif(admaif->regmap, reg, &cif_conf);
return 0;
}
@@ -571,13 +624,13 @@ static const struct snd_soc_dai_ops tegra_admaif_dai_ops = {
.prepare = tegra_admaif_prepare,
};
-#define DAI(dai_name) \
+#define DAI(dai_name, channel) \
{ \
.name = dai_name, \
.playback = { \
.stream_name = dai_name " Playback", \
.channels_min = 1, \
- .channels_max = 16, \
+ .channels_max = channel, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
@@ -587,7 +640,7 @@ static const struct snd_soc_dai_ops tegra_admaif_dai_ops = {
.capture = { \
.stream_name = dai_name " Capture", \
.channels_min = 1, \
- .channels_max = 16, \
+ .channels_max = channel, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
@@ -598,39 +651,74 @@ static const struct snd_soc_dai_ops tegra_admaif_dai_ops = {
}
static struct snd_soc_dai_driver tegra210_admaif_cmpnt_dais[] = {
- DAI("ADMAIF1"),
- DAI("ADMAIF2"),
- DAI("ADMAIF3"),
- DAI("ADMAIF4"),
- DAI("ADMAIF5"),
- DAI("ADMAIF6"),
- DAI("ADMAIF7"),
- DAI("ADMAIF8"),
- DAI("ADMAIF9"),
- DAI("ADMAIF10"),
+ DAI("ADMAIF1", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF2", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF3", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF4", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF5", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF6", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF7", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF8", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF9", TEGRA210_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF10", TEGRA210_ADMAIF_MAX_CHANNEL),
};
static struct snd_soc_dai_driver tegra186_admaif_cmpnt_dais[] = {
- DAI("ADMAIF1"),
- DAI("ADMAIF2"),
- DAI("ADMAIF3"),
- DAI("ADMAIF4"),
- DAI("ADMAIF5"),
- DAI("ADMAIF6"),
- DAI("ADMAIF7"),
- DAI("ADMAIF8"),
- DAI("ADMAIF9"),
- DAI("ADMAIF10"),
- DAI("ADMAIF11"),
- DAI("ADMAIF12"),
- DAI("ADMAIF13"),
- DAI("ADMAIF14"),
- DAI("ADMAIF15"),
- DAI("ADMAIF16"),
- DAI("ADMAIF17"),
- DAI("ADMAIF18"),
- DAI("ADMAIF19"),
- DAI("ADMAIF20"),
+ DAI("ADMAIF1", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF2", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF3", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF4", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF5", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF6", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF7", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF8", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF9", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF10", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF11", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF12", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF13", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF14", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF15", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF16", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF17", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF18", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF19", TEGRA186_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF20", TEGRA186_ADMAIF_MAX_CHANNEL),
+};
+
+static struct snd_soc_dai_driver tegra264_admaif_cmpnt_dais[] = {
+ DAI("ADMAIF1", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF2", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF3", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF4", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF5", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF6", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF7", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF8", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF9", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF10", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF11", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF12", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF13", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF14", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF15", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF16", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF17", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF18", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF19", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF20", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF21", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF22", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF23", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF24", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF25", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF26", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF27", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF28", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF29", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF30", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF31", TEGRA264_ADMAIF_MAX_CHANNEL),
+ DAI("ADMAIF32", TEGRA264_ADMAIF_MAX_CHANNEL),
};
static const char * const tegra_admaif_stereo_conv_text[] = {
@@ -710,6 +798,41 @@ static struct snd_kcontrol_new tegra186_admaif_controls[] = {
TEGRA_ADMAIF_CIF_CTRL(20),
};
+static struct snd_kcontrol_new tegra264_admaif_controls[] = {
+ TEGRA_ADMAIF_CIF_CTRL(1),
+ TEGRA_ADMAIF_CIF_CTRL(2),
+ TEGRA_ADMAIF_CIF_CTRL(3),
+ TEGRA_ADMAIF_CIF_CTRL(4),
+ TEGRA_ADMAIF_CIF_CTRL(5),
+ TEGRA_ADMAIF_CIF_CTRL(6),
+ TEGRA_ADMAIF_CIF_CTRL(7),
+ TEGRA_ADMAIF_CIF_CTRL(8),
+ TEGRA_ADMAIF_CIF_CTRL(9),
+ TEGRA_ADMAIF_CIF_CTRL(10),
+ TEGRA_ADMAIF_CIF_CTRL(11),
+ TEGRA_ADMAIF_CIF_CTRL(12),
+ TEGRA_ADMAIF_CIF_CTRL(13),
+ TEGRA_ADMAIF_CIF_CTRL(14),
+ TEGRA_ADMAIF_CIF_CTRL(15),
+ TEGRA_ADMAIF_CIF_CTRL(16),
+ TEGRA_ADMAIF_CIF_CTRL(17),
+ TEGRA_ADMAIF_CIF_CTRL(18),
+ TEGRA_ADMAIF_CIF_CTRL(19),
+ TEGRA_ADMAIF_CIF_CTRL(20),
+ TEGRA_ADMAIF_CIF_CTRL(21),
+ TEGRA_ADMAIF_CIF_CTRL(22),
+ TEGRA_ADMAIF_CIF_CTRL(23),
+ TEGRA_ADMAIF_CIF_CTRL(24),
+ TEGRA_ADMAIF_CIF_CTRL(25),
+ TEGRA_ADMAIF_CIF_CTRL(26),
+ TEGRA_ADMAIF_CIF_CTRL(27),
+ TEGRA_ADMAIF_CIF_CTRL(28),
+ TEGRA_ADMAIF_CIF_CTRL(29),
+ TEGRA_ADMAIF_CIF_CTRL(30),
+ TEGRA_ADMAIF_CIF_CTRL(31),
+ TEGRA_ADMAIF_CIF_CTRL(32),
+};
+
static const struct snd_soc_component_driver tegra210_admaif_cmpnt = {
.controls = tegra210_admaif_controls,
.num_controls = ARRAY_SIZE(tegra210_admaif_controls),
@@ -730,8 +853,19 @@ static const struct snd_soc_component_driver tegra186_admaif_cmpnt = {
.pointer = tegra_pcm_pointer,
};
+static const struct snd_soc_component_driver tegra264_admaif_cmpnt = {
+ .controls = tegra264_admaif_controls,
+ .num_controls = ARRAY_SIZE(tegra264_admaif_controls),
+ .pcm_construct = tegra_pcm_construct,
+ .open = tegra_pcm_open,
+ .close = tegra_pcm_close,
+ .hw_params = tegra_pcm_hw_params,
+ .pointer = tegra_pcm_pointer,
+};
+
static const struct tegra_admaif_soc_data soc_data_tegra210 = {
.num_ch = TEGRA210_ADMAIF_CHANNEL_COUNT,
+ .max_stream_ch = TEGRA210_ADMAIF_MAX_CHANNEL,
.cmpnt = &tegra210_admaif_cmpnt,
.dais = tegra210_admaif_cmpnt_dais,
.regmap_conf = &tegra210_admaif_regmap_config,
@@ -742,6 +876,7 @@ static const struct tegra_admaif_soc_data soc_data_tegra210 = {
static const struct tegra_admaif_soc_data soc_data_tegra186 = {
.num_ch = TEGRA186_ADMAIF_CHANNEL_COUNT,
+ .max_stream_ch = TEGRA186_ADMAIF_MAX_CHANNEL,
.cmpnt = &tegra186_admaif_cmpnt,
.dais = tegra186_admaif_cmpnt_dais,
.regmap_conf = &tegra186_admaif_regmap_config,
@@ -750,9 +885,21 @@ static const struct tegra_admaif_soc_data soc_data_tegra186 = {
.rx_base = TEGRA186_ADMAIF_RX_BASE,
};
+static const struct tegra_admaif_soc_data soc_data_tegra264 = {
+ .num_ch = TEGRA264_ADMAIF_CHANNEL_COUNT,
+ .max_stream_ch = TEGRA264_ADMAIF_MAX_CHANNEL,
+ .cmpnt = &tegra264_admaif_cmpnt,
+ .dais = tegra264_admaif_cmpnt_dais,
+ .regmap_conf = &tegra264_admaif_regmap_config,
+ .global_base = TEGRA264_ADMAIF_GLOBAL_BASE,
+ .tx_base = TEGRA264_ADMAIF_TX_BASE,
+ .rx_base = TEGRA264_ADMAIF_RX_BASE,
+};
+
static const struct of_device_id tegra_admaif_of_match[] = {
{ .compatible = "nvidia,tegra210-admaif", .data = &soc_data_tegra210 },
{ .compatible = "nvidia,tegra186-admaif", .data = &soc_data_tegra186 },
+ { .compatible = "nvidia,tegra264-admaif", .data = &soc_data_tegra264 },
{},
};
MODULE_DEVICE_TABLE(of, tegra_admaif_of_match);
diff --git a/sound/soc/tegra/tegra210_admaif.h b/sound/soc/tegra/tegra210_admaif.h
index 748f886ee74e..304d45c76a9a 100644
--- a/sound/soc/tegra/tegra210_admaif.h
+++ b/sound/soc/tegra/tegra210_admaif.h
@@ -16,12 +16,21 @@
#define TEGRA210_ADMAIF_RX_BASE 0x0
#define TEGRA210_ADMAIF_TX_BASE 0x300
#define TEGRA210_ADMAIF_GLOBAL_BASE 0x700
+#define TEGRA210_ADMAIF_MAX_CHANNEL 16
/* Tegra186 specific */
#define TEGRA186_ADMAIF_LAST_REG 0xd5f
#define TEGRA186_ADMAIF_CHANNEL_COUNT 20
#define TEGRA186_ADMAIF_RX_BASE 0x0
#define TEGRA186_ADMAIF_TX_BASE 0x500
#define TEGRA186_ADMAIF_GLOBAL_BASE 0xd00
+#define TEGRA186_ADMAIF_MAX_CHANNEL 16
+/* Tegra264 specific */
+#define TEGRA264_ADMAIF_LAST_REG 0x205f
+#define TEGRA264_ADMAIF_CHANNEL_COUNT 32
+#define TEGRA264_ADMAIF_RX_BASE 0x0
+#define TEGRA264_ADMAIF_TX_BASE 0x1000
+#define TEGRA264_ADMAIF_GLOBAL_BASE 0x2000
+#define TEGRA264_ADMAIF_MAX_CHANNEL 32
/* Global registers */
#define TEGRA_ADMAIF_GLOBAL_ENABLE 0x0
#define TEGRA_ADMAIF_GLOBAL_CG_0 0x8
@@ -66,6 +75,7 @@
#define SW_RESET_MASK 1
#define SW_RESET 1
/* Default values - Tegra210 */
+#define TEGRA210_ADMAIF_CIF_REG_DEFAULT 0x00007700
#define TEGRA210_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT 0x00000300
#define TEGRA210_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT 0x00000304
#define TEGRA210_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT 0x00000208
@@ -87,6 +97,7 @@
#define TEGRA210_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT 0x0180021a
#define TEGRA210_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT 0x0180021d
/* Default values - Tegra186 */
+#define TEGRA186_ADMAIF_CIF_REG_DEFAULT 0x00007700
#define TEGRA186_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT 0x00000300
#define TEGRA186_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT 0x00000304
#define TEGRA186_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT 0x00000308
@@ -127,6 +138,72 @@
#define TEGRA186_ADMAIF_TX18_FIFO_CTRL_REG_DEFAULT 0x01800237
#define TEGRA186_ADMAIF_TX19_FIFO_CTRL_REG_DEFAULT 0x0180023a
#define TEGRA186_ADMAIF_TX20_FIFO_CTRL_REG_DEFAULT 0x0180023d
+/* Default values - Tegra264 */
+#define TEGRA264_ADMAIF_CIF_REG_DEFAULT 0x00003f00
+#define TEGRA264_ADMAIF_RX1_FIFO_CTRL_REG_DEFAULT 0x00000200
+#define TEGRA264_ADMAIF_RX2_FIFO_CTRL_REG_DEFAULT 0x00000203
+#define TEGRA264_ADMAIF_RX3_FIFO_CTRL_REG_DEFAULT 0x00000206
+#define TEGRA264_ADMAIF_RX4_FIFO_CTRL_REG_DEFAULT 0x00000209
+#define TEGRA264_ADMAIF_RX5_FIFO_CTRL_REG_DEFAULT 0x0000020c
+#define TEGRA264_ADMAIF_RX6_FIFO_CTRL_REG_DEFAULT 0x0000020f
+#define TEGRA264_ADMAIF_RX7_FIFO_CTRL_REG_DEFAULT 0x00000212
+#define TEGRA264_ADMAIF_RX8_FIFO_CTRL_REG_DEFAULT 0x00000215
+#define TEGRA264_ADMAIF_RX9_FIFO_CTRL_REG_DEFAULT 0x00000218
+#define TEGRA264_ADMAIF_RX10_FIFO_CTRL_REG_DEFAULT 0x0000021b
+#define TEGRA264_ADMAIF_RX11_FIFO_CTRL_REG_DEFAULT 0x0000021e
+#define TEGRA264_ADMAIF_RX12_FIFO_CTRL_REG_DEFAULT 0x00000221
+#define TEGRA264_ADMAIF_RX13_FIFO_CTRL_REG_DEFAULT 0x00000224
+#define TEGRA264_ADMAIF_RX14_FIFO_CTRL_REG_DEFAULT 0x00000227
+#define TEGRA264_ADMAIF_RX15_FIFO_CTRL_REG_DEFAULT 0x0000022a
+#define TEGRA264_ADMAIF_RX16_FIFO_CTRL_REG_DEFAULT 0x0000022d
+#define TEGRA264_ADMAIF_RX17_FIFO_CTRL_REG_DEFAULT 0x00000230
+#define TEGRA264_ADMAIF_RX18_FIFO_CTRL_REG_DEFAULT 0x00000233
+#define TEGRA264_ADMAIF_RX19_FIFO_CTRL_REG_DEFAULT 0x00000236
+#define TEGRA264_ADMAIF_RX20_FIFO_CTRL_REG_DEFAULT 0x00000239
+#define TEGRA264_ADMAIF_RX21_FIFO_CTRL_REG_DEFAULT 0x0000023c
+#define TEGRA264_ADMAIF_RX22_FIFO_CTRL_REG_DEFAULT 0x0000023f
+#define TEGRA264_ADMAIF_RX23_FIFO_CTRL_REG_DEFAULT 0x00000242
+#define TEGRA264_ADMAIF_RX24_FIFO_CTRL_REG_DEFAULT 0x00000245
+#define TEGRA264_ADMAIF_RX25_FIFO_CTRL_REG_DEFAULT 0x00000248
+#define TEGRA264_ADMAIF_RX26_FIFO_CTRL_REG_DEFAULT 0x0000024b
+#define TEGRA264_ADMAIF_RX27_FIFO_CTRL_REG_DEFAULT 0x0000024e
+#define TEGRA264_ADMAIF_RX28_FIFO_CTRL_REG_DEFAULT 0x00000251
+#define TEGRA264_ADMAIF_RX29_FIFO_CTRL_REG_DEFAULT 0x00000254
+#define TEGRA264_ADMAIF_RX30_FIFO_CTRL_REG_DEFAULT 0x00000257
+#define TEGRA264_ADMAIF_RX31_FIFO_CTRL_REG_DEFAULT 0x0000025a
+#define TEGRA264_ADMAIF_RX32_FIFO_CTRL_REG_DEFAULT 0x0000025d
+#define TEGRA264_ADMAIF_TX1_FIFO_CTRL_REG_DEFAULT 0x01800200
+#define TEGRA264_ADMAIF_TX2_FIFO_CTRL_REG_DEFAULT 0x01800203
+#define TEGRA264_ADMAIF_TX3_FIFO_CTRL_REG_DEFAULT 0x01800206
+#define TEGRA264_ADMAIF_TX4_FIFO_CTRL_REG_DEFAULT 0x01800209
+#define TEGRA264_ADMAIF_TX5_FIFO_CTRL_REG_DEFAULT 0x0180020c
+#define TEGRA264_ADMAIF_TX6_FIFO_CTRL_REG_DEFAULT 0x0180020f
+#define TEGRA264_ADMAIF_TX7_FIFO_CTRL_REG_DEFAULT 0x01800212
+#define TEGRA264_ADMAIF_TX8_FIFO_CTRL_REG_DEFAULT 0x01800215
+#define TEGRA264_ADMAIF_TX9_FIFO_CTRL_REG_DEFAULT 0x01800218
+#define TEGRA264_ADMAIF_TX10_FIFO_CTRL_REG_DEFAULT 0x0180021b
+#define TEGRA264_ADMAIF_TX11_FIFO_CTRL_REG_DEFAULT 0x0180021e
+#define TEGRA264_ADMAIF_TX12_FIFO_CTRL_REG_DEFAULT 0x01800221
+#define TEGRA264_ADMAIF_TX13_FIFO_CTRL_REG_DEFAULT 0x01800224
+#define TEGRA264_ADMAIF_TX14_FIFO_CTRL_REG_DEFAULT 0x01800227
+#define TEGRA264_ADMAIF_TX15_FIFO_CTRL_REG_DEFAULT 0x0180022a
+#define TEGRA264_ADMAIF_TX16_FIFO_CTRL_REG_DEFAULT 0x0180022d
+#define TEGRA264_ADMAIF_TX17_FIFO_CTRL_REG_DEFAULT 0x01800230
+#define TEGRA264_ADMAIF_TX18_FIFO_CTRL_REG_DEFAULT 0x01800233
+#define TEGRA264_ADMAIF_TX19_FIFO_CTRL_REG_DEFAULT 0x01800236
+#define TEGRA264_ADMAIF_TX20_FIFO_CTRL_REG_DEFAULT 0x01800239
+#define TEGRA264_ADMAIF_TX21_FIFO_CTRL_REG_DEFAULT 0x0180023c
+#define TEGRA264_ADMAIF_TX22_FIFO_CTRL_REG_DEFAULT 0x0180023f
+#define TEGRA264_ADMAIF_TX23_FIFO_CTRL_REG_DEFAULT 0x01800242
+#define TEGRA264_ADMAIF_TX24_FIFO_CTRL_REG_DEFAULT 0x01800245
+#define TEGRA264_ADMAIF_TX25_FIFO_CTRL_REG_DEFAULT 0x01800248
+#define TEGRA264_ADMAIF_TX26_FIFO_CTRL_REG_DEFAULT 0x0180024b
+#define TEGRA264_ADMAIF_TX27_FIFO_CTRL_REG_DEFAULT 0x0180024e
+#define TEGRA264_ADMAIF_TX28_FIFO_CTRL_REG_DEFAULT 0x01800251
+#define TEGRA264_ADMAIF_TX29_FIFO_CTRL_REG_DEFAULT 0x01800254
+#define TEGRA264_ADMAIF_TX30_FIFO_CTRL_REG_DEFAULT 0x01800257
+#define TEGRA264_ADMAIF_TX31_FIFO_CTRL_REG_DEFAULT 0x0180025a
+#define TEGRA264_ADMAIF_TX32_FIFO_CTRL_REG_DEFAULT 0x0180025d
enum {
DATA_8BIT,
@@ -148,6 +225,7 @@ struct tegra_admaif_soc_data {
unsigned int tx_base;
unsigned int rx_base;
unsigned int num_ch;
+ unsigned int max_stream_ch;
};
struct tegra_admaif {
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
index b6c798baedea..ad7cd8655047 100644
--- a/sound/soc/tegra/tegra210_adx.c
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -9,6 +9,7 @@
#include <linux/io.h>
#include <linux/mod_devicetable.h>
#include <linux/module.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -32,21 +33,37 @@ static const struct reg_default tegra210_adx_reg_defaults[] = {
{ TEGRA210_ADX_CFG_RAM_CTRL, 0x00004000},
};
+static const struct reg_default tegra264_adx_reg_defaults[] = {
+ { TEGRA210_ADX_RX_INT_MASK, 0x00000001},
+ { TEGRA210_ADX_RX_CIF_CTRL, 0x00003800},
+ { TEGRA210_ADX_TX_INT_MASK, 0x0000000f },
+ { TEGRA210_ADX_TX1_CIF_CTRL, 0x00003800},
+ { TEGRA210_ADX_TX2_CIF_CTRL, 0x00003800},
+ { TEGRA210_ADX_TX3_CIF_CTRL, 0x00003800},
+ { TEGRA210_ADX_TX4_CIF_CTRL, 0x00003800},
+ { TEGRA210_ADX_CG, 0x1},
+ { TEGRA264_ADX_CFG_RAM_CTRL, 0x00004000},
+};
+
static void tegra210_adx_write_map_ram(struct tegra210_adx *adx)
{
int i;
- regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL,
+ regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_CTRL +
+ adx->soc_data->cya_offset,
TEGRA210_ADX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
TEGRA210_ADX_CFG_RAM_CTRL_ADDR_INIT_EN |
TEGRA210_ADX_CFG_RAM_CTRL_RW_WRITE);
- for (i = 0; i < TEGRA210_ADX_RAM_DEPTH; i++)
- regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA,
+ for (i = 0; i < adx->soc_data->ram_depth; i++)
+ regmap_write(adx->regmap, TEGRA210_ADX_CFG_RAM_DATA +
+ adx->soc_data->cya_offset,
adx->map[i]);
- regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN0, adx->byte_mask[0]);
- regmap_write(adx->regmap, TEGRA210_ADX_IN_BYTE_EN1, adx->byte_mask[1]);
+ for (i = 0; i < adx->soc_data->byte_mask_size; i++)
+ regmap_write(adx->regmap,
+ TEGRA210_ADX_IN_BYTE_EN0 + (i * TEGRA210_ADX_AUDIOCIF_CH_STRIDE),
+ adx->byte_mask[i]);
}
static int tegra210_adx_startup(struct snd_pcm_substream *substream,
@@ -117,7 +134,7 @@ static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,
memset(&cif_conf, 0, sizeof(struct tegra_cif_conf));
- if (channels < 1 || channels > 16)
+ if (channels < 1 || channels > adx->soc_data->max_ch)
return -EINVAL;
switch (format) {
@@ -140,7 +157,10 @@ static int tegra210_adx_set_audio_cif(struct snd_soc_dai *dai,
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
- tegra_set_cif(adx->regmap, reg, &cif_conf);
+ if (adx->soc_data->max_ch == 32)
+ tegra264_set_cif(adx->regmap, reg, &cif_conf);
+ else
+ tegra_set_cif(adx->regmap, reg, &cif_conf);
return 0;
}
@@ -169,7 +189,7 @@ static int tegra210_adx_get_byte_map(struct snd_kcontrol *kcontrol,
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
struct soc_mixer_control *mc;
- unsigned char *bytes_map = (unsigned char *)&adx->map;
+ unsigned char *bytes_map = (unsigned char *)adx->map;
int enabled;
mc = (struct soc_mixer_control *)kcontrol->private_value;
@@ -198,7 +218,7 @@ static int tegra210_adx_put_byte_map(struct snd_kcontrol *kcontrol,
{
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_adx *adx = snd_soc_component_get_drvdata(cmpnt);
- unsigned char *bytes_map = (unsigned char *)&adx->map;
+ unsigned char *bytes_map = (unsigned char *)adx->map;
int value = ucontrol->value.integer.value[0];
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
@@ -402,7 +422,90 @@ static struct snd_kcontrol_new tegra210_adx_controls[] = {
TEGRA210_ADX_BYTE_MAP_CTRL(63),
};
+static struct snd_kcontrol_new tegra264_adx_controls[] = {
+ TEGRA210_ADX_BYTE_MAP_CTRL(64),
+ TEGRA210_ADX_BYTE_MAP_CTRL(65),
+ TEGRA210_ADX_BYTE_MAP_CTRL(66),
+ TEGRA210_ADX_BYTE_MAP_CTRL(67),
+ TEGRA210_ADX_BYTE_MAP_CTRL(68),
+ TEGRA210_ADX_BYTE_MAP_CTRL(69),
+ TEGRA210_ADX_BYTE_MAP_CTRL(70),
+ TEGRA210_ADX_BYTE_MAP_CTRL(71),
+ TEGRA210_ADX_BYTE_MAP_CTRL(72),
+ TEGRA210_ADX_BYTE_MAP_CTRL(73),
+ TEGRA210_ADX_BYTE_MAP_CTRL(74),
+ TEGRA210_ADX_BYTE_MAP_CTRL(75),
+ TEGRA210_ADX_BYTE_MAP_CTRL(76),
+ TEGRA210_ADX_BYTE_MAP_CTRL(77),
+ TEGRA210_ADX_BYTE_MAP_CTRL(78),
+ TEGRA210_ADX_BYTE_MAP_CTRL(79),
+ TEGRA210_ADX_BYTE_MAP_CTRL(80),
+ TEGRA210_ADX_BYTE_MAP_CTRL(81),
+ TEGRA210_ADX_BYTE_MAP_CTRL(82),
+ TEGRA210_ADX_BYTE_MAP_CTRL(83),
+ TEGRA210_ADX_BYTE_MAP_CTRL(84),
+ TEGRA210_ADX_BYTE_MAP_CTRL(85),
+ TEGRA210_ADX_BYTE_MAP_CTRL(86),
+ TEGRA210_ADX_BYTE_MAP_CTRL(87),
+ TEGRA210_ADX_BYTE_MAP_CTRL(88),
+ TEGRA210_ADX_BYTE_MAP_CTRL(89),
+ TEGRA210_ADX_BYTE_MAP_CTRL(90),
+ TEGRA210_ADX_BYTE_MAP_CTRL(91),
+ TEGRA210_ADX_BYTE_MAP_CTRL(92),
+ TEGRA210_ADX_BYTE_MAP_CTRL(93),
+ TEGRA210_ADX_BYTE_MAP_CTRL(94),
+ TEGRA210_ADX_BYTE_MAP_CTRL(95),
+ TEGRA210_ADX_BYTE_MAP_CTRL(96),
+ TEGRA210_ADX_BYTE_MAP_CTRL(97),
+ TEGRA210_ADX_BYTE_MAP_CTRL(98),
+ TEGRA210_ADX_BYTE_MAP_CTRL(99),
+ TEGRA210_ADX_BYTE_MAP_CTRL(100),
+ TEGRA210_ADX_BYTE_MAP_CTRL(101),
+ TEGRA210_ADX_BYTE_MAP_CTRL(102),
+ TEGRA210_ADX_BYTE_MAP_CTRL(103),
+ TEGRA210_ADX_BYTE_MAP_CTRL(104),
+ TEGRA210_ADX_BYTE_MAP_CTRL(105),
+ TEGRA210_ADX_BYTE_MAP_CTRL(106),
+ TEGRA210_ADX_BYTE_MAP_CTRL(107),
+ TEGRA210_ADX_BYTE_MAP_CTRL(108),
+ TEGRA210_ADX_BYTE_MAP_CTRL(109),
+ TEGRA210_ADX_BYTE_MAP_CTRL(110),
+ TEGRA210_ADX_BYTE_MAP_CTRL(111),
+ TEGRA210_ADX_BYTE_MAP_CTRL(112),
+ TEGRA210_ADX_BYTE_MAP_CTRL(113),
+ TEGRA210_ADX_BYTE_MAP_CTRL(114),
+ TEGRA210_ADX_BYTE_MAP_CTRL(115),
+ TEGRA210_ADX_BYTE_MAP_CTRL(116),
+ TEGRA210_ADX_BYTE_MAP_CTRL(117),
+ TEGRA210_ADX_BYTE_MAP_CTRL(118),
+ TEGRA210_ADX_BYTE_MAP_CTRL(119),
+ TEGRA210_ADX_BYTE_MAP_CTRL(120),
+ TEGRA210_ADX_BYTE_MAP_CTRL(121),
+ TEGRA210_ADX_BYTE_MAP_CTRL(122),
+ TEGRA210_ADX_BYTE_MAP_CTRL(123),
+ TEGRA210_ADX_BYTE_MAP_CTRL(124),
+ TEGRA210_ADX_BYTE_MAP_CTRL(125),
+ TEGRA210_ADX_BYTE_MAP_CTRL(126),
+ TEGRA210_ADX_BYTE_MAP_CTRL(127),
+};
+
+static int tegra210_adx_component_probe(struct snd_soc_component *component)
+{
+ struct tegra210_adx *adx = snd_soc_component_get_drvdata(component);
+ int err = 0;
+
+ if (adx->soc_data->num_controls) {
+ err = snd_soc_add_component_controls(component, adx->soc_data->controls,
+ adx->soc_data->num_controls);
+ if (err)
+ dev_err(component->dev, "can't add ADX controls, err: %d\n", err);
+ }
+
+ return err;
+}
+
static const struct snd_soc_component_driver tegra210_adx_cmpnt = {
+ .probe = tegra210_adx_component_probe,
.dapm_widgets = tegra210_adx_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_adx_widgets),
.dapm_routes = tegra210_adx_routes,
@@ -460,6 +563,58 @@ static bool tegra210_adx_volatile_reg(struct device *dev,
return false;
}
+static bool tegra264_adx_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_TX_INT_MASK ... TEGRA210_ADX_TX4_CIF_CTRL:
+ case TEGRA210_ADX_RX_INT_MASK ... TEGRA210_ADX_RX_CIF_CTRL:
+ case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_CG:
+ case TEGRA210_ADX_CTRL ... TEGRA264_ADX_CYA:
+ case TEGRA264_ADX_CFG_RAM_CTRL ... TEGRA264_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra264_adx_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_RX_STATUS ... TEGRA210_ADX_RX_CIF_CTRL:
+ case TEGRA210_ADX_TX_STATUS ... TEGRA210_ADX_TX4_CIF_CTRL:
+ case TEGRA210_ADX_ENABLE ... TEGRA210_ADX_INT_STATUS:
+ case TEGRA210_ADX_CTRL ... TEGRA264_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool tegra264_adx_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_ADX_RX_STATUS:
+ case TEGRA210_ADX_RX_INT_STATUS:
+ case TEGRA210_ADX_RX_INT_SET:
+ case TEGRA210_ADX_TX_STATUS:
+ case TEGRA210_ADX_TX_INT_STATUS:
+ case TEGRA210_ADX_TX_INT_SET:
+ case TEGRA210_ADX_SOFT_RESET:
+ case TEGRA210_ADX_STATUS:
+ case TEGRA210_ADX_INT_STATUS:
+ case TEGRA264_ADX_CFG_RAM_CTRL:
+ case TEGRA264_ADX_CFG_RAM_DATA:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static const struct regmap_config tegra210_adx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -473,8 +628,40 @@ static const struct regmap_config tegra210_adx_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static const struct regmap_config tegra264_adx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA264_ADX_CFG_RAM_DATA,
+ .writeable_reg = tegra264_adx_wr_reg,
+ .readable_reg = tegra264_adx_rd_reg,
+ .volatile_reg = tegra264_adx_volatile_reg,
+ .reg_defaults = tegra264_adx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra264_adx_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
+static const struct tegra210_adx_soc_data soc_data_tegra210 = {
+ .regmap_conf = &tegra210_adx_regmap_config,
+ .max_ch = TEGRA210_ADX_MAX_CHANNEL,
+ .ram_depth = TEGRA210_ADX_RAM_DEPTH,
+ .byte_mask_size = TEGRA210_ADX_BYTE_MASK_COUNT,
+ .cya_offset = TEGRA210_ADX_CYA_OFFSET,
+};
+
+static const struct tegra210_adx_soc_data soc_data_tegra264 = {
+ .regmap_conf = &tegra264_adx_regmap_config,
+ .max_ch = TEGRA264_ADX_MAX_CHANNEL,
+ .ram_depth = TEGRA264_ADX_RAM_DEPTH,
+ .byte_mask_size = TEGRA264_ADX_BYTE_MASK_COUNT,
+ .cya_offset = TEGRA264_ADX_CYA_OFFSET,
+ .controls = tegra264_adx_controls,
+ .num_controls = ARRAY_SIZE(tegra264_adx_controls),
+};
+
static const struct of_device_id tegra210_adx_of_match[] = {
- { .compatible = "nvidia,tegra210-adx" },
+ { .compatible = "nvidia,tegra210-adx", .data = &soc_data_tegra210 },
+ { .compatible = "nvidia,tegra264-adx", .data = &soc_data_tegra264 },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_adx_of_match);
@@ -483,6 +670,8 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_adx *adx;
+ const struct of_device_id *match;
+ struct tegra210_adx_soc_data *soc_data;
void __iomem *regs;
int err;
@@ -490,6 +679,10 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev)
if (!adx)
return -ENOMEM;
+ match = of_match_device(tegra210_adx_of_match, dev);
+ soc_data = (struct tegra210_adx_soc_data *)match->data;
+ adx->soc_data = soc_data;
+
dev_set_drvdata(dev, adx);
regs = devm_platform_ioremap_resource(pdev, 0);
@@ -497,7 +690,7 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev)
return PTR_ERR(regs);
adx->regmap = devm_regmap_init_mmio(dev, regs,
- &tegra210_adx_regmap_config);
+ soc_data->regmap_conf);
if (IS_ERR(adx->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(adx->regmap);
@@ -505,6 +698,20 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev)
regcache_cache_only(adx->regmap, true);
+ adx->map = devm_kzalloc(dev, soc_data->ram_depth * sizeof(*adx->map),
+ GFP_KERNEL);
+ if (!adx->map)
+ return -ENOMEM;
+
+ adx->byte_mask = devm_kzalloc(dev,
+ soc_data->byte_mask_size * sizeof(*adx->byte_mask),
+ GFP_KERNEL);
+ if (!adx->byte_mask)
+ return -ENOMEM;
+
+ tegra210_adx_dais[TEGRA_ADX_IN_DAI_ID].playback.channels_max =
+ adx->soc_data->max_ch;
+
err = devm_snd_soc_register_component(dev, &tegra210_adx_cmpnt,
tegra210_adx_dais,
ARRAY_SIZE(tegra210_adx_dais));
diff --git a/sound/soc/tegra/tegra210_adx.h b/sound/soc/tegra/tegra210_adx.h
index d7dcb6497978..176a4e40de0a 100644
--- a/sound/soc/tegra/tegra210_adx.h
+++ b/sound/soc/tegra/tegra210_adx.h
@@ -1,8 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * tegra210_adx.h - Definitions for Tegra210 ADX driver
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION. All rights reserved.
*
- * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ * tegra210_adx.h - Definitions for Tegra210 ADX driver
*
*/
@@ -36,6 +35,10 @@
#define TEGRA210_ADX_CFG_RAM_CTRL 0xb8
#define TEGRA210_ADX_CFG_RAM_DATA 0xbc
+#define TEGRA264_ADX_CYA 0xb8
+#define TEGRA264_ADX_CFG_RAM_CTRL 0xc0
+#define TEGRA264_ADX_CFG_RAM_DATA 0xc4
+
/* Fields in TEGRA210_ADX_ENABLE */
#define TEGRA210_ADX_ENABLE_SHIFT 0
@@ -62,11 +65,32 @@
#define TEGRA210_ADX_MAP_STREAM_NUMBER_SHIFT 6
#define TEGRA210_ADX_MAP_WORD_NUMBER_SHIFT 2
#define TEGRA210_ADX_MAP_BYTE_NUMBER_SHIFT 0
+#define TEGRA210_ADX_BYTE_MASK_COUNT 2
+#define TEGRA210_ADX_MAX_CHANNEL 16
+#define TEGRA210_ADX_CYA_OFFSET 0
+
+#define TEGRA264_ADX_RAM_DEPTH 32
+#define TEGRA264_ADX_BYTE_MASK_COUNT 4
+#define TEGRA264_ADX_MAX_CHANNEL 32
+#define TEGRA264_ADX_CYA_OFFSET 8
+
+#define TEGRA_ADX_IN_DAI_ID 4
+
+struct tegra210_adx_soc_data {
+ const struct regmap_config *regmap_conf;
+ const struct snd_kcontrol_new *controls;
+ unsigned int num_controls;
+ unsigned int max_ch;
+ unsigned int ram_depth;
+ unsigned int byte_mask_size;
+ unsigned int cya_offset;
+};
struct tegra210_adx {
struct regmap *regmap;
- unsigned int map[TEGRA210_ADX_RAM_DEPTH];
- unsigned int byte_mask[2];
+ unsigned int *map;
+ unsigned int *byte_mask;
+ const struct tegra210_adx_soc_data *soc_data;
};
#endif
diff --git a/sound/soc/tegra/tegra210_ahub.c b/sound/soc/tegra/tegra210_ahub.c
index 99683f292b5d..21aeaeba0b10 100644
--- a/sound/soc/tegra/tegra210_ahub.c
+++ b/sound/soc/tegra/tegra210_ahub.c
@@ -2,7 +2,7 @@
//
// tegra210_ahub.c - Tegra210 AHUB driver
//
-// Copyright (c) 2020-2024, NVIDIA CORPORATION. All rights reserved.
+// Copyright (c) 2020-2025, NVIDIA CORPORATION. All rights reserved.
#include <linux/clk.h>
#include <linux/device.h>
@@ -29,7 +29,7 @@ static int tegra_ahub_get_value_enum(struct snd_kcontrol *kctl,
for (i = 0; i < ahub->soc_data->reg_count; i++) {
unsigned int reg_val;
- reg = e->reg + (TEGRA210_XBAR_PART1_RX * i);
+ reg = e->reg + (ahub->soc_data->xbar_part_size * i);
reg_val = snd_soc_component_read(cmpnt, reg);
reg_val &= ahub->soc_data->mask[i];
@@ -80,7 +80,7 @@ static int tegra_ahub_put_value_enum(struct snd_kcontrol *kctl,
* different part of the MUX register.
*/
for (i = 0; i < ahub->soc_data->reg_count; i++) {
- update[i].reg = e->reg + (TEGRA210_XBAR_PART1_RX * i);
+ update[i].reg = e->reg + (ahub->soc_data->xbar_part_size * i);
update[i].val = (i == reg_idx) ? reg_val : 0;
update[i].mask = ahub->soc_data->mask[i];
update[i].kcontrol = kctl;
@@ -304,6 +304,164 @@ static struct snd_soc_dai_driver tegra186_ahub_dais[] = {
DAI(OPE1 TX),
};
+static struct snd_soc_dai_driver tegra264_ahub_dais[] = {
+ DAI(ADMAIF1),
+ DAI(ADMAIF2),
+ DAI(ADMAIF3),
+ DAI(ADMAIF4),
+ DAI(ADMAIF5),
+ DAI(ADMAIF6),
+ DAI(ADMAIF7),
+ DAI(ADMAIF8),
+ DAI(ADMAIF9),
+ DAI(ADMAIF10),
+ DAI(ADMAIF11),
+ DAI(ADMAIF12),
+ DAI(ADMAIF13),
+ DAI(ADMAIF14),
+ DAI(ADMAIF15),
+ DAI(ADMAIF16),
+ DAI(ADMAIF17),
+ DAI(ADMAIF18),
+ DAI(ADMAIF19),
+ DAI(ADMAIF20),
+ DAI(ADMAIF21),
+ DAI(ADMAIF22),
+ DAI(ADMAIF23),
+ DAI(ADMAIF24),
+ DAI(ADMAIF25),
+ DAI(ADMAIF26),
+ DAI(ADMAIF27),
+ DAI(ADMAIF28),
+ DAI(ADMAIF29),
+ DAI(ADMAIF30),
+ DAI(ADMAIF31),
+ DAI(ADMAIF32),
+ /* XBAR <-> I2S <-> Codec */
+ DAI(I2S1),
+ DAI(I2S2),
+ DAI(I2S3),
+ DAI(I2S4),
+ DAI(I2S5),
+ DAI(I2S6),
+ DAI(I2S7),
+ DAI(I2S8),
+ /* XBAR <-> DMIC <-> Codec */
+ DAI(DMIC1),
+ DAI(DMIC2),
+ /* XBAR <-> DSPK <-> Codec */
+ DAI(DSPK1),
+ /* XBAR -> SFC -> XBAR */
+ DAI(SFC1 RX),
+ DAI(SFC1 TX),
+ DAI(SFC2 RX),
+ DAI(SFC2 TX),
+ DAI(SFC3 RX),
+ DAI(SFC3 TX),
+ DAI(SFC4 RX),
+ DAI(SFC4 TX),
+ /* XBAR -> MVC -> XBAR */
+ DAI(MVC1 RX),
+ DAI(MVC1 TX),
+ DAI(MVC2 RX),
+ DAI(MVC2 TX),
+ /* XBAR -> AMX(4:1) -> XBAR */
+ DAI(AMX1 RX1),
+ DAI(AMX1 RX2),
+ DAI(AMX1 RX3),
+ DAI(AMX1 RX4),
+ DAI(AMX1),
+ DAI(AMX2 RX1),
+ DAI(AMX2 RX2),
+ DAI(AMX2 RX3),
+ DAI(AMX2 RX4),
+ DAI(AMX2),
+ DAI(AMX3 RX1),
+ DAI(AMX3 RX2),
+ DAI(AMX3 RX3),
+ DAI(AMX3 RX4),
+ DAI(AMX3),
+ DAI(AMX4 RX1),
+ DAI(AMX4 RX2),
+ DAI(AMX4 RX3),
+ DAI(AMX4 RX4),
+ DAI(AMX4),
+ DAI(AMX5 RX1),
+ DAI(AMX5 RX2),
+ DAI(AMX5 RX3),
+ DAI(AMX5 RX4),
+ DAI(AMX5),
+ DAI(AMX6 RX1),
+ DAI(AMX6 RX2),
+ DAI(AMX6 RX3),
+ DAI(AMX6 RX4),
+ DAI(AMX6),
+ /* XBAR -> ADX(1:4) -> XBAR */
+ DAI(ADX1),
+ DAI(ADX1 TX1),
+ DAI(ADX1 TX2),
+ DAI(ADX1 TX3),
+ DAI(ADX1 TX4),
+ DAI(ADX2),
+ DAI(ADX2 TX1),
+ DAI(ADX2 TX2),
+ DAI(ADX2 TX3),
+ DAI(ADX2 TX4),
+ DAI(ADX3),
+ DAI(ADX3 TX1),
+ DAI(ADX3 TX2),
+ DAI(ADX3 TX3),
+ DAI(ADX3 TX4),
+ DAI(ADX4),
+ DAI(ADX4 TX1),
+ DAI(ADX4 TX2),
+ DAI(ADX4 TX3),
+ DAI(ADX4 TX4),
+ DAI(ADX5),
+ DAI(ADX5 TX1),
+ DAI(ADX5 TX2),
+ DAI(ADX5 TX3),
+ DAI(ADX5 TX4),
+ DAI(ADX6),
+ DAI(ADX6 TX1),
+ DAI(ADX6 TX2),
+ DAI(ADX6 TX3),
+ DAI(ADX6 TX4),
+ /* XBAR -> MIXER1(10:5) -> XBAR */
+ DAI(MIXER1 RX1),
+ DAI(MIXER1 RX2),
+ DAI(MIXER1 RX3),
+ DAI(MIXER1 RX4),
+ DAI(MIXER1 RX5),
+ DAI(MIXER1 RX6),
+ DAI(MIXER1 RX7),
+ DAI(MIXER1 RX8),
+ DAI(MIXER1 RX9),
+ DAI(MIXER1 RX10),
+ DAI(MIXER1 TX1),
+ DAI(MIXER1 TX2),
+ DAI(MIXER1 TX3),
+ DAI(MIXER1 TX4),
+ DAI(MIXER1 TX5),
+ /* 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),
+ /* XBAR -> OPE -> XBAR */
+ DAI(OPE1 RX),
+ DAI(OPE1 TX),
+};
+
static const char * const tegra210_ahub_mux_texts[] = {
"None",
"ADMAIF1",
@@ -421,6 +579,100 @@ static const char * const tegra186_ahub_mux_texts[] = {
"OPE1",
};
+static const char * const tegra264_ahub_mux_texts[] = {
+ "None",
+ "ADMAIF1",
+ "ADMAIF2",
+ "ADMAIF3",
+ "ADMAIF4",
+ "ADMAIF5",
+ "ADMAIF6",
+ "ADMAIF7",
+ "ADMAIF8",
+ "ADMAIF9",
+ "ADMAIF10",
+ "ADMAIF11",
+ "ADMAIF12",
+ "ADMAIF13",
+ "ADMAIF14",
+ "ADMAIF15",
+ "ADMAIF16",
+ "I2S1",
+ "I2S2",
+ "I2S3",
+ "I2S4",
+ "I2S5",
+ "I2S6",
+ "I2S7",
+ "I2S8",
+ "SFC1",
+ "SFC2",
+ "SFC3",
+ "SFC4",
+ "MIXER1 TX1",
+ "MIXER1 TX2",
+ "MIXER1 TX3",
+ "MIXER1 TX4",
+ "MIXER1 TX5",
+ "AMX1",
+ "AMX2",
+ "AMX3",
+ "AMX4",
+ "AMX5",
+ "AMX6",
+ "OPE1",
+ "MVC1",
+ "MVC2",
+ "DMIC1",
+ "DMIC2",
+ "ADX1 TX1",
+ "ADX1 TX2",
+ "ADX1 TX3",
+ "ADX1 TX4",
+ "ADX2 TX1",
+ "ADX2 TX2",
+ "ADX2 TX3",
+ "ADX2 TX4",
+ "ADX3 TX1",
+ "ADX3 TX2",
+ "ADX3 TX3",
+ "ADX3 TX4",
+ "ADX4 TX1",
+ "ADX4 TX2",
+ "ADX4 TX3",
+ "ADX4 TX4",
+ "ADX5 TX1",
+ "ADX5 TX2",
+ "ADX5 TX3",
+ "ADX5 TX4",
+ "ADX6 TX1",
+ "ADX6 TX2",
+ "ADX6 TX3",
+ "ADX6 TX4",
+ "ASRC1 TX1",
+ "ASRC1 TX2",
+ "ASRC1 TX3",
+ "ASRC1 TX4",
+ "ASRC1 TX5",
+ "ASRC1 TX6",
+ "ADMAIF17",
+ "ADMAIF18",
+ "ADMAIF19",
+ "ADMAIF20",
+ "ADMAIF21",
+ "ADMAIF22",
+ "ADMAIF23",
+ "ADMAIF24",
+ "ADMAIF25",
+ "ADMAIF26",
+ "ADMAIF27",
+ "ADMAIF28",
+ "ADMAIF29",
+ "ADMAIF30",
+ "ADMAIF31",
+ "ADMAIF32",
+};
+
static const unsigned int tegra210_ahub_mux_values[] = {
0,
/* ADMAIF */
@@ -558,6 +810,111 @@ static const unsigned int tegra186_ahub_mux_values[] = {
MUX_VALUE(2, 0),
};
+static const unsigned int tegra264_ahub_mux_values[] = {
+ 0,
+ /* ADMAIF */
+ MUX_VALUE(0, 0),
+ MUX_VALUE(0, 1),
+ MUX_VALUE(0, 2),
+ MUX_VALUE(0, 3),
+ MUX_VALUE(0, 4),
+ MUX_VALUE(0, 5),
+ MUX_VALUE(0, 6),
+ MUX_VALUE(0, 7),
+ MUX_VALUE(0, 8),
+ MUX_VALUE(0, 9),
+ MUX_VALUE(0, 10),
+ MUX_VALUE(0, 11),
+ MUX_VALUE(0, 12),
+ MUX_VALUE(0, 13),
+ MUX_VALUE(0, 14),
+ MUX_VALUE(0, 15),
+ /* I2S */
+ MUX_VALUE(0, 16),
+ MUX_VALUE(0, 17),
+ MUX_VALUE(0, 18),
+ MUX_VALUE(0, 19),
+ MUX_VALUE(0, 20),
+ MUX_VALUE(0, 21),
+ MUX_VALUE(0, 22),
+ MUX_VALUE(0, 23),
+ /* SFC */
+ MUX_VALUE(0, 24),
+ MUX_VALUE(0, 25),
+ MUX_VALUE(0, 26),
+ MUX_VALUE(0, 27),
+ /* MIXER */
+ MUX_VALUE(1, 0),
+ MUX_VALUE(1, 1),
+ MUX_VALUE(1, 2),
+ MUX_VALUE(1, 3),
+ MUX_VALUE(1, 4),
+ /* AMX */
+ MUX_VALUE(1, 8),
+ MUX_VALUE(1, 9),
+ MUX_VALUE(1, 10),
+ MUX_VALUE(1, 11),
+ MUX_VALUE(1, 12),
+ MUX_VALUE(1, 13),
+ /* OPE */
+ MUX_VALUE(2, 0),
+ /* MVC */
+ MUX_VALUE(2, 8),
+ MUX_VALUE(2, 9),
+ /* DMIC */
+ MUX_VALUE(2, 18),
+ MUX_VALUE(2, 19),
+ /* ADX */
+ MUX_VALUE(2, 24),
+ MUX_VALUE(2, 25),
+ MUX_VALUE(2, 26),
+ MUX_VALUE(2, 27),
+ MUX_VALUE(2, 28),
+ MUX_VALUE(2, 29),
+ MUX_VALUE(2, 30),
+ MUX_VALUE(2, 31),
+ MUX_VALUE(3, 0),
+ MUX_VALUE(3, 1),
+ MUX_VALUE(3, 2),
+ MUX_VALUE(3, 3),
+ MUX_VALUE(3, 4),
+ MUX_VALUE(3, 5),
+ MUX_VALUE(3, 6),
+ MUX_VALUE(3, 7),
+ MUX_VALUE(3, 8),
+ MUX_VALUE(3, 9),
+ MUX_VALUE(3, 10),
+ MUX_VALUE(3, 11),
+ MUX_VALUE(3, 12),
+ MUX_VALUE(3, 13),
+ MUX_VALUE(3, 14),
+ MUX_VALUE(3, 15),
+ /* 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),
+ /* ADMAIF */
+ MUX_VALUE(4, 7),
+ MUX_VALUE(4, 8),
+ MUX_VALUE(4, 9),
+ MUX_VALUE(4, 10),
+ MUX_VALUE(4, 11),
+ MUX_VALUE(4, 12),
+ MUX_VALUE(4, 13),
+ MUX_VALUE(4, 14),
+ MUX_VALUE(4, 15),
+ MUX_VALUE(4, 16),
+ MUX_VALUE(4, 17),
+ MUX_VALUE(4, 18),
+ MUX_VALUE(4, 19),
+ MUX_VALUE(4, 20),
+ MUX_VALUE(4, 21),
+ MUX_VALUE(4, 22),
+};
+
/* Controls for t210 */
MUX_ENUM_CTRL_DECL(t210_admaif1_tx, 0x00);
MUX_ENUM_CTRL_DECL(t210_admaif2_tx, 0x01);
@@ -712,6 +1069,103 @@ 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);
+/* Controls for t264 */
+MUX_ENUM_CTRL_DECL_264(t264_admaif1_tx, 0x00);
+MUX_ENUM_CTRL_DECL_264(t264_admaif2_tx, 0x01);
+MUX_ENUM_CTRL_DECL_264(t264_admaif3_tx, 0x02);
+MUX_ENUM_CTRL_DECL_264(t264_admaif4_tx, 0x03);
+MUX_ENUM_CTRL_DECL_264(t264_admaif5_tx, 0x04);
+MUX_ENUM_CTRL_DECL_264(t264_admaif6_tx, 0x05);
+MUX_ENUM_CTRL_DECL_264(t264_admaif7_tx, 0x06);
+MUX_ENUM_CTRL_DECL_264(t264_admaif8_tx, 0x07);
+MUX_ENUM_CTRL_DECL_264(t264_admaif9_tx, 0x08);
+MUX_ENUM_CTRL_DECL_264(t264_admaif10_tx, 0x09);
+MUX_ENUM_CTRL_DECL_264(t264_admaif11_tx, 0x0a);
+MUX_ENUM_CTRL_DECL_264(t264_admaif12_tx, 0x0b);
+MUX_ENUM_CTRL_DECL_264(t264_admaif13_tx, 0x0c);
+MUX_ENUM_CTRL_DECL_264(t264_admaif14_tx, 0x0d);
+MUX_ENUM_CTRL_DECL_264(t264_admaif15_tx, 0x0e);
+MUX_ENUM_CTRL_DECL_264(t264_admaif16_tx, 0x0f);
+MUX_ENUM_CTRL_DECL_264(t264_i2s1_tx, 0x10);
+MUX_ENUM_CTRL_DECL_264(t264_i2s2_tx, 0x11);
+MUX_ENUM_CTRL_DECL_264(t264_i2s3_tx, 0x12);
+MUX_ENUM_CTRL_DECL_264(t264_i2s4_tx, 0x13);
+MUX_ENUM_CTRL_DECL_264(t264_i2s5_tx, 0x14);
+MUX_ENUM_CTRL_DECL_264(t264_i2s6_tx, 0x15);
+MUX_ENUM_CTRL_DECL_264(t264_i2s7_tx, 0x16);
+MUX_ENUM_CTRL_DECL_264(t264_i2s8_tx, 0x17);
+MUX_ENUM_CTRL_DECL_264(t264_sfc1_tx, 0x18);
+MUX_ENUM_CTRL_DECL_264(t264_sfc2_tx, 0x19);
+MUX_ENUM_CTRL_DECL_264(t264_sfc3_tx, 0x1a);
+MUX_ENUM_CTRL_DECL_264(t264_sfc4_tx, 0x1b);
+MUX_ENUM_CTRL_DECL_264(t264_mixer11_tx, 0x20);
+MUX_ENUM_CTRL_DECL_264(t264_mixer12_tx, 0x21);
+MUX_ENUM_CTRL_DECL_264(t264_mixer13_tx, 0x22);
+MUX_ENUM_CTRL_DECL_264(t264_mixer14_tx, 0x23);
+MUX_ENUM_CTRL_DECL_264(t264_mixer15_tx, 0x24);
+MUX_ENUM_CTRL_DECL_264(t264_mixer16_tx, 0x25);
+MUX_ENUM_CTRL_DECL_264(t264_mixer17_tx, 0x26);
+MUX_ENUM_CTRL_DECL_264(t264_mixer18_tx, 0x27);
+MUX_ENUM_CTRL_DECL_264(t264_mixer19_tx, 0x28);
+MUX_ENUM_CTRL_DECL_264(t264_mixer110_tx, 0x29);
+MUX_ENUM_CTRL_DECL_264(t264_dspk1_tx, 0x30);
+MUX_ENUM_CTRL_DECL_264(t264_ope1_tx, 0x40);
+MUX_ENUM_CTRL_DECL_264(t264_mvc1_tx, 0x44);
+MUX_ENUM_CTRL_DECL_264(t264_mvc2_tx, 0x45);
+MUX_ENUM_CTRL_DECL_264(t264_amx11_tx, 0x48);
+MUX_ENUM_CTRL_DECL_264(t264_amx12_tx, 0x49);
+MUX_ENUM_CTRL_DECL_264(t264_amx13_tx, 0x4a);
+MUX_ENUM_CTRL_DECL_264(t264_amx14_tx, 0x4b);
+MUX_ENUM_CTRL_DECL_264(t264_amx21_tx, 0x4c);
+MUX_ENUM_CTRL_DECL_264(t264_amx22_tx, 0x4d);
+MUX_ENUM_CTRL_DECL_264(t264_amx23_tx, 0x4e);
+MUX_ENUM_CTRL_DECL_264(t264_amx24_tx, 0x4f);
+MUX_ENUM_CTRL_DECL_264(t264_amx31_tx, 0x50);
+MUX_ENUM_CTRL_DECL_264(t264_amx32_tx, 0x51);
+MUX_ENUM_CTRL_DECL_264(t264_amx33_tx, 0x52);
+MUX_ENUM_CTRL_DECL_264(t264_amx34_tx, 0x53);
+MUX_ENUM_CTRL_DECL_264(t264_adx1_tx, 0x58);
+MUX_ENUM_CTRL_DECL_264(t264_adx2_tx, 0x59);
+MUX_ENUM_CTRL_DECL_264(t264_adx3_tx, 0x5a);
+MUX_ENUM_CTRL_DECL_264(t264_adx4_tx, 0x5b);
+MUX_ENUM_CTRL_DECL_264(t264_amx41_tx, 0x5c);
+MUX_ENUM_CTRL_DECL_264(t264_amx42_tx, 0x5d);
+MUX_ENUM_CTRL_DECL_264(t264_amx43_tx, 0x5e);
+MUX_ENUM_CTRL_DECL_264(t264_amx44_tx, 0x5f);
+MUX_ENUM_CTRL_DECL_264(t264_admaif17_tx, 0x60);
+MUX_ENUM_CTRL_DECL_264(t264_admaif18_tx, 0x61);
+MUX_ENUM_CTRL_DECL_264(t264_admaif19_tx, 0x62);
+MUX_ENUM_CTRL_DECL_264(t264_admaif20_tx, 0x63);
+MUX_ENUM_CTRL_DECL_264(t264_asrc11_tx, 0x64);
+MUX_ENUM_CTRL_DECL_264(t264_asrc12_tx, 0x65);
+MUX_ENUM_CTRL_DECL_264(t264_asrc13_tx, 0x66);
+MUX_ENUM_CTRL_DECL_264(t264_asrc14_tx, 0x67);
+MUX_ENUM_CTRL_DECL_264(t264_asrc15_tx, 0x68);
+MUX_ENUM_CTRL_DECL_264(t264_asrc16_tx, 0x69);
+MUX_ENUM_CTRL_DECL_264(t264_asrc17_tx, 0x6a);
+MUX_ENUM_CTRL_DECL_264(t264_admaif21_tx, 0x74);
+MUX_ENUM_CTRL_DECL_264(t264_admaif22_tx, 0x75);
+MUX_ENUM_CTRL_DECL_264(t264_admaif23_tx, 0x76);
+MUX_ENUM_CTRL_DECL_264(t264_admaif24_tx, 0x77);
+MUX_ENUM_CTRL_DECL_264(t264_admaif25_tx, 0x78);
+MUX_ENUM_CTRL_DECL_264(t264_admaif26_tx, 0x79);
+MUX_ENUM_CTRL_DECL_264(t264_admaif27_tx, 0x7a);
+MUX_ENUM_CTRL_DECL_264(t264_admaif28_tx, 0x7b);
+MUX_ENUM_CTRL_DECL_264(t264_admaif29_tx, 0x7c);
+MUX_ENUM_CTRL_DECL_264(t264_admaif30_tx, 0x7d);
+MUX_ENUM_CTRL_DECL_264(t264_admaif31_tx, 0x7e);
+MUX_ENUM_CTRL_DECL_264(t264_admaif32_tx, 0x7f);
+MUX_ENUM_CTRL_DECL_264(t264_amx51_tx, 0x80);
+MUX_ENUM_CTRL_DECL_264(t264_amx52_tx, 0x81);
+MUX_ENUM_CTRL_DECL_264(t264_amx53_tx, 0x82);
+MUX_ENUM_CTRL_DECL_264(t264_amx54_tx, 0x83);
+MUX_ENUM_CTRL_DECL_264(t264_amx61_tx, 0x84);
+MUX_ENUM_CTRL_DECL_264(t264_amx62_tx, 0x85);
+MUX_ENUM_CTRL_DECL_264(t264_amx63_tx, 0x86);
+MUX_ENUM_CTRL_DECL_264(t264_amx64_tx, 0x87);
+MUX_ENUM_CTRL_DECL_264(t264_adx5_tx, 0x88);
+MUX_ENUM_CTRL_DECL_264(t264_adx6_tx, 0x89);
+
static const struct snd_soc_dapm_widget tegra210_ahub_widgets[] = {
WIDGETS("ADMAIF1", t210_admaif1_tx),
WIDGETS("ADMAIF2", t210_admaif2_tx),
@@ -996,6 +1450,147 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
WIDGETS("OPE1", t186_ope1_tx),
};
+static const struct snd_soc_dapm_widget tegra264_ahub_widgets[] = {
+ WIDGETS("ADMAIF1", t264_admaif1_tx),
+ WIDGETS("ADMAIF2", t264_admaif2_tx),
+ WIDGETS("ADMAIF3", t264_admaif3_tx),
+ WIDGETS("ADMAIF4", t264_admaif4_tx),
+ WIDGETS("ADMAIF5", t264_admaif5_tx),
+ WIDGETS("ADMAIF6", t264_admaif6_tx),
+ WIDGETS("ADMAIF7", t264_admaif7_tx),
+ WIDGETS("ADMAIF8", t264_admaif8_tx),
+ WIDGETS("ADMAIF9", t264_admaif9_tx),
+ WIDGETS("ADMAIF10", t264_admaif10_tx),
+ WIDGETS("ADMAIF11", t264_admaif11_tx),
+ WIDGETS("ADMAIF12", t264_admaif12_tx),
+ WIDGETS("ADMAIF13", t264_admaif13_tx),
+ WIDGETS("ADMAIF14", t264_admaif14_tx),
+ WIDGETS("ADMAIF15", t264_admaif15_tx),
+ WIDGETS("ADMAIF16", t264_admaif16_tx),
+ WIDGETS("ADMAIF17", t264_admaif17_tx),
+ WIDGETS("ADMAIF18", t264_admaif18_tx),
+ WIDGETS("ADMAIF19", t264_admaif19_tx),
+ WIDGETS("ADMAIF20", t264_admaif20_tx),
+ WIDGETS("ADMAIF21", t264_admaif21_tx),
+ WIDGETS("ADMAIF22", t264_admaif22_tx),
+ WIDGETS("ADMAIF23", t264_admaif23_tx),
+ WIDGETS("ADMAIF24", t264_admaif24_tx),
+ WIDGETS("ADMAIF25", t264_admaif25_tx),
+ WIDGETS("ADMAIF26", t264_admaif26_tx),
+ WIDGETS("ADMAIF27", t264_admaif27_tx),
+ WIDGETS("ADMAIF28", t264_admaif28_tx),
+ WIDGETS("ADMAIF29", t264_admaif29_tx),
+ WIDGETS("ADMAIF30", t264_admaif30_tx),
+ WIDGETS("ADMAIF31", t264_admaif31_tx),
+ WIDGETS("ADMAIF32", t264_admaif32_tx),
+ WIDGETS("I2S1", t264_i2s1_tx),
+ WIDGETS("I2S2", t264_i2s2_tx),
+ WIDGETS("I2S3", t264_i2s3_tx),
+ WIDGETS("I2S4", t264_i2s4_tx),
+ WIDGETS("I2S5", t264_i2s5_tx),
+ WIDGETS("I2S6", t264_i2s6_tx),
+ WIDGETS("I2S7", t264_i2s7_tx),
+ WIDGETS("I2S8", t264_i2s8_tx),
+ TX_WIDGETS("DMIC1"),
+ TX_WIDGETS("DMIC2"),
+ WIDGETS("DSPK1", t264_dspk1_tx),
+ WIDGETS("SFC1", t264_sfc1_tx),
+ WIDGETS("SFC2", t264_sfc2_tx),
+ WIDGETS("SFC3", t264_sfc3_tx),
+ WIDGETS("SFC4", t264_sfc4_tx),
+ WIDGETS("MVC1", t264_mvc1_tx),
+ WIDGETS("MVC2", t264_mvc2_tx),
+ WIDGETS("AMX1 RX1", t264_amx11_tx),
+ WIDGETS("AMX1 RX2", t264_amx12_tx),
+ WIDGETS("AMX1 RX3", t264_amx13_tx),
+ WIDGETS("AMX1 RX4", t264_amx14_tx),
+ WIDGETS("AMX2 RX1", t264_amx21_tx),
+ WIDGETS("AMX2 RX2", t264_amx22_tx),
+ WIDGETS("AMX2 RX3", t264_amx23_tx),
+ WIDGETS("AMX2 RX4", t264_amx24_tx),
+ WIDGETS("AMX3 RX1", t264_amx31_tx),
+ WIDGETS("AMX3 RX2", t264_amx32_tx),
+ WIDGETS("AMX3 RX3", t264_amx33_tx),
+ WIDGETS("AMX3 RX4", t264_amx34_tx),
+ WIDGETS("AMX4 RX1", t264_amx41_tx),
+ WIDGETS("AMX4 RX2", t264_amx42_tx),
+ WIDGETS("AMX4 RX3", t264_amx43_tx),
+ WIDGETS("AMX4 RX4", t264_amx44_tx),
+ WIDGETS("AMX5 RX1", t264_amx51_tx),
+ WIDGETS("AMX5 RX2", t264_amx52_tx),
+ WIDGETS("AMX5 RX3", t264_amx53_tx),
+ WIDGETS("AMX5 RX4", t264_amx54_tx),
+ WIDGETS("AMX6 RX1", t264_amx61_tx),
+ WIDGETS("AMX6 RX2", t264_amx62_tx),
+ WIDGETS("AMX6 RX3", t264_amx63_tx),
+ WIDGETS("AMX6 RX4", t264_amx64_tx),
+ TX_WIDGETS("AMX1"),
+ TX_WIDGETS("AMX2"),
+ TX_WIDGETS("AMX3"),
+ TX_WIDGETS("AMX4"),
+ TX_WIDGETS("AMX5"),
+ TX_WIDGETS("AMX6"),
+ WIDGETS("ADX1", t264_adx1_tx),
+ WIDGETS("ADX2", t264_adx2_tx),
+ WIDGETS("ADX3", t264_adx3_tx),
+ WIDGETS("ADX4", t264_adx4_tx),
+ WIDGETS("ADX5", t264_adx5_tx),
+ WIDGETS("ADX6", t264_adx6_tx),
+ TX_WIDGETS("ADX1 TX1"),
+ TX_WIDGETS("ADX1 TX2"),
+ TX_WIDGETS("ADX1 TX3"),
+ TX_WIDGETS("ADX1 TX4"),
+ TX_WIDGETS("ADX2 TX1"),
+ TX_WIDGETS("ADX2 TX2"),
+ TX_WIDGETS("ADX2 TX3"),
+ TX_WIDGETS("ADX2 TX4"),
+ TX_WIDGETS("ADX3 TX1"),
+ TX_WIDGETS("ADX3 TX2"),
+ TX_WIDGETS("ADX3 TX3"),
+ TX_WIDGETS("ADX3 TX4"),
+ TX_WIDGETS("ADX4 TX1"),
+ TX_WIDGETS("ADX4 TX2"),
+ TX_WIDGETS("ADX4 TX3"),
+ TX_WIDGETS("ADX4 TX4"),
+ TX_WIDGETS("ADX5 TX1"),
+ TX_WIDGETS("ADX5 TX2"),
+ TX_WIDGETS("ADX5 TX3"),
+ TX_WIDGETS("ADX5 TX4"),
+ TX_WIDGETS("ADX6 TX1"),
+ TX_WIDGETS("ADX6 TX2"),
+ TX_WIDGETS("ADX6 TX3"),
+ TX_WIDGETS("ADX6 TX4"),
+ WIDGETS("MIXER1 RX1", t264_mixer11_tx),
+ WIDGETS("MIXER1 RX2", t264_mixer12_tx),
+ WIDGETS("MIXER1 RX3", t264_mixer13_tx),
+ WIDGETS("MIXER1 RX4", t264_mixer14_tx),
+ WIDGETS("MIXER1 RX5", t264_mixer15_tx),
+ WIDGETS("MIXER1 RX6", t264_mixer16_tx),
+ WIDGETS("MIXER1 RX7", t264_mixer17_tx),
+ WIDGETS("MIXER1 RX8", t264_mixer18_tx),
+ WIDGETS("MIXER1 RX9", t264_mixer19_tx),
+ WIDGETS("MIXER1 RX10", t264_mixer110_tx),
+ TX_WIDGETS("MIXER1 TX1"),
+ TX_WIDGETS("MIXER1 TX2"),
+ TX_WIDGETS("MIXER1 TX3"),
+ TX_WIDGETS("MIXER1 TX4"),
+ TX_WIDGETS("MIXER1 TX5"),
+ WIDGETS("ASRC1 RX1", t264_asrc11_tx),
+ WIDGETS("ASRC1 RX2", t264_asrc12_tx),
+ WIDGETS("ASRC1 RX3", t264_asrc13_tx),
+ WIDGETS("ASRC1 RX4", t264_asrc14_tx),
+ WIDGETS("ASRC1 RX5", t264_asrc15_tx),
+ WIDGETS("ASRC1 RX6", t264_asrc16_tx),
+ WIDGETS("ASRC1 RX7", t264_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"),
+ WIDGETS("OPE1", t264_ope1_tx),
+};
+
#define TEGRA_COMMON_MUX_ROUTES(name) \
{ name " XBAR-TX", NULL, name " Mux" }, \
{ name " Mux", "ADMAIF1", "ADMAIF1 XBAR-RX" }, \
@@ -1015,7 +1610,6 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
{ name " Mux", "I2S5", "I2S5 XBAR-RX" }, \
{ name " Mux", "DMIC1", "DMIC1 XBAR-RX" }, \
{ name " Mux", "DMIC2", "DMIC2 XBAR-RX" }, \
- { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \
{ name " Mux", "SFC1", "SFC1 XBAR-RX" }, \
{ name " Mux", "SFC2", "SFC2 XBAR-RX" }, \
{ name " Mux", "SFC3", "SFC3 XBAR-RX" }, \
@@ -1040,6 +1634,7 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
{ name " Mux", "OPE1", "OPE1 XBAR-RX" },
#define TEGRA210_ONLY_MUX_ROUTES(name) \
+ { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \
{ name " Mux", "OPE2", "OPE2 XBAR-RX" },
#define TEGRA186_ONLY_MUX_ROUTES(name) \
@@ -1054,6 +1649,7 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
{ name " Mux", "ADMAIF19", "ADMAIF19 XBAR-RX" }, \
{ name " Mux", "ADMAIF20", "ADMAIF20 XBAR-RX" }, \
{ name " Mux", "I2S6", "I2S6 XBAR-RX" }, \
+ { name " Mux", "DMIC3", "DMIC3 XBAR-RX" }, \
{ name " Mux", "DMIC4", "DMIC4 XBAR-RX" }, \
{ name " Mux", "AMX3", "AMX3 XBAR-RX" }, \
{ name " Mux", "AMX4", "AMX4 XBAR-RX" }, \
@@ -1072,6 +1668,59 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
{ name " Mux", "ASRC1 TX5", "ASRC1 TX5 XBAR-RX" }, \
{ name " Mux", "ASRC1 TX6", "ASRC1 TX6 XBAR-RX" },
+#define TEGRA264_ONLY_MUX_ROUTES(name) \
+ { name " Mux", "ADMAIF11", "ADMAIF11 XBAR-RX" }, \
+ { name " Mux", "ADMAIF12", "ADMAIF12 XBAR-RX" }, \
+ { name " Mux", "ADMAIF13", "ADMAIF13 XBAR-RX" }, \
+ { name " Mux", "ADMAIF14", "ADMAIF14 XBAR-RX" }, \
+ { name " Mux", "ADMAIF15", "ADMAIF15 XBAR-RX" }, \
+ { name " Mux", "ADMAIF16", "ADMAIF16 XBAR-RX" }, \
+ { name " Mux", "ADMAIF17", "ADMAIF17 XBAR-RX" }, \
+ { name " Mux", "ADMAIF18", "ADMAIF18 XBAR-RX" }, \
+ { name " Mux", "ADMAIF19", "ADMAIF19 XBAR-RX" }, \
+ { name " Mux", "ADMAIF20", "ADMAIF20 XBAR-RX" }, \
+ { name " Mux", "ADMAIF21", "ADMAIF21 XBAR-RX" }, \
+ { name " Mux", "ADMAIF22", "ADMAIF22 XBAR-RX" }, \
+ { name " Mux", "ADMAIF23", "ADMAIF23 XBAR-RX" }, \
+ { name " Mux", "ADMAIF24", "ADMAIF24 XBAR-RX" }, \
+ { name " Mux", "ADMAIF25", "ADMAIF25 XBAR-RX" }, \
+ { name " Mux", "ADMAIF26", "ADMAIF26 XBAR-RX" }, \
+ { name " Mux", "ADMAIF27", "ADMAIF27 XBAR-RX" }, \
+ { name " Mux", "ADMAIF28", "ADMAIF28 XBAR-RX" }, \
+ { name " Mux", "ADMAIF29", "ADMAIF29 XBAR-RX" }, \
+ { name " Mux", "ADMAIF30", "ADMAIF30 XBAR-RX" }, \
+ { name " Mux", "ADMAIF31", "ADMAIF31 XBAR-RX" }, \
+ { name " Mux", "ADMAIF32", "ADMAIF32 XBAR-RX" }, \
+ { name " Mux", "I2S6", "I2S6 XBAR-RX" }, \
+ { name " Mux", "I2S7", "I2S7 XBAR-RX" }, \
+ { name " Mux", "I2S8", "I2S8 XBAR-RX" }, \
+ { name " Mux", "AMX3", "AMX3 XBAR-RX" }, \
+ { name " Mux", "AMX4", "AMX4 XBAR-RX" }, \
+ { name " Mux", "AMX5", "AMX5 XBAR-RX" }, \
+ { name " Mux", "AMX6", "AMX6 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX1", "ADX3 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX2", "ADX3 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX3", "ADX3 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX3 TX4", "ADX3 TX4 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX1", "ADX4 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX2", "ADX4 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX3", "ADX4 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX4 TX4", "ADX4 TX4 XBAR-RX" }, \
+ { name " Mux", "ADX5 TX1", "ADX5 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX5 TX2", "ADX5 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX5 TX3", "ADX5 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX5 TX4", "ADX5 TX4 XBAR-RX" }, \
+ { name " Mux", "ADX6 TX1", "ADX6 TX1 XBAR-RX" }, \
+ { name " Mux", "ADX6 TX2", "ADX6 TX2 XBAR-RX" }, \
+ { name " Mux", "ADX6 TX3", "ADX6 TX3 XBAR-RX" }, \
+ { name " Mux", "ADX6 TX4", "ADX6 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) \
TEGRA210_ONLY_MUX_ROUTES(name)
@@ -1080,6 +1729,10 @@ static const struct snd_soc_dapm_widget tegra234_ahub_widgets[] = {
TEGRA_COMMON_MUX_ROUTES(name) \
TEGRA186_ONLY_MUX_ROUTES(name)
+#define TEGRA264_MUX_ROUTES(name) \
+ TEGRA_COMMON_MUX_ROUTES(name) \
+ TEGRA264_ONLY_MUX_ROUTES(name)
+
/* Connect FEs with XBAR */
#define TEGRA_FE_ROUTES(name) \
{ name " XBAR-Playback", NULL, name " Playback" }, \
@@ -1238,6 +1891,136 @@ static const struct snd_soc_dapm_route tegra186_ahub_routes[] = {
TEGRA186_MUX_ROUTES("OPE1")
};
+static const struct snd_soc_dapm_route tegra264_ahub_routes[] = {
+ TEGRA_FE_ROUTES("ADMAIF1")
+ TEGRA_FE_ROUTES("ADMAIF2")
+ TEGRA_FE_ROUTES("ADMAIF3")
+ TEGRA_FE_ROUTES("ADMAIF4")
+ TEGRA_FE_ROUTES("ADMAIF5")
+ TEGRA_FE_ROUTES("ADMAIF6")
+ TEGRA_FE_ROUTES("ADMAIF7")
+ TEGRA_FE_ROUTES("ADMAIF8")
+ TEGRA_FE_ROUTES("ADMAIF9")
+ TEGRA_FE_ROUTES("ADMAIF10")
+ TEGRA_FE_ROUTES("ADMAIF11")
+ TEGRA_FE_ROUTES("ADMAIF12")
+ TEGRA_FE_ROUTES("ADMAIF13")
+ TEGRA_FE_ROUTES("ADMAIF14")
+ TEGRA_FE_ROUTES("ADMAIF15")
+ TEGRA_FE_ROUTES("ADMAIF16")
+ TEGRA_FE_ROUTES("ADMAIF17")
+ TEGRA_FE_ROUTES("ADMAIF18")
+ TEGRA_FE_ROUTES("ADMAIF19")
+ TEGRA_FE_ROUTES("ADMAIF20")
+ TEGRA_FE_ROUTES("ADMAIF21")
+ TEGRA_FE_ROUTES("ADMAIF22")
+ TEGRA_FE_ROUTES("ADMAIF23")
+ TEGRA_FE_ROUTES("ADMAIF24")
+ TEGRA_FE_ROUTES("ADMAIF25")
+ TEGRA_FE_ROUTES("ADMAIF26")
+ TEGRA_FE_ROUTES("ADMAIF27")
+ TEGRA_FE_ROUTES("ADMAIF28")
+ TEGRA_FE_ROUTES("ADMAIF29")
+ TEGRA_FE_ROUTES("ADMAIF30")
+ TEGRA_FE_ROUTES("ADMAIF31")
+ TEGRA_FE_ROUTES("ADMAIF32")
+ TEGRA264_MUX_ROUTES("ADMAIF1")
+ TEGRA264_MUX_ROUTES("ADMAIF2")
+ TEGRA264_MUX_ROUTES("ADMAIF3")
+ TEGRA264_MUX_ROUTES("ADMAIF4")
+ TEGRA264_MUX_ROUTES("ADMAIF5")
+ TEGRA264_MUX_ROUTES("ADMAIF6")
+ TEGRA264_MUX_ROUTES("ADMAIF7")
+ TEGRA264_MUX_ROUTES("ADMAIF8")
+ TEGRA264_MUX_ROUTES("ADMAIF9")
+ TEGRA264_MUX_ROUTES("ADMAIF10")
+ TEGRA264_MUX_ROUTES("ADMAIF11")
+ TEGRA264_MUX_ROUTES("ADMAIF12")
+ TEGRA264_MUX_ROUTES("ADMAIF13")
+ TEGRA264_MUX_ROUTES("ADMAIF14")
+ TEGRA264_MUX_ROUTES("ADMAIF15")
+ TEGRA264_MUX_ROUTES("ADMAIF16")
+ TEGRA264_MUX_ROUTES("ADMAIF17")
+ TEGRA264_MUX_ROUTES("ADMAIF18")
+ TEGRA264_MUX_ROUTES("ADMAIF19")
+ TEGRA264_MUX_ROUTES("ADMAIF20")
+ TEGRA264_MUX_ROUTES("ADMAIF21")
+ TEGRA264_MUX_ROUTES("ADMAIF22")
+ TEGRA264_MUX_ROUTES("ADMAIF23")
+ TEGRA264_MUX_ROUTES("ADMAIF24")
+ TEGRA264_MUX_ROUTES("ADMAIF25")
+ TEGRA264_MUX_ROUTES("ADMAIF26")
+ TEGRA264_MUX_ROUTES("ADMAIF27")
+ TEGRA264_MUX_ROUTES("ADMAIF28")
+ TEGRA264_MUX_ROUTES("ADMAIF29")
+ TEGRA264_MUX_ROUTES("ADMAIF30")
+ TEGRA264_MUX_ROUTES("ADMAIF31")
+ TEGRA264_MUX_ROUTES("ADMAIF32")
+ TEGRA264_MUX_ROUTES("I2S1")
+ TEGRA264_MUX_ROUTES("I2S2")
+ TEGRA264_MUX_ROUTES("I2S3")
+ TEGRA264_MUX_ROUTES("I2S4")
+ TEGRA264_MUX_ROUTES("I2S5")
+ TEGRA264_MUX_ROUTES("I2S6")
+ TEGRA264_MUX_ROUTES("I2S7")
+ TEGRA264_MUX_ROUTES("I2S8")
+ TEGRA264_MUX_ROUTES("DSPK1")
+ TEGRA264_MUX_ROUTES("SFC1")
+ TEGRA264_MUX_ROUTES("SFC2")
+ TEGRA264_MUX_ROUTES("SFC3")
+ TEGRA264_MUX_ROUTES("SFC4")
+ TEGRA264_MUX_ROUTES("MVC1")
+ TEGRA264_MUX_ROUTES("MVC2")
+ TEGRA264_MUX_ROUTES("AMX1 RX1")
+ TEGRA264_MUX_ROUTES("AMX1 RX2")
+ TEGRA264_MUX_ROUTES("AMX1 RX3")
+ TEGRA264_MUX_ROUTES("AMX1 RX4")
+ TEGRA264_MUX_ROUTES("AMX2 RX1")
+ TEGRA264_MUX_ROUTES("AMX2 RX2")
+ TEGRA264_MUX_ROUTES("AMX2 RX3")
+ TEGRA264_MUX_ROUTES("AMX2 RX4")
+ TEGRA264_MUX_ROUTES("AMX3 RX1")
+ TEGRA264_MUX_ROUTES("AMX3 RX2")
+ TEGRA264_MUX_ROUTES("AMX3 RX3")
+ TEGRA264_MUX_ROUTES("AMX3 RX4")
+ TEGRA264_MUX_ROUTES("AMX4 RX1")
+ TEGRA264_MUX_ROUTES("AMX4 RX2")
+ TEGRA264_MUX_ROUTES("AMX4 RX3")
+ TEGRA264_MUX_ROUTES("AMX4 RX4")
+ TEGRA264_MUX_ROUTES("AMX5 RX1")
+ TEGRA264_MUX_ROUTES("AMX5 RX2")
+ TEGRA264_MUX_ROUTES("AMX5 RX3")
+ TEGRA264_MUX_ROUTES("AMX5 RX4")
+ TEGRA264_MUX_ROUTES("AMX6 RX1")
+ TEGRA264_MUX_ROUTES("AMX6 RX2")
+ TEGRA264_MUX_ROUTES("AMX6 RX3")
+ TEGRA264_MUX_ROUTES("AMX6 RX4")
+ TEGRA264_MUX_ROUTES("ADX1")
+ TEGRA264_MUX_ROUTES("ADX2")
+ TEGRA264_MUX_ROUTES("ADX3")
+ TEGRA264_MUX_ROUTES("ADX4")
+ TEGRA264_MUX_ROUTES("ADX5")
+ TEGRA264_MUX_ROUTES("ADX6")
+ TEGRA264_MUX_ROUTES("MIXER1 RX1")
+ TEGRA264_MUX_ROUTES("MIXER1 RX2")
+ TEGRA264_MUX_ROUTES("MIXER1 RX3")
+ TEGRA264_MUX_ROUTES("MIXER1 RX4")
+ TEGRA264_MUX_ROUTES("MIXER1 RX5")
+ TEGRA264_MUX_ROUTES("MIXER1 RX6")
+ TEGRA264_MUX_ROUTES("MIXER1 RX7")
+ TEGRA264_MUX_ROUTES("MIXER1 RX8")
+ TEGRA264_MUX_ROUTES("MIXER1 RX9")
+ TEGRA264_MUX_ROUTES("MIXER1 RX10")
+ TEGRA264_MUX_ROUTES("ASRC1 RX1")
+ TEGRA264_MUX_ROUTES("ASRC1 RX2")
+ TEGRA264_MUX_ROUTES("ASRC1 RX3")
+ TEGRA264_MUX_ROUTES("ASRC1 RX4")
+ TEGRA264_MUX_ROUTES("ASRC1 RX5")
+ TEGRA264_MUX_ROUTES("ASRC1 RX6")
+ TEGRA264_MUX_ROUTES("ASRC1 RX7")
+ TEGRA264_MUX_ROUTES("OPE1")
+};
+
static const struct snd_soc_component_driver tegra210_ahub_component = {
.dapm_widgets = tegra210_ahub_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_ahub_widgets),
@@ -1259,6 +2042,36 @@ static const struct snd_soc_component_driver tegra234_ahub_component = {
.num_dapm_routes = ARRAY_SIZE(tegra186_ahub_routes),
};
+static const struct snd_soc_component_driver tegra264_ahub_component = {
+ .dapm_widgets = tegra264_ahub_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra264_ahub_widgets),
+ .dapm_routes = tegra264_ahub_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra264_ahub_routes),
+};
+
+static bool tegra264_ahub_wr_reg(struct device *dev, unsigned int reg)
+{
+ int part;
+
+ for (part = 0; part < TEGRA264_XBAR_UPDATE_MAX_REG; part++) {
+ switch (reg & ~(part << 12)) {
+ case TEGRA264_AXBAR_ADMAIF_RX1 ... TEGRA264_AXBAR_SFC4_RX1:
+ case TEGRA264_AXBAR_MIXER1_RX1 ... TEGRA264_AXBAR_MIXER1_RX10:
+ case TEGRA264_AXBAR_DSPK1_RX1:
+ case TEGRA264_AXBAR_OPE1_RX1:
+ case TEGRA264_AXBAR_MVC1_RX1 ... TEGRA264_AXBAR_MVC2_RX1:
+ case TEGRA264_AXBAR_AMX1_RX1 ... TEGRA264_AXBAR_AMX3_RX4:
+ case TEGRA264_AXBAR_ADX1_RX1 ... TEGRA264_AXBAR_ASRC1_RX7:
+ case TEGRA264_AXBAR_ADMAIF_RX21 ... TEGRA264_AXBAR_ADX6_RX1:
+ return true;
+ default:
+ break;
+ }
+ }
+
+ return false;
+}
+
static const struct regmap_config tegra210_ahub_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
@@ -1275,6 +2088,15 @@ static const struct regmap_config tegra186_ahub_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static const struct regmap_config tegra264_ahub_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .writeable_reg = tegra264_ahub_wr_reg,
+ .max_register = TEGRA264_MAX_REGISTER_ADDR,
+ .cache_type = REGCACHE_FLAT,
+};
+
static const struct tegra_ahub_soc_data soc_data_tegra210 = {
.cmpnt_drv = &tegra210_ahub_component,
.dai_drv = tegra210_ahub_dais,
@@ -1285,6 +2107,7 @@ static const struct tegra_ahub_soc_data soc_data_tegra210 = {
.mask[2] = TEGRA210_XBAR_REG_MASK_2,
.mask[3] = TEGRA210_XBAR_REG_MASK_3,
.reg_count = TEGRA210_XBAR_UPDATE_MAX_REG,
+ .xbar_part_size = TEGRA210_XBAR_PART1_RX,
};
static const struct tegra_ahub_soc_data soc_data_tegra186 = {
@@ -1297,6 +2120,7 @@ static const struct tegra_ahub_soc_data soc_data_tegra186 = {
.mask[2] = TEGRA186_XBAR_REG_MASK_2,
.mask[3] = TEGRA186_XBAR_REG_MASK_3,
.reg_count = TEGRA186_XBAR_UPDATE_MAX_REG,
+ .xbar_part_size = TEGRA210_XBAR_PART1_RX,
};
static const struct tegra_ahub_soc_data soc_data_tegra234 = {
@@ -1309,12 +2133,28 @@ static const struct tegra_ahub_soc_data soc_data_tegra234 = {
.mask[2] = TEGRA186_XBAR_REG_MASK_2,
.mask[3] = TEGRA186_XBAR_REG_MASK_3,
.reg_count = TEGRA186_XBAR_UPDATE_MAX_REG,
+ .xbar_part_size = TEGRA210_XBAR_PART1_RX,
+};
+
+static const struct tegra_ahub_soc_data soc_data_tegra264 = {
+ .cmpnt_drv = &tegra264_ahub_component,
+ .dai_drv = tegra264_ahub_dais,
+ .num_dais = ARRAY_SIZE(tegra264_ahub_dais),
+ .regmap_config = &tegra264_ahub_regmap_config,
+ .mask[0] = TEGRA264_XBAR_REG_MASK_0,
+ .mask[1] = TEGRA264_XBAR_REG_MASK_1,
+ .mask[2] = TEGRA264_XBAR_REG_MASK_2,
+ .mask[3] = TEGRA264_XBAR_REG_MASK_3,
+ .mask[4] = TEGRA264_XBAR_REG_MASK_4,
+ .reg_count = TEGRA264_XBAR_UPDATE_MAX_REG,
+ .xbar_part_size = TEGRA264_XBAR_PART1_RX,
};
static const struct of_device_id tegra_ahub_of_match[] = {
{ .compatible = "nvidia,tegra210-ahub", .data = &soc_data_tegra210 },
{ .compatible = "nvidia,tegra186-ahub", .data = &soc_data_tegra186 },
{ .compatible = "nvidia,tegra234-ahub", .data = &soc_data_tegra234 },
+ { .compatible = "nvidia,tegra264-ahub", .data = &soc_data_tegra264 },
{},
};
MODULE_DEVICE_TABLE(of, tegra_ahub_of_match);
@@ -1359,6 +2199,8 @@ static int tegra_ahub_probe(struct platform_device *pdev)
return -ENOMEM;
ahub->soc_data = of_device_get_match_data(&pdev->dev);
+ if (!ahub->soc_data)
+ return -ENODEV;
platform_set_drvdata(pdev, ahub);
diff --git a/sound/soc/tegra/tegra210_ahub.h b/sound/soc/tegra/tegra210_ahub.h
index 2728db4d24f2..f355b2cfd19b 100644
--- a/sound/soc/tegra/tegra210_ahub.h
+++ b/sound/soc/tegra/tegra210_ahub.h
@@ -2,7 +2,7 @@
/*
* tegra210_ahub.h - TEGRA210 AHUB
*
- * Copyright (c) 2020-2022, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2020-2025, NVIDIA CORPORATION. All rights reserved.
*
*/
@@ -28,7 +28,39 @@
#define TEGRA186_XBAR_REG_MASK_3 0x3f0f00ff
#define TEGRA186_XBAR_UPDATE_MAX_REG 4
-#define TEGRA_XBAR_UPDATE_MAX_REG (TEGRA186_XBAR_UPDATE_MAX_REG)
+/* Tegra264 specific */
+#define TEGRA264_XBAR_PART1_RX 0x1000
+#define TEGRA264_XBAR_PART2_RX 0x2000
+#define TEGRA264_XBAR_PART3_RX 0x3000
+#define TEGRA264_XBAR_PART4_RX 0x4000
+#define TEGRA264_XBAR_PART0_ADX6_RX1 0x224
+#define TEGRA264_XBAR_AUDIO_RX_COUNT ((TEGRA264_XBAR_PART0_ADX6_RX1 / 4) + 1)
+#define TEGRA264_XBAR_REG_MASK_0 0xfffffff
+#define TEGRA264_XBAR_REG_MASK_1 0x3f013f1f
+#define TEGRA264_XBAR_REG_MASK_2 0xff3c0301
+#define TEGRA264_XBAR_REG_MASK_3 0x3f00ffff
+#define TEGRA264_XBAR_REG_MASK_4 0x7fff9f
+#define TEGRA264_XBAR_UPDATE_MAX_REG 5
+
+#define TEGRA264_AXBAR_ADMAIF_RX1 0x0
+#define TEGRA264_AXBAR_SFC4_RX1 0x6c
+#define TEGRA264_AXBAR_MIXER1_RX1 0x80
+#define TEGRA264_AXBAR_MIXER1_RX10 0xa4
+#define TEGRA264_AXBAR_DSPK1_RX1 0xc0
+#define TEGRA264_AXBAR_OPE1_RX1 0x100
+#define TEGRA264_AXBAR_MVC1_RX1 0x110
+#define TEGRA264_AXBAR_MVC2_RX1 0x114
+#define TEGRA264_AXBAR_AMX1_RX1 0x120
+#define TEGRA264_AXBAR_AMX3_RX4 0x14c
+#define TEGRA264_AXBAR_ADX1_RX1 0x160
+#define TEGRA264_AXBAR_ASRC1_RX7 0x1a8
+#define TEGRA264_AXBAR_ADMAIF_RX21 0x1d0
+#define TEGRA264_AXBAR_ADX6_RX1 0x224
+
+#define TEGRA_XBAR_UPDATE_MAX_REG (TEGRA264_XBAR_UPDATE_MAX_REG)
+
+#define TEGRA264_MAX_REGISTER_ADDR (TEGRA264_XBAR_PART4_RX + \
+ (TEGRA210_XBAR_RX_STRIDE * (TEGRA264_XBAR_AUDIO_RX_COUNT - 1)))
#define TEGRA186_MAX_REGISTER_ADDR (TEGRA186_XBAR_PART3_RX + \
(TEGRA210_XBAR_RX_STRIDE * (TEGRA186_XBAR_AUDIO_RX_COUNT - 1)))
@@ -76,6 +108,15 @@
#define MUX_ENUM_CTRL_DECL_234(ename, id) MUX_ENUM_CTRL_DECL_186(ename, id)
+#define MUX_ENUM_CTRL_DECL_264(ename, id) \
+ SOC_VALUE_ENUM_WIDE_DECL(ename##_enum, MUX_REG(id), 0, \
+ tegra264_ahub_mux_texts, \
+ tegra264_ahub_mux_values); \
+ static const struct snd_kcontrol_new ename##_control = \
+ SOC_DAPM_ENUM_EXT("Route", ename##_enum, \
+ tegra_ahub_get_value_enum, \
+ tegra_ahub_put_value_enum)
+
#define WIDGETS(sname, ename) \
SND_SOC_DAPM_AIF_IN(sname " XBAR-RX", NULL, 0, SND_SOC_NOPM, 0, 0), \
SND_SOC_DAPM_AIF_OUT(sname " XBAR-TX", NULL, 0, SND_SOC_NOPM, 0, 0), \
@@ -92,7 +133,7 @@
.playback = { \
.stream_name = #sname " XBAR-Playback", \
.channels_min = 1, \
- .channels_max = 16, \
+ .channels_max = 32, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
@@ -102,7 +143,7 @@
.capture = { \
.stream_name = #sname " XBAR-Capture", \
.channels_min = 1, \
- .channels_max = 16, \
+ .channels_max = 32, \
.rates = SNDRV_PCM_RATE_8000_192000, \
.formats = SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
@@ -115,9 +156,10 @@ struct tegra_ahub_soc_data {
const struct regmap_config *regmap_config;
const struct snd_soc_component_driver *cmpnt_drv;
struct snd_soc_dai_driver *dai_drv;
- unsigned int mask[4];
+ unsigned int mask[TEGRA_XBAR_UPDATE_MAX_REG];
unsigned int reg_count;
unsigned int num_dais;
+ unsigned int xbar_part_size;
};
struct tegra_ahub {
diff --git a/sound/soc/tegra/tegra210_amx.c b/sound/soc/tegra/tegra210_amx.c
index 1981b94009cf..7f558c40e097 100644
--- a/sound/soc/tegra/tegra210_amx.c
+++ b/sound/soc/tegra/tegra210_amx.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-// SPDX-FileCopyrightText: Copyright (c) 2021-2024 NVIDIA CORPORATION & AFFILIATES.
+// SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION & AFFILIATES.
// All rights reserved.
//
// tegra210_amx.c - Tegra210 AMX driver
@@ -46,21 +46,35 @@ static const struct reg_default tegra210_amx_reg_defaults[] = {
{ TEGRA210_AMX_CFG_RAM_CTRL, 0x00004000},
};
+static const struct reg_default tegra264_amx_reg_defaults[] = {
+ { TEGRA210_AMX_RX_INT_MASK, 0x0000000f},
+ { TEGRA210_AMX_RX1_CIF_CTRL, 0x00003800},
+ { TEGRA210_AMX_RX2_CIF_CTRL, 0x00003800},
+ { TEGRA210_AMX_RX3_CIF_CTRL, 0x00003800},
+ { TEGRA210_AMX_RX4_CIF_CTRL, 0x00003800},
+ { TEGRA210_AMX_TX_INT_MASK, 0x00000001},
+ { TEGRA210_AMX_TX_CIF_CTRL, 0x00003800},
+ { TEGRA210_AMX_CG, 0x1},
+ { TEGRA264_AMX_CFG_RAM_CTRL, 0x00004000},
+};
+
static void tegra210_amx_write_map_ram(struct tegra210_amx *amx)
{
int i;
- regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL,
+ regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_CTRL + amx->soc_data->reg_offset,
TEGRA210_AMX_CFG_RAM_CTRL_SEQ_ACCESS_EN |
TEGRA210_AMX_CFG_RAM_CTRL_ADDR_INIT_EN |
TEGRA210_AMX_CFG_RAM_CTRL_RW_WRITE);
- for (i = 0; i < TEGRA210_AMX_RAM_DEPTH; i++)
- regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA,
+ for (i = 0; i < amx->soc_data->ram_depth; i++)
+ regmap_write(amx->regmap, TEGRA210_AMX_CFG_RAM_DATA + amx->soc_data->reg_offset,
amx->map[i]);
- regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN0, amx->byte_mask[0]);
- regmap_write(amx->regmap, TEGRA210_AMX_OUT_BYTE_EN1, amx->byte_mask[1]);
+ for (i = 0; i < amx->soc_data->byte_mask_size; i++)
+ regmap_write(amx->regmap,
+ TEGRA210_AMX_OUT_BYTE_EN0 + (i * TEGRA210_AMX_AUDIOCIF_CH_STRIDE),
+ amx->byte_mask[i]);
}
static int tegra210_amx_startup(struct snd_pcm_substream *substream,
@@ -157,7 +171,10 @@ static int tegra210_amx_set_audio_cif(struct snd_soc_dai *dai,
cif_conf.audio_bits = audio_bits;
cif_conf.client_bits = audio_bits;
- tegra_set_cif(amx->regmap, reg, &cif_conf);
+ if (amx->soc_data->max_ch == TEGRA264_AMX_MAX_CHANNEL)
+ tegra264_set_cif(amx->regmap, reg, &cif_conf);
+ else
+ tegra_set_cif(amx->regmap, reg, &cif_conf);
return 0;
}
@@ -170,9 +187,10 @@ static int tegra210_amx_in_hw_params(struct snd_pcm_substream *substream,
if (amx->soc_data->auto_disable) {
regmap_write(amx->regmap,
- AMX_CH_REG(dai->id, TEGRA194_AMX_RX1_FRAME_PERIOD),
+ AMX_CH_REG(dai->id, TEGRA194_AMX_RX1_FRAME_PERIOD +
+ amx->soc_data->reg_offset),
TEGRA194_MAX_FRAME_IDLE_COUNT);
- regmap_write(amx->regmap, TEGRA210_AMX_CYA, 1);
+ regmap_write(amx->regmap, TEGRA210_AMX_CYA + amx->soc_data->reg_offset, 1);
}
return tegra210_amx_set_audio_cif(dai, params,
@@ -194,14 +212,11 @@ static int tegra210_amx_get_byte_map(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
- unsigned char *bytes_map = (unsigned char *)&amx->map;
+ unsigned char *bytes_map = (unsigned char *)amx->map;
int reg = mc->reg;
int enabled;
- if (reg > 31)
- enabled = amx->byte_mask[1] & (1 << (reg - 32));
- else
- enabled = amx->byte_mask[0] & (1 << reg);
+ enabled = amx->byte_mask[reg / 32] & (1 << (reg % 32));
/*
* TODO: Simplify this logic to just return from bytes_map[]
@@ -228,7 +243,7 @@ static int tegra210_amx_put_byte_map(struct snd_kcontrol *kcontrol,
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
struct tegra210_amx *amx = snd_soc_component_get_drvdata(cmpnt);
- unsigned char *bytes_map = (unsigned char *)&amx->map;
+ unsigned char *bytes_map = (unsigned char *)amx->map;
int reg = mc->reg;
int value = ucontrol->value.integer.value[0];
unsigned int mask_val = amx->byte_mask[reg / 32];
@@ -418,7 +433,90 @@ static struct snd_kcontrol_new tegra210_amx_controls[] = {
TEGRA210_AMX_BYTE_MAP_CTRL(63),
};
+static struct snd_kcontrol_new tegra264_amx_controls[] = {
+ TEGRA210_AMX_BYTE_MAP_CTRL(64),
+ TEGRA210_AMX_BYTE_MAP_CTRL(65),
+ TEGRA210_AMX_BYTE_MAP_CTRL(66),
+ TEGRA210_AMX_BYTE_MAP_CTRL(67),
+ TEGRA210_AMX_BYTE_MAP_CTRL(68),
+ TEGRA210_AMX_BYTE_MAP_CTRL(69),
+ TEGRA210_AMX_BYTE_MAP_CTRL(70),
+ TEGRA210_AMX_BYTE_MAP_CTRL(71),
+ TEGRA210_AMX_BYTE_MAP_CTRL(72),
+ TEGRA210_AMX_BYTE_MAP_CTRL(73),
+ TEGRA210_AMX_BYTE_MAP_CTRL(74),
+ TEGRA210_AMX_BYTE_MAP_CTRL(75),
+ TEGRA210_AMX_BYTE_MAP_CTRL(76),
+ TEGRA210_AMX_BYTE_MAP_CTRL(77),
+ TEGRA210_AMX_BYTE_MAP_CTRL(78),
+ TEGRA210_AMX_BYTE_MAP_CTRL(79),
+ TEGRA210_AMX_BYTE_MAP_CTRL(80),
+ TEGRA210_AMX_BYTE_MAP_CTRL(81),
+ TEGRA210_AMX_BYTE_MAP_CTRL(82),
+ TEGRA210_AMX_BYTE_MAP_CTRL(83),
+ TEGRA210_AMX_BYTE_MAP_CTRL(84),
+ TEGRA210_AMX_BYTE_MAP_CTRL(85),
+ TEGRA210_AMX_BYTE_MAP_CTRL(86),
+ TEGRA210_AMX_BYTE_MAP_CTRL(87),
+ TEGRA210_AMX_BYTE_MAP_CTRL(88),
+ TEGRA210_AMX_BYTE_MAP_CTRL(89),
+ TEGRA210_AMX_BYTE_MAP_CTRL(90),
+ TEGRA210_AMX_BYTE_MAP_CTRL(91),
+ TEGRA210_AMX_BYTE_MAP_CTRL(92),
+ TEGRA210_AMX_BYTE_MAP_CTRL(93),
+ TEGRA210_AMX_BYTE_MAP_CTRL(94),
+ TEGRA210_AMX_BYTE_MAP_CTRL(95),
+ TEGRA210_AMX_BYTE_MAP_CTRL(96),
+ TEGRA210_AMX_BYTE_MAP_CTRL(97),
+ TEGRA210_AMX_BYTE_MAP_CTRL(98),
+ TEGRA210_AMX_BYTE_MAP_CTRL(99),
+ TEGRA210_AMX_BYTE_MAP_CTRL(100),
+ TEGRA210_AMX_BYTE_MAP_CTRL(101),
+ TEGRA210_AMX_BYTE_MAP_CTRL(102),
+ TEGRA210_AMX_BYTE_MAP_CTRL(103),
+ TEGRA210_AMX_BYTE_MAP_CTRL(104),
+ TEGRA210_AMX_BYTE_MAP_CTRL(105),
+ TEGRA210_AMX_BYTE_MAP_CTRL(106),
+ TEGRA210_AMX_BYTE_MAP_CTRL(107),
+ TEGRA210_AMX_BYTE_MAP_CTRL(108),
+ TEGRA210_AMX_BYTE_MAP_CTRL(109),
+ TEGRA210_AMX_BYTE_MAP_CTRL(110),
+ TEGRA210_AMX_BYTE_MAP_CTRL(111),
+ TEGRA210_AMX_BYTE_MAP_CTRL(112),
+ TEGRA210_AMX_BYTE_MAP_CTRL(113),
+ TEGRA210_AMX_BYTE_MAP_CTRL(114),
+ TEGRA210_AMX_BYTE_MAP_CTRL(115),
+ TEGRA210_AMX_BYTE_MAP_CTRL(116),
+ TEGRA210_AMX_BYTE_MAP_CTRL(117),
+ TEGRA210_AMX_BYTE_MAP_CTRL(118),
+ TEGRA210_AMX_BYTE_MAP_CTRL(119),
+ TEGRA210_AMX_BYTE_MAP_CTRL(120),
+ TEGRA210_AMX_BYTE_MAP_CTRL(121),
+ TEGRA210_AMX_BYTE_MAP_CTRL(122),
+ TEGRA210_AMX_BYTE_MAP_CTRL(123),
+ TEGRA210_AMX_BYTE_MAP_CTRL(124),
+ TEGRA210_AMX_BYTE_MAP_CTRL(125),
+ TEGRA210_AMX_BYTE_MAP_CTRL(126),
+ TEGRA210_AMX_BYTE_MAP_CTRL(127),
+};
+
+static int tegra210_amx_component_probe(struct snd_soc_component *component)
+{
+ struct tegra210_amx *amx = snd_soc_component_get_drvdata(component);
+ int err = 0;
+
+ if (amx->soc_data->num_controls) {
+ err = snd_soc_add_component_controls(component, amx->soc_data->controls,
+ amx->soc_data->num_controls);
+ if (err)
+ dev_err(component->dev, "can't add AMX controls, err: %d\n", err);
+ }
+
+ return err;
+}
+
static const struct snd_soc_component_driver tegra210_amx_cmpnt = {
+ .probe = tegra210_amx_component_probe,
.dapm_widgets = tegra210_amx_widgets,
.num_dapm_widgets = ARRAY_SIZE(tegra210_amx_widgets),
.dapm_routes = tegra210_amx_routes,
@@ -450,6 +548,22 @@ static bool tegra194_amx_wr_reg(struct device *dev, unsigned int reg)
}
}
+static bool tegra264_amx_wr_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_RX_INT_MASK ... TEGRA210_AMX_RX4_CIF_CTRL:
+ case TEGRA210_AMX_TX_INT_MASK ... TEGRA210_AMX_TX_CIF_CTRL:
+ case TEGRA210_AMX_ENABLE ... TEGRA210_AMX_CG:
+ case TEGRA210_AMX_CTRL ... TEGRA264_AMX_STREAMS_AUTO_DISABLE:
+ case TEGRA264_AMX_CFG_RAM_CTRL ... TEGRA264_AMX_CFG_RAM_DATA:
+ case TEGRA264_AMX_RX1_FRAME_PERIOD ... TEGRA264_AMX_RX4_FRAME_PERIOD:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool tegra210_amx_rd_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -470,6 +584,21 @@ static bool tegra194_amx_rd_reg(struct device *dev, unsigned int reg)
}
}
+static bool tegra264_amx_rd_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_RX_STATUS ... TEGRA210_AMX_RX4_CIF_CTRL:
+ case TEGRA210_AMX_TX_STATUS ... TEGRA210_AMX_TX_CIF_CTRL:
+ case TEGRA210_AMX_ENABLE ... TEGRA210_AMX_INT_STATUS:
+ case TEGRA210_AMX_CTRL ... TEGRA264_AMX_CFG_RAM_DATA:
+ case TEGRA264_AMX_RX1_FRAME_PERIOD ... TEGRA264_AMX_RX4_FRAME_PERIOD:
+ return true;
+ default:
+ return false;
+ }
+}
+
static bool tegra210_amx_volatile_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -492,6 +621,29 @@ static bool tegra210_amx_volatile_reg(struct device *dev, unsigned int reg)
return false;
}
+static bool tegra264_amx_volatile_reg(struct device *dev,
+ unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_AMX_RX_STATUS:
+ case TEGRA210_AMX_RX_INT_STATUS:
+ case TEGRA210_AMX_RX_INT_SET:
+ case TEGRA210_AMX_TX_STATUS:
+ case TEGRA210_AMX_TX_INT_STATUS:
+ case TEGRA210_AMX_TX_INT_SET:
+ case TEGRA210_AMX_SOFT_RESET:
+ case TEGRA210_AMX_STATUS:
+ case TEGRA210_AMX_INT_STATUS:
+ case TEGRA264_AMX_CFG_RAM_CTRL:
+ case TEGRA264_AMX_CFG_RAM_DATA:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
static const struct regmap_config tegra210_amx_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
@@ -518,18 +670,51 @@ static const struct regmap_config tegra194_amx_regmap_config = {
.cache_type = REGCACHE_FLAT,
};
+static const struct regmap_config tegra264_amx_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA264_AMX_RX4_LAST_FRAME_PERIOD,
+ .writeable_reg = tegra264_amx_wr_reg,
+ .readable_reg = tegra264_amx_rd_reg,
+ .volatile_reg = tegra264_amx_volatile_reg,
+ .reg_defaults = tegra264_amx_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra264_amx_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
static const struct tegra210_amx_soc_data soc_data_tegra210 = {
.regmap_conf = &tegra210_amx_regmap_config,
+ .max_ch = TEGRA210_AMX_MAX_CHANNEL,
+ .ram_depth = TEGRA210_AMX_RAM_DEPTH,
+ .byte_mask_size = TEGRA210_AMX_BYTE_MASK_COUNT,
+ .reg_offset = TEGRA210_AMX_AUTO_DISABLE_OFFSET,
};
static const struct tegra210_amx_soc_data soc_data_tegra194 = {
.regmap_conf = &tegra194_amx_regmap_config,
.auto_disable = true,
+ .max_ch = TEGRA210_AMX_MAX_CHANNEL,
+ .ram_depth = TEGRA210_AMX_RAM_DEPTH,
+ .byte_mask_size = TEGRA210_AMX_BYTE_MASK_COUNT,
+ .reg_offset = TEGRA210_AMX_AUTO_DISABLE_OFFSET,
+};
+
+static const struct tegra210_amx_soc_data soc_data_tegra264 = {
+ .regmap_conf = &tegra264_amx_regmap_config,
+ .auto_disable = true,
+ .max_ch = TEGRA264_AMX_MAX_CHANNEL,
+ .ram_depth = TEGRA264_AMX_RAM_DEPTH,
+ .byte_mask_size = TEGRA264_AMX_BYTE_MASK_COUNT,
+ .reg_offset = TEGRA264_AMX_AUTO_DISABLE_OFFSET,
+ .controls = tegra264_amx_controls,
+ .num_controls = ARRAY_SIZE(tegra264_amx_controls),
};
static const struct of_device_id tegra210_amx_of_match[] = {
{ .compatible = "nvidia,tegra210-amx", .data = &soc_data_tegra210 },
{ .compatible = "nvidia,tegra194-amx", .data = &soc_data_tegra194 },
+ { .compatible = "nvidia,tegra264-amx", .data = &soc_data_tegra264 },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_amx_of_match);
@@ -562,6 +747,20 @@ static int tegra210_amx_platform_probe(struct platform_device *pdev)
regcache_cache_only(amx->regmap, true);
+ amx->map = devm_kzalloc(dev, amx->soc_data->ram_depth * sizeof(*amx->map),
+ GFP_KERNEL);
+ if (!amx->map)
+ return -ENOMEM;
+
+ amx->byte_mask = devm_kzalloc(dev,
+ amx->soc_data->byte_mask_size * sizeof(*amx->byte_mask),
+ GFP_KERNEL);
+ if (!amx->byte_mask)
+ return -ENOMEM;
+
+ tegra210_amx_dais[TEGRA_AMX_OUT_DAI_ID].capture.channels_max =
+ amx->soc_data->max_ch;
+
err = devm_snd_soc_register_component(dev, &tegra210_amx_cmpnt,
tegra210_amx_dais,
ARRAY_SIZE(tegra210_amx_dais));
diff --git a/sound/soc/tegra/tegra210_amx.h b/sound/soc/tegra/tegra210_amx.h
index e277741e4258..50a237b197ba 100644
--- a/sound/soc/tegra/tegra210_amx.h
+++ b/sound/soc/tegra/tegra210_amx.h
@@ -1,8 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * tegra210_amx.h - Definitions for Tegra210 AMX driver
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2021-2025 NVIDIA CORPORATION. All rights reserved.
*
- * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ * tegra210_amx.h - Definitions for Tegra210 AMX driver
*
*/
@@ -32,7 +31,6 @@
#define TEGRA210_AMX_INT_STATUS 0x90
#define TEGRA210_AMX_CTRL 0xa4
#define TEGRA210_AMX_OUT_BYTE_EN0 0xa8
-#define TEGRA210_AMX_OUT_BYTE_EN1 0xac
#define TEGRA210_AMX_CYA 0xb0
#define TEGRA210_AMX_CFG_RAM_CTRL 0xb8
#define TEGRA210_AMX_CFG_RAM_DATA 0xbc
@@ -41,6 +39,13 @@
#define TEGRA194_AMX_RX4_FRAME_PERIOD 0xcc
#define TEGRA194_AMX_RX4_LAST_FRAME_PERIOD 0xdc
+#define TEGRA264_AMX_STREAMS_AUTO_DISABLE 0xb8
+#define TEGRA264_AMX_CFG_RAM_CTRL 0xc0
+#define TEGRA264_AMX_CFG_RAM_DATA 0xc4
+#define TEGRA264_AMX_RX1_FRAME_PERIOD 0xc8
+#define TEGRA264_AMX_RX4_FRAME_PERIOD 0xd4
+#define TEGRA264_AMX_RX4_LAST_FRAME_PERIOD 0xe4
+
/* Fields in TEGRA210_AMX_ENABLE */
#define TEGRA210_AMX_ENABLE_SHIFT 0
@@ -72,6 +77,15 @@
#define TEGRA210_AMX_MAP_STREAM_NUM_SHIFT 6
#define TEGRA210_AMX_MAP_WORD_NUM_SHIFT 2
#define TEGRA210_AMX_MAP_BYTE_NUM_SHIFT 0
+#define TEGRA210_AMX_BYTE_MASK_COUNT 2
+#define TEGRA210_AMX_MAX_CHANNEL 16
+#define TEGRA210_AMX_AUTO_DISABLE_OFFSET 0
+
+#define TEGRA264_AMX_RAM_DEPTH 32
+#define TEGRA264_AMX_BYTE_MASK_COUNT 4
+#define TEGRA264_AMX_MAX_CHANNEL 32
+#define TEGRA264_AMX_AUTO_DISABLE_OFFSET 8
+#define TEGRA_AMX_OUT_DAI_ID 4
enum {
TEGRA210_AMX_WAIT_ON_ALL,
@@ -81,13 +95,19 @@ enum {
struct tegra210_amx_soc_data {
const struct regmap_config *regmap_conf;
bool auto_disable;
+ const struct snd_kcontrol_new *controls;
+ unsigned int num_controls;
+ unsigned int max_ch;
+ unsigned int ram_depth;
+ unsigned int byte_mask_size;
+ unsigned int reg_offset;
};
struct tegra210_amx {
const struct tegra210_amx_soc_data *soc_data;
- unsigned int map[TEGRA210_AMX_RAM_DEPTH];
+ unsigned int *map;
+ unsigned int *byte_mask;
struct regmap *regmap;
- unsigned int byte_mask[2];
};
#endif
diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
index 766cddebd5f6..100277c39001 100644
--- a/sound/soc/tegra/tegra210_i2s.c
+++ b/sound/soc/tegra/tegra210_i2s.c
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only
-// SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES.
+// SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
// All rights reserved.
//
// tegra210_i2s.c - Tegra210 I2S driver
@@ -36,14 +36,28 @@ static const struct reg_default tegra210_i2s_reg_defaults[] = {
{ TEGRA210_I2S_CYA, 0x1 },
};
-static void tegra210_i2s_set_slot_ctrl(struct regmap *regmap,
+static const struct reg_default tegra264_i2s_reg_defaults[] = {
+ { TEGRA210_I2S_RX_INT_MASK, 0x00000003 },
+ { TEGRA210_I2S_RX_CIF_CTRL, 0x00003f00 },
+ { TEGRA264_I2S_TX_INT_MASK, 0x00000003 },
+ { TEGRA264_I2S_TX_CIF_CTRL, 0x00003f00 },
+ { TEGRA264_I2S_CG, 0x1 },
+ { TEGRA264_I2S_TIMING, 0x0000001f },
+ { TEGRA264_I2S_ENABLE, 0x1 },
+ { TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE, 0x1 },
+ { TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE, 0x1 },
+};
+
+static void tegra210_i2s_set_slot_ctrl(struct tegra210_i2s *i2s,
unsigned int total_slots,
unsigned int tx_slot_mask,
unsigned int rx_slot_mask)
{
- regmap_write(regmap, TEGRA210_I2S_SLOT_CTRL, total_slots - 1);
- regmap_write(regmap, TEGRA210_I2S_TX_SLOT_CTRL, tx_slot_mask);
- regmap_write(regmap, TEGRA210_I2S_RX_SLOT_CTRL, rx_slot_mask);
+ regmap_write(i2s->regmap, TEGRA210_I2S_SLOT_CTRL + i2s->soc_data->i2s_ctrl_offset,
+ total_slots - 1);
+ regmap_write(i2s->regmap, TEGRA210_I2S_TX_SLOT_CTRL + i2s->soc_data->tx_offset,
+ tx_slot_mask);
+ regmap_write(i2s->regmap, TEGRA210_I2S_RX_SLOT_CTRL, rx_slot_mask);
}
static int tegra210_i2s_set_clock_rate(struct device *dev,
@@ -53,7 +67,7 @@ static int tegra210_i2s_set_clock_rate(struct device *dev,
unsigned int val;
int err;
- regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
+ regmap_read(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset, &val);
/* No need to set rates if I2S is being operated in slave */
if (!(val & I2S_CTRL_MASTER_EN))
@@ -100,15 +114,15 @@ static int tegra210_i2s_sw_reset(struct snd_soc_component *compnt,
cif_reg = TEGRA210_I2S_RX_CIF_CTRL;
stream_reg = TEGRA210_I2S_RX_CTRL;
} else {
- reset_reg = TEGRA210_I2S_TX_SOFT_RESET;
- cif_reg = TEGRA210_I2S_TX_CIF_CTRL;
- stream_reg = TEGRA210_I2S_TX_CTRL;
+ reset_reg = TEGRA210_I2S_TX_SOFT_RESET + i2s->soc_data->tx_offset;
+ cif_reg = TEGRA210_I2S_TX_CIF_CTRL + i2s->soc_data->tx_offset;
+ stream_reg = TEGRA210_I2S_TX_CTRL + i2s->soc_data->tx_offset;
}
/* Store CIF and I2S control values */
regmap_read(i2s->regmap, cif_reg, &cif_ctrl);
regmap_read(i2s->regmap, stream_reg, &stream_ctrl);
- regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &i2s_ctrl);
+ regmap_read(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset, &i2s_ctrl);
/* Reset to make sure the previous transactions are clean */
regmap_update_bits(i2s->regmap, reset_reg, reset_mask, reset_en);
@@ -125,7 +139,7 @@ static int tegra210_i2s_sw_reset(struct snd_soc_component *compnt,
/* Restore CIF and I2S control values */
regmap_write(i2s->regmap, cif_reg, cif_ctrl);
regmap_write(i2s->regmap, stream_reg, stream_ctrl);
- regmap_write(i2s->regmap, TEGRA210_I2S_CTRL, i2s_ctrl);
+ regmap_write(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset, i2s_ctrl);
return 0;
}
@@ -140,16 +154,13 @@ static int tegra210_i2s_init(struct snd_soc_dapm_widget *w,
int stream;
int err;
- switch (w->reg) {
- case TEGRA210_I2S_RX_ENABLE:
+ if (w->reg == TEGRA210_I2S_RX_ENABLE) {
stream = SNDRV_PCM_STREAM_PLAYBACK;
status_reg = TEGRA210_I2S_RX_STATUS;
- break;
- case TEGRA210_I2S_TX_ENABLE:
+ } else if (w->reg == (TEGRA210_I2S_TX_ENABLE + i2s->soc_data->tx_offset)) {
stream = SNDRV_PCM_STREAM_CAPTURE;
- status_reg = TEGRA210_I2S_TX_STATUS;
- break;
- default:
+ status_reg = TEGRA210_I2S_TX_STATUS + i2s->soc_data->tx_offset;
+ } else {
return -EINVAL;
}
@@ -199,7 +210,7 @@ static void tegra210_i2s_set_data_offset(struct tegra210_i2s *i2s,
unsigned int data_offset)
{
/* Capture path */
- regmap_update_bits(i2s->regmap, TEGRA210_I2S_TX_CTRL,
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_TX_CTRL + i2s->soc_data->tx_offset,
I2S_CTRL_DATA_OFFSET_MASK,
data_offset << I2S_DATA_SHIFT);
@@ -282,7 +293,8 @@ static int tegra210_i2s_set_fmt(struct snd_soc_dai *dai,
return -EINVAL;
}
- regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, mask, val);
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset,
+ mask, val);
i2s->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
@@ -296,10 +308,10 @@ static int tegra210_i2s_set_tdm_slot(struct snd_soc_dai *dai,
struct tegra210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
/* Copy the required tx and rx mask */
- i2s->tx_mask = (tx_mask > DEFAULT_I2S_SLOT_MASK) ?
- DEFAULT_I2S_SLOT_MASK : tx_mask;
- i2s->rx_mask = (rx_mask > DEFAULT_I2S_SLOT_MASK) ?
- DEFAULT_I2S_SLOT_MASK : rx_mask;
+ i2s->tx_mask = (tx_mask > i2s->soc_data->slot_mask) ?
+ i2s->soc_data->slot_mask : tx_mask;
+ i2s->rx_mask = (rx_mask > i2s->soc_data->slot_mask) ?
+ i2s->soc_data->slot_mask : rx_mask;
return 0;
}
@@ -327,8 +339,8 @@ static int tegra210_i2s_put_loopback(struct snd_kcontrol *kcontrol,
i2s->loopback = value;
- regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL, I2S_CTRL_LPBK_MASK,
- i2s->loopback << I2S_CTRL_LPBK_SHIFT);
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset,
+ I2S_CTRL_LPBK_MASK, i2s->loopback << I2S_CTRL_LPBK_SHIFT);
return 1;
}
@@ -364,9 +376,9 @@ static int tegra210_i2s_put_fsync_width(struct snd_kcontrol *kcontrol,
* cases mixer control is used to update custom values. A value
* of "N" here means, width is "N + 1" bit clock wide.
*/
- regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL,
- I2S_CTRL_FSYNC_WIDTH_MASK,
- i2s->fsync_width << I2S_FSYNC_WIDTH_SHIFT);
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset,
+ i2s->soc_data->fsync_width_mask,
+ i2s->fsync_width << i2s->soc_data->fsync_width_shift);
return 1;
}
@@ -562,7 +574,7 @@ static int tegra210_i2s_set_timing_params(struct device *dev,
return err;
}
- regmap_read(i2s->regmap, TEGRA210_I2S_CTRL, &val);
+ regmap_read(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset, &val);
/*
* For LRCK mode, channel bit count depends on number of bit clocks
@@ -578,7 +590,7 @@ static int tegra210_i2s_set_timing_params(struct device *dev,
case I2S_CTRL_FRAME_FMT_FSYNC_MODE:
bit_count = (bclk_rate / srate) - 1;
- tegra210_i2s_set_slot_ctrl(i2s->regmap, channels,
+ tegra210_i2s_set_slot_ctrl(i2s, channels,
i2s->tx_mask, i2s->rx_mask);
break;
default:
@@ -591,7 +603,7 @@ static int tegra210_i2s_set_timing_params(struct device *dev,
return -EINVAL;
}
- regmap_write(i2s->regmap, TEGRA210_I2S_TIMING,
+ regmap_write(i2s->regmap, TEGRA210_I2S_TIMING + i2s->soc_data->i2s_ctrl_offset,
bit_count << I2S_TIMING_CH_BIT_CNT_SHIFT);
return 0;
@@ -673,7 +685,7 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
}
/* Program sample size */
- regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL,
+ regmap_update_bits(i2s->regmap, TEGRA210_I2S_CTRL + i2s->soc_data->i2s_ctrl_offset,
I2S_CTRL_BIT_SIZE_MASK, val);
srate = params_rate(params);
@@ -697,13 +709,16 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
reg = TEGRA210_I2S_RX_CIF_CTRL;
} else {
- reg = TEGRA210_I2S_TX_CIF_CTRL;
+ reg = TEGRA210_I2S_TX_CIF_CTRL + i2s->soc_data->tx_offset;
}
cif_conf.mono_conv = i2s->mono_to_stereo[path];
cif_conf.stereo_conv = i2s->stereo_to_mono[path];
- tegra_set_cif(i2s->regmap, reg, &cif_conf);
+ if (i2s->soc_data->max_ch == TEGRA264_I2S_MAX_CHANNEL)
+ tegra264_set_cif(i2s->regmap, reg, &cif_conf);
+ else
+ tegra_set_cif(i2s->regmap, reg, &cif_conf);
return tegra210_i2s_set_timing_params(dev, sample_size, srate,
cif_conf.client_ch);
@@ -808,13 +823,20 @@ static const struct snd_kcontrol_new tegra210_i2s_controls[] = {
tegra210_i2s_put_bclk_ratio),
};
-static const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = {
- SND_SOC_DAPM_AIF_IN_E("RX", NULL, 0, TEGRA210_I2S_RX_ENABLE,
- 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU),
- SND_SOC_DAPM_AIF_OUT_E("TX", NULL, 0, TEGRA210_I2S_TX_ENABLE,
- 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU),
- SND_SOC_DAPM_MIC("MIC", NULL),
+#define TEGRA_I2S_WIDGETS(tx_enable_reg) \
+ SND_SOC_DAPM_AIF_IN_E("RX", NULL, 0, TEGRA210_I2S_RX_ENABLE, \
+ 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU), \
+ SND_SOC_DAPM_AIF_OUT_E("TX", NULL, 0, tx_enable_reg, \
+ 0, 0, tegra210_i2s_init, SND_SOC_DAPM_PRE_PMU), \
+ SND_SOC_DAPM_MIC("MIC", NULL), \
SND_SOC_DAPM_SPK("SPK", NULL),
+
+static const struct snd_soc_dapm_widget tegra210_i2s_widgets[] = {
+ TEGRA_I2S_WIDGETS(TEGRA210_I2S_TX_ENABLE)
+};
+
+static const struct snd_soc_dapm_widget tegra264_i2s_widgets[] = {
+ TEGRA_I2S_WIDGETS(TEGRA264_I2S_TX_ENABLE)
};
static const struct snd_soc_dapm_route tegra210_i2s_routes[] = {
@@ -841,6 +863,15 @@ static const struct snd_soc_component_driver tegra210_i2s_cmpnt = {
.num_controls = ARRAY_SIZE(tegra210_i2s_controls),
};
+static const struct snd_soc_component_driver tegra264_i2s_cmpnt = {
+ .dapm_widgets = tegra264_i2s_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra264_i2s_widgets),
+ .dapm_routes = tegra210_i2s_routes,
+ .num_dapm_routes = ARRAY_SIZE(tegra210_i2s_routes),
+ .controls = tegra210_i2s_controls,
+ .num_controls = ARRAY_SIZE(tegra210_i2s_controls),
+};
+
static bool tegra210_i2s_wr_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -895,7 +926,68 @@ static bool tegra210_i2s_volatile_reg(struct device *dev, unsigned int reg)
}
}
-static const struct regmap_config tegra210_i2s_regmap_config = {
+static bool tegra264_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_I2S_RX_ENABLE ... TEGRA210_I2S_RX_SOFT_RESET:
+ case TEGRA210_I2S_RX_INT_MASK ... TEGRA264_I2S_RX_CYA:
+ case TEGRA264_I2S_TX_ENABLE ... TEGRA264_I2S_TX_SOFT_RESET:
+ case TEGRA264_I2S_TX_INT_MASK ... TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE:
+ case TEGRA264_I2S_TX_FIFO_THRESHOLD ... TEGRA264_I2S_TX_CYA:
+ case TEGRA264_I2S_ENABLE ... TEGRA264_I2S_CG:
+ case TEGRA264_I2S_INT_SET ... TEGRA264_I2S_INT_MASK:
+ case TEGRA264_I2S_CTRL ... TEGRA264_I2S_CYA:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra264_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+ if (tegra264_i2s_wr_reg(dev, reg))
+ return true;
+
+ switch (reg) {
+ case TEGRA210_I2S_RX_STATUS:
+ case TEGRA210_I2S_RX_INT_STATUS:
+ case TEGRA264_I2S_RX_CIF_FIFO_STATUS:
+ case TEGRA264_I2S_TX_STATUS:
+ case TEGRA264_I2S_TX_INT_STATUS:
+ case TEGRA264_I2S_TX_FIFO_RD_DATA:
+ case TEGRA264_I2S_TX_CIF_FIFO_STATUS:
+ case TEGRA264_I2S_STATUS:
+ case TEGRA264_I2S_INT_STATUS:
+ case TEGRA264_I2S_PIO_MODE_ENABLE:
+ case TEGRA264_I2S_PAD_MACRO_STATUS:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static bool tegra264_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TEGRA210_I2S_RX_SOFT_RESET:
+ case TEGRA210_I2S_RX_STATUS:
+ case TEGRA210_I2S_RX_INT_STATUS:
+ case TEGRA264_I2S_RX_CIF_FIFO_STATUS:
+ case TEGRA264_I2S_TX_STATUS:
+ case TEGRA264_I2S_TX_INT_STATUS:
+ case TEGRA264_I2S_TX_FIFO_RD_DATA:
+ case TEGRA264_I2S_TX_CIF_FIFO_STATUS:
+ case TEGRA264_I2S_STATUS:
+ case TEGRA264_I2S_INT_STATUS:
+ case TEGRA264_I2S_TX_SOFT_RESET:
+ case TEGRA264_I2S_PAD_MACRO_STATUS:
+ return true;
+ default:
+ return false;
+ };
+}
+
+static const struct regmap_config tegra210_regmap_conf = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@@ -942,20 +1034,34 @@ static void tegra210_parse_client_convert(struct device *dev)
i2s->client_sample_format = simple_util_get_sample_fmt(&data);
}
+static const struct regmap_config tegra264_regmap_conf = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = TEGRA264_I2S_PAD_MACRO_STATUS,
+ .writeable_reg = tegra264_i2s_wr_reg,
+ .readable_reg = tegra264_i2s_rd_reg,
+ .volatile_reg = tegra264_i2s_volatile_reg,
+ .reg_defaults = tegra264_i2s_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tegra264_i2s_reg_defaults),
+ .cache_type = REGCACHE_FLAT,
+};
+
static int tegra210_i2s_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct tegra210_i2s *i2s;
void __iomem *regs;
- int err;
+ int err, id;
i2s = devm_kzalloc(dev, sizeof(*i2s), GFP_KERNEL);
if (!i2s)
return -ENOMEM;
+ i2s->soc_data = of_device_get_match_data(&pdev->dev);
i2s->rx_fifo_th = DEFAULT_I2S_RX_FIFO_THRESHOLD;
- i2s->tx_mask = DEFAULT_I2S_SLOT_MASK;
- i2s->rx_mask = DEFAULT_I2S_SLOT_MASK;
+ i2s->tx_mask = i2s->soc_data->slot_mask;
+ i2s->rx_mask = i2s->soc_data->slot_mask;
i2s->loopback = false;
i2s->client_sample_format = -EINVAL;
@@ -981,7 +1087,7 @@ static int tegra210_i2s_probe(struct platform_device *pdev)
return PTR_ERR(regs);
i2s->regmap = devm_regmap_init_mmio(dev, regs,
- &tegra210_i2s_regmap_config);
+ i2s->soc_data->regmap_conf);
if (IS_ERR(i2s->regmap)) {
dev_err(dev, "regmap init failed\n");
return PTR_ERR(i2s->regmap);
@@ -991,7 +1097,13 @@ static int tegra210_i2s_probe(struct platform_device *pdev)
regcache_cache_only(i2s->regmap, true);
- err = devm_snd_soc_register_component(dev, &tegra210_i2s_cmpnt,
+ /* Update the dais max channel as per soc */
+ for (id = 0; id < ARRAY_SIZE(tegra210_i2s_dais); id++) {
+ tegra210_i2s_dais[id].playback.channels_max = i2s->soc_data->max_ch;
+ tegra210_i2s_dais[id].capture.channels_max = i2s->soc_data->max_ch;
+ }
+
+ err = devm_snd_soc_register_component(dev, i2s->soc_data->i2s_cmpnt,
tegra210_i2s_dais,
ARRAY_SIZE(tegra210_i2s_dais));
if (err) {
@@ -1015,8 +1127,31 @@ static const struct dev_pm_ops tegra210_i2s_pm_ops = {
SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
+static const struct tegra_i2s_soc_data soc_data_tegra210 = {
+ .regmap_conf = &tegra210_regmap_conf,
+ .i2s_cmpnt = &tegra210_i2s_cmpnt,
+ .max_ch = TEGRA210_I2S_MAX_CHANNEL,
+ .tx_offset = TEGRA210_I2S_TX_OFFSET,
+ .i2s_ctrl_offset = TEGRA210_I2S_CTRL_OFFSET,
+ .fsync_width_mask = I2S_CTRL_FSYNC_WIDTH_MASK,
+ .fsync_width_shift = I2S_FSYNC_WIDTH_SHIFT,
+ .slot_mask = DEFAULT_I2S_SLOT_MASK,
+};
+
+static const struct tegra_i2s_soc_data soc_data_tegra264 = {
+ .regmap_conf = &tegra264_regmap_conf,
+ .i2s_cmpnt = &tegra264_i2s_cmpnt,
+ .max_ch = TEGRA264_I2S_MAX_CHANNEL,
+ .tx_offset = TEGRA264_I2S_TX_OFFSET,
+ .i2s_ctrl_offset = TEGRA264_I2S_CTRL_OFFSET,
+ .fsync_width_mask = TEGRA264_I2S_CTRL_FSYNC_WIDTH_MASK,
+ .fsync_width_shift = TEGRA264_I2S_FSYNC_WIDTH_SHIFT,
+ .slot_mask = TEGRA264_DEFAULT_I2S_SLOT_MASK,
+};
+
static const struct of_device_id tegra210_i2s_of_match[] = {
- { .compatible = "nvidia,tegra210-i2s" },
+ { .compatible = "nvidia,tegra210-i2s", .data = &soc_data_tegra210 },
+ { .compatible = "nvidia,tegra264-i2s", .data = &soc_data_tegra264 },
{},
};
MODULE_DEVICE_TABLE(of, tegra210_i2s_of_match);
diff --git a/sound/soc/tegra/tegra210_i2s.h b/sound/soc/tegra/tegra210_i2s.h
index 543332de7405..42be2137342c 100644
--- a/sound/soc/tegra/tegra210_i2s.h
+++ b/sound/soc/tegra/tegra210_i2s.h
@@ -1,5 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0-only
- * SPDX-FileCopyrightText: Copyright (c) 2020-2024 NVIDIA CORPORATION & AFFILIATES.
+ * SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION & AFFILIATES.
* All rights reserved.
*
* tegra210_i2s.h - Definitions for Tegra210 I2S driver
@@ -47,9 +47,38 @@
#define TEGRA210_I2S_CLK_TRIM 0xac
#define TEGRA210_I2S_CYA 0xb0
+/* T264 specific registers */
+#define TEGRA264_I2S_RX_FIFO_WR_ACCESS_MODE 0x30
+#define TEGRA264_I2S_RX_CYA 0x3c
+#define TEGRA264_I2S_RX_CIF_FIFO_STATUS 0x40
+#define TEGRA264_I2S_TX_ENABLE 0x80
+#define TEGRA264_I2S_TX_SOFT_RESET 0x84
+#define TEGRA264_I2S_TX_STATUS 0x8c
+#define TEGRA264_I2S_TX_INT_STATUS 0x90
+#define TEGRA264_I2S_TX_INT_MASK 0x94
+#define TEGRA264_I2S_TX_CIF_CTRL 0xa0
+#define TEGRA264_I2S_TX_FIFO_RD_ACCESS_MODE 0xb0
+#define TEGRA264_I2S_TX_FIFO_RD_DATA 0xb4
+#define TEGRA264_I2S_TX_FIFO_THRESHOLD 0xb8
+#define TEGRA264_I2S_TX_CYA 0xbc
+#define TEGRA264_I2S_TX_CIF_FIFO_STATUS 0xc0
+#define TEGRA264_I2S_ENABLE 0x100
+#define TEGRA264_I2S_CG 0x108
+#define TEGRA264_I2S_STATUS 0x10c
+#define TEGRA264_I2S_INT_STATUS 0x110
+#define TEGRA264_I2S_INT_SET 0x114
+#define TEGRA264_I2S_INT_MASK 0x11c
+#define TEGRA264_I2S_CTRL 0x12c
+#define TEGRA264_I2S_TIMING 0x130
+#define TEGRA264_I2S_CYA 0x13c
+#define TEGRA264_I2S_PIO_MODE_ENABLE 0x140
+#define TEGRA264_I2S_PAD_MACRO_STATUS 0x144
+
/* Bit fields, shifts and masks */
#define I2S_DATA_SHIFT 8
#define I2S_CTRL_DATA_OFFSET_MASK (0x7ff << I2S_DATA_SHIFT)
+#define TEGRA264_I2S_FSYNC_WIDTH_SHIFT 23
+#define TEGRA264_I2S_CTRL_FSYNC_WIDTH_MASK (0x1ff << TEGRA264_I2S_FSYNC_WIDTH_SHIFT)
#define I2S_EN_SHIFT 0
#define I2S_EN_MASK BIT(I2S_EN_SHIFT)
@@ -102,6 +131,14 @@
#define DEFAULT_I2S_RX_FIFO_THRESHOLD 3
#define DEFAULT_I2S_SLOT_MASK 0xffff
+#define TEGRA210_I2S_TX_OFFSET 0
+#define TEGRA210_I2S_CTRL_OFFSET 0
+#define TEGRA210_I2S_MAX_CHANNEL 16
+
+#define TEGRA264_DEFAULT_I2S_SLOT_MASK 0xffffffff
+#define TEGRA264_I2S_TX_OFFSET 0x40
+#define TEGRA264_I2S_CTRL_OFFSET 0x8c
+#define TEGRA264_I2S_MAX_CHANNEL 32
enum tegra210_i2s_path {
I2S_RX_PATH,
@@ -109,7 +146,19 @@ enum tegra210_i2s_path {
I2S_PATHS,
};
+struct tegra_i2s_soc_data {
+ const struct regmap_config *regmap_conf;
+ const struct snd_soc_component_driver *i2s_cmpnt;
+ unsigned int max_ch;
+ unsigned int tx_offset;
+ unsigned int i2s_ctrl_offset;
+ unsigned int fsync_width_mask;
+ unsigned int fsync_width_shift;
+ unsigned int slot_mask;
+};
+
struct tegra210_i2s {
+ const struct tegra_i2s_soc_data *soc_data;
struct clk *clk_i2s;
struct clk *clk_sync_input;
struct regmap *regmap;
diff --git a/sound/soc/tegra/tegra_audio_graph_card.c b/sound/soc/tegra/tegra_audio_graph_card.c
index 8b48813c2c59..94b5ab77649b 100644
--- a/sound/soc/tegra/tegra_audio_graph_card.c
+++ b/sound/soc/tegra/tegra_audio_graph_card.c
@@ -1,8 +1,7 @@
// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION. All rights reserved.
//
// tegra_audio_graph_card.c - Audio Graph based Tegra Machine Driver
-//
-// Copyright (c) 2020-2021 NVIDIA CORPORATION. All rights reserved.
#include <linux/math64.h>
#include <linux/module.h>
@@ -232,11 +231,22 @@ static const struct tegra_audio_cdata tegra186_data = {
.plla_out0_rates[x11_RATE] = 45158400,
};
+static const struct tegra_audio_cdata tegra264_data = {
+ /* PLLA1 */
+ .plla_rates[x8_RATE] = 983040000,
+ .plla_rates[x11_RATE] = 993484800,
+ /* PLLA1_OUT1 */
+ .plla_out0_rates[x8_RATE] = 49152000,
+ .plla_out0_rates[x11_RATE] = 45158400,
+};
+
static const struct of_device_id graph_of_tegra_match[] = {
{ .compatible = "nvidia,tegra210-audio-graph-card",
.data = &tegra210_data },
{ .compatible = "nvidia,tegra186-audio-graph-card",
.data = &tegra186_data },
+ { .compatible = "nvidia,tegra264-audio-graph-card",
+ .data = &tegra264_data },
{},
};
MODULE_DEVICE_TABLE(of, graph_of_tegra_match);
diff --git a/sound/soc/tegra/tegra_cif.h b/sound/soc/tegra/tegra_cif.h
index 7cca8068f4b5..916aa10d8af8 100644
--- a/sound/soc/tegra/tegra_cif.h
+++ b/sound/soc/tegra/tegra_cif.h
@@ -1,8 +1,7 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * tegra_cif.h - TEGRA Audio CIF Programming
+/* SPDX-License-Identifier: GPL-2.0-only
+ * SPDX-FileCopyrightText: Copyright (c) 2020-2025 NVIDIA CORPORATION. All rights reserved.
*
- * Copyright (c) 2020 NVIDIA CORPORATION. All rights reserved.
+ * tegra_cif.h - TEGRA Audio CIF Programming
*
*/
@@ -22,6 +21,10 @@
#define TEGRA_ACIF_CTRL_TRUNCATE_SHIFT 1
#define TEGRA_ACIF_CTRL_MONO_CONV_SHIFT 0
+#define TEGRA264_ACIF_CTRL_AUDIO_BITS_SHIFT 11
+#define TEGRA264_ACIF_CTRL_CLIENT_CH_SHIFT 14
+#define TEGRA264_ACIF_CTRL_AUDIO_CH_SHIFT 19
+
/* AUDIO/CLIENT_BITS values */
#define TEGRA_ACIF_BITS_8 1
#define TEGRA_ACIF_BITS_16 3
@@ -62,4 +65,23 @@ static inline void tegra_set_cif(struct regmap *regmap, unsigned int reg,
regmap_update_bits(regmap, reg, TEGRA_ACIF_UPDATE_MASK, value);
}
+static inline void tegra264_set_cif(struct regmap *regmap, unsigned int reg,
+ struct tegra_cif_conf *conf)
+{
+ unsigned int value;
+
+ value = (conf->threshold << TEGRA_ACIF_CTRL_FIFO_TH_SHIFT) |
+ ((conf->audio_ch - 1) << TEGRA264_ACIF_CTRL_AUDIO_CH_SHIFT) |
+ ((conf->client_ch - 1) << TEGRA264_ACIF_CTRL_CLIENT_CH_SHIFT) |
+ (conf->audio_bits << TEGRA264_ACIF_CTRL_AUDIO_BITS_SHIFT) |
+ (conf->client_bits << TEGRA_ACIF_CTRL_CLIENT_BITS_SHIFT) |
+ (conf->expand << TEGRA_ACIF_CTRL_EXPAND_SHIFT) |
+ (conf->stereo_conv << TEGRA_ACIF_CTRL_STEREO_CONV_SHIFT) |
+ (conf->replicate << TEGRA_ACIF_CTRL_REPLICATE_SHIFT) |
+ (conf->truncate << TEGRA_ACIF_CTRL_TRUNCATE_SHIFT) |
+ (conf->mono_conv << TEGRA_ACIF_CTRL_MONO_CONV_SHIFT);
+
+ regmap_update_bits(regmap, reg, TEGRA_ACIF_UPDATE_MASK, value);
+}
+
#endif
diff --git a/sound/soc/tegra/tegra_isomgr_bw.c b/sound/soc/tegra/tegra_isomgr_bw.c
index 18e802bca6a6..fa979960bc09 100644
--- a/sound/soc/tegra/tegra_isomgr_bw.c
+++ b/sound/soc/tegra/tegra_isomgr_bw.c
@@ -11,8 +11,8 @@
#include "tegra_isomgr_bw.h"
#include "tegra210_admaif.h"
-/* Max possible rate is 192KHz x 16channel x 4bytes */
-#define MAX_BW_PER_DEV 12288
+#define MAX_SAMPLE_RATE 192 /* KHz*/
+#define MAX_BYTES_PER_SAMPLE 4
int tegra_isomgr_adma_setbw(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai, bool is_running)
@@ -98,7 +98,8 @@ int tegra_isomgr_adma_register(struct device *dev)
}
adma_isomgr->max_pcm_device = admaif->soc_data->num_ch;
- adma_isomgr->max_bw = STREAM_TYPE * MAX_BW_PER_DEV * adma_isomgr->max_pcm_device;
+ adma_isomgr->max_bw = STREAM_TYPE * MAX_SAMPLE_RATE * MAX_BYTES_PER_SAMPLE *
+ admaif->soc_data->max_stream_ch * adma_isomgr->max_pcm_device;
for (i = 0; i < STREAM_TYPE; i++) {
adma_isomgr->bw_per_dev[i] = devm_kzalloc(dev, adma_isomgr->max_pcm_device *
diff --git a/sound/soc/ti/Kconfig b/sound/soc/ti/Kconfig
index e22e41af3226..3323cf96e309 100644
--- a/sound/soc/ti/Kconfig
+++ b/sound/soc/ti/Kconfig
@@ -1,5 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
-menu "Audio support for Texas Instruments SoCs"
+menu "Texas Instruments"
depends on DMA_OMAP || TI_EDMA || TI_K3_UDMA || COMPILE_TEST
config SND_SOC_TI_EDMA_PCM
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index a0b8cca31cba..caf1887cc9d1 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -2157,8 +2157,8 @@ static int davinci_mcasp_gpio_direction_out(struct gpio_chip *chip,
return 0;
}
-static void davinci_mcasp_gpio_set(struct gpio_chip *chip, unsigned offset,
- int value)
+static int davinci_mcasp_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
{
struct davinci_mcasp *mcasp = gpiochip_get_data(chip);
@@ -2166,6 +2166,8 @@ static void davinci_mcasp_gpio_set(struct gpio_chip *chip, unsigned offset,
mcasp_set_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
else
mcasp_clr_bits(mcasp, DAVINCI_MCASP_PDOUT_REG, BIT(offset));
+
+ return 0;
}
static int davinci_mcasp_gpio_direction_in(struct gpio_chip *chip,
@@ -2216,7 +2218,7 @@ static const struct gpio_chip davinci_mcasp_template_chip = {
.request = davinci_mcasp_gpio_request,
.free = davinci_mcasp_gpio_free,
.direction_output = davinci_mcasp_gpio_direction_out,
- .set = davinci_mcasp_gpio_set,
+ .set_rv = davinci_mcasp_gpio_set,
.direction_input = davinci_mcasp_gpio_direction_in,
.get = davinci_mcasp_gpio_get,
.get_direction = davinci_mcasp_gpio_get_direction,
diff --git a/sound/soc/ti/omap-hdmi.c b/sound/soc/ti/omap-hdmi.c
index cf43ac19c4a6..55e7cb96858f 100644
--- a/sound/soc/ti/omap-hdmi.c
+++ b/sound/soc/ti/omap-hdmi.c
@@ -361,17 +361,20 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
if (!card->dai_link)
return -ENOMEM;
- compnent = devm_kzalloc(dev, sizeof(*compnent), GFP_KERNEL);
+ compnent = devm_kzalloc(dev, 2 * sizeof(*compnent), GFP_KERNEL);
if (!compnent)
return -ENOMEM;
- card->dai_link->cpus = compnent;
+ card->dai_link->cpus = &compnent[0];
card->dai_link->num_cpus = 1;
card->dai_link->codecs = &snd_soc_dummy_dlc;
card->dai_link->num_codecs = 1;
+ card->dai_link->platforms = &compnent[1];
+ card->dai_link->num_platforms = 1;
card->dai_link->name = card->name;
card->dai_link->stream_name = card->name;
card->dai_link->cpus->dai_name = dev_name(ad->dssdev);
+ card->dai_link->platforms->name = dev_name(ad->dssdev);
card->num_links = 1;
card->dev = dev;
diff --git a/sound/soc/uniphier/Kconfig b/sound/soc/uniphier/Kconfig
index ddfa6424c656..7c978639e0aa 100644
--- a/sound/soc/uniphier/Kconfig
+++ b/sound/soc/uniphier/Kconfig
@@ -1,18 +1,11 @@
# SPDX-License-Identifier: GPL-2.0
-config SND_SOC_UNIPHIER
- tristate "ASoC support for UniPhier"
+menu "UniPhier"
depends on (ARCH_UNIPHIER || COMPILE_TEST)
- help
- Say Y or M if you want to add support for the Socionext
- UniPhier SoC audio interfaces. You will also need to select the
- audio interfaces to support below.
- If unsure select "N".
config SND_SOC_UNIPHIER_AIO
tristate "UniPhier AIO DAI Driver"
select REGMAP_MMIO
select SND_SOC_COMPRESS
- depends on SND_SOC_UNIPHIER
help
This adds ASoC driver support for Socionext UniPhier
'AIO' Audio Input/Output subsystem.
@@ -21,7 +14,6 @@ config SND_SOC_UNIPHIER_AIO
config SND_SOC_UNIPHIER_LD11
tristate "UniPhier LD11/LD20 Device Driver"
- depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
help
This adds ASoC driver for Socionext UniPhier LD11/LD20
@@ -31,7 +23,6 @@ config SND_SOC_UNIPHIER_LD11
config SND_SOC_UNIPHIER_PXS2
tristate "UniPhier PXs2 Device Driver"
- depends on SND_SOC_UNIPHIER
select SND_SOC_UNIPHIER_AIO
help
This adds ASoC driver for Socionext UniPhier PXs2
@@ -41,10 +32,11 @@ config SND_SOC_UNIPHIER_PXS2
config SND_SOC_UNIPHIER_EVEA_CODEC
tristate "UniPhier SoC internal audio codec"
- depends on SND_SOC_UNIPHIER
select REGMAP_MMIO
help
This adds Codec driver for Socionext UniPhier LD11/20 SoC
internal DAC. This driver supports Line In / Out and HeadPhone.
Select Y if you use such device.
If unsure select "N".
+
+endmenu
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
index 34b2438b52d3..113058060350 100644
--- a/sound/soc/ux500/Kconfig
+++ b/sound/soc/ux500/Kconfig
@@ -3,7 +3,7 @@
# Ux500 SoC audio configuration
#
menuconfig SND_SOC_UX500
- tristate "SoC Audio support for Ux500 platform"
+ tristate "Ux500"
depends on SND_SOC
depends on MFD_DB8500_PRCMU
help
diff --git a/sound/soc/xilinx/Kconfig b/sound/soc/xilinx/Kconfig
index 5bd2730aab76..670e0f301097 100644
--- a/sound/soc/xilinx/Kconfig
+++ b/sound/soc/xilinx/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Xilinx"
+
config SND_SOC_XILINX_I2S
tristate "Audio support for the Xilinx I2S"
help
@@ -21,3 +23,5 @@ config SND_SOC_XILINX_SPDIF
Select this option to enable Xilinx SPDIF Audio.
This provides playback and capture of SPDIF audio in
AES format.
+
+endmenu
diff --git a/sound/soc/xtensa/Kconfig b/sound/soc/xtensa/Kconfig
index 74b778f186ce..87457272c773 100644
--- a/sound/soc/xtensa/Kconfig
+++ b/sound/soc/xtensa/Kconfig
@@ -1,4 +1,6 @@
# SPDX-License-Identifier: GPL-2.0-only
+menu "Xtensa"
+
config SND_SOC_XTFPGA_I2S
tristate "XTFPGA I2S master"
select REGMAP_MMIO
@@ -6,3 +8,5 @@ config SND_SOC_XTFPGA_I2S
Say Y or M if you want to add support for codecs attached to the
I2S interface on XTFPGA daughter board. You will also need to select
the drivers for the rest of XTFPGA audio subsystem.
+
+endmenu