summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-10-01 15:00:50 +0100
committerMark Brown <broonie@kernel.org>2026-10-01 15:00:50 +0100
commit289b8ea1171a5e5ebc8d97ffb559774a70e045e3 (patch)
tree4457d311751b9947bda8c2a4e2365d454e5f6168
parent6cdd2fcc51988af989c24e09b970ea79afe47984 (diff)
parent0fbfaeecfd6f5925bfcd7cfcbd62860a9e868619 (diff)
downloadlinux-next-289b8ea1171a5e5ebc8d97ffb559774a70e045e3.tar.gz
linux-next-289b8ea1171a5e5ebc8d97ffb559774a70e045e3.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau1372.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/adi,adau7118.yaml22
-rw-r--r--Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml41
-rw-r--r--Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml71
-rw-r--r--Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml20
-rw-r--r--Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml16
-rw-r--r--Documentation/devicetree/bindings/sound/audio-graph-port.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml60
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml48
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml20
-rw-r--r--Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml60
-rw-r--r--Documentation/devicetree/bindings/sound/davinci-evm-audio.txt49
-rw-r--r--Documentation/devicetree/bindings/sound/dialog,da7219.yaml80
-rw-r--r--Documentation/devicetree/bindings/sound/esstech,es9039q2m.yaml94
-rw-r--r--Documentation/devicetree/bindings/sound/everest,es8316.yaml5
-rw-r--r--Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,easrc.yaml45
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml45
-rw-r--r--Documentation/devicetree/bindings/sound/fsl,sai.yaml14
-rw-r--r--Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audio-card.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/imx-audmux.yaml8
-rw-r--r--Documentation/devicetree/bindings/sound/invensense,ics43432.yaml8
-rw-r--r--Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml22
-rw-r--r--Documentation/devicetree/bindings/sound/mediatek,mt8183-audio.yaml82
-rw-r--r--Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml36
-rw-r--r--Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml36
-rw-r--r--Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml46
-rw-r--r--Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml50
-rw-r--r--Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml115
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml12
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml16
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,sm8250.yaml1
-rw-r--r--Documentation/devicetree/bindings/sound/qcom,wcd9378-sdw.yaml105
-rw-r--r--Documentation/devicetree/bindings/sound/realtek,rt5677.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rsnd.yaml16
-rw-r--r--Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml32
-rw-r--r--Documentation/devicetree/bindings/sound/stericsson,ux500-msp-i2s.yaml96
-rw-r--r--Documentation/devicetree/bindings/sound/ti,da830-evm-audio.yaml83
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tas2781.yaml4
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tas5805m.yaml10
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/ti,tpa6130a2.yaml2
-rw-r--r--Documentation/devicetree/bindings/sound/ux500-mop500.txt39
-rw-r--r--Documentation/devicetree/bindings/sound/ux500-msp.txt42
-rw-r--r--Documentation/devicetree/bindings/vendor-prefixes.yaml2
-rw-r--r--MAINTAINERS18
-rw-r--r--drivers/firmware/cirrus/cs_dsp.c2
-rw-r--r--drivers/gpib/fmh_gpib/fmh_gpib.c10
-rw-r--r--include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h52
-rw-r--r--include/linux/dmaengine.h9
-rw-r--r--include/linux/firmware/cirrus/cs_dsp.h2
-rw-r--r--include/sound/sdca.h23
-rw-r--r--include/sound/sdca_class.h70
-rw-r--r--include/sound/simple_card_utils.h2
-rw-r--r--include/sound/soc-component.h14
-rw-r--r--include/sound/soc-dai.h189
-rw-r--r--include/sound/soc.h16
-rw-r--r--include/sound/soc_sdw_utils.h20
-rw-r--r--include/sound/tas2781-dsp.h13
-rw-r--r--include/sound/tas2781.h24
-rw-r--r--sound/hda/core/intel-nhlt.c4
-rw-r--r--sound/soc/amd/acp-es8336.c3
-rw-r--r--sound/soc/amd/acp/Kconfig4
-rw-r--r--sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c12
-rw-r--r--sound/soc/amd/acp/soc_amd_sdw_common.h13
-rw-r--r--sound/soc/atmel/atmel-pcm-dma.c2
-rw-r--r--sound/soc/bcm/cygnus-ssp.h2
-rw-r--r--sound/soc/codecs/88pm860x-codec.c6
-rw-r--r--sound/soc/codecs/Kconfig49
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/ab8500-codec.c157
-rw-r--r--sound/soc/codecs/adau1372.c12
-rw-r--r--sound/soc/codecs/adau1373.c12
-rw-r--r--sound/soc/codecs/adau1701.c15
-rw-r--r--sound/soc/codecs/adau17x1.c17
-rw-r--r--sound/soc/codecs/adau1977.c13
-rw-r--r--sound/soc/codecs/adau7118.c12
-rw-r--r--sound/soc/codecs/ak4104.c7
-rw-r--r--sound/soc/codecs/ak4118.c7
-rw-r--r--sound/soc/codecs/ak4458.c9
-rw-r--r--sound/soc/codecs/ak4535.c6
-rw-r--r--sound/soc/codecs/ak4619.c32
-rw-r--r--sound/soc/codecs/ak4642.c14
-rw-r--r--sound/soc/codecs/ak4671.c7
-rw-r--r--sound/soc/codecs/ak5386.c6
-rw-r--r--sound/soc/codecs/ak5558.c7
-rw-r--r--sound/soc/codecs/alc5623.c11
-rw-r--r--sound/soc/codecs/alc5632.c13
-rw-r--r--sound/soc/codecs/arizona-jack.c1
-rw-r--r--sound/soc/codecs/arizona.c23
-rw-r--r--sound/soc/codecs/cpcap.c89
-rw-r--r--sound/soc/codecs/cs35l33.c8
-rw-r--r--sound/soc/codecs/cs35l34.c2
-rw-r--r--sound/soc/codecs/cs35l35.c7
-rw-r--r--sound/soc/codecs/cs35l36.c56
-rw-r--r--sound/soc/codecs/cs35l41.c10
-rw-r--r--sound/soc/codecs/cs35l45-tables.c4
-rw-r--r--sound/soc/codecs/cs35l45.c97
-rw-r--r--sound/soc/codecs/cs35l45.h23
-rw-r--r--sound/soc/codecs/cs35l56-shared-test.c56
-rw-r--r--sound/soc/codecs/cs35l56.c10
-rw-r--r--sound/soc/codecs/cs40l50-codec.c9
-rw-r--r--sound/soc/codecs/cs4234.c9
-rw-r--r--sound/soc/codecs/cs4265.c12
-rw-r--r--sound/soc/codecs/cs4270.c6
-rw-r--r--sound/soc/codecs/cs4271.c6
-rw-r--r--sound/soc/codecs/cs42l42.c11
-rw-r--r--sound/soc/codecs/cs42l43.c12
-rw-r--r--sound/soc/codecs/cs42l51.c7
-rw-r--r--sound/soc/codecs/cs42l52.c22
-rw-r--r--sound/soc/codecs/cs42l56.c17
-rw-r--r--sound/soc/codecs/cs42l73.c11
-rw-r--r--sound/soc/codecs/cs42l84.c6
-rw-r--r--sound/soc/codecs/cs42xx8.c8
-rw-r--r--sound/soc/codecs/cs43130.c14
-rw-r--r--sound/soc/codecs/cs4341.c8
-rw-r--r--sound/soc/codecs/cs4349.c12
-rw-r--r--sound/soc/codecs/cs48l32.c23
-rw-r--r--sound/soc/codecs/cs530x.c9
-rw-r--r--sound/soc/codecs/cs53l30.c8
-rw-r--r--sound/soc/codecs/cx20442.c2
-rw-r--r--sound/soc/codecs/cx2072x.c12
-rw-r--r--sound/soc/codecs/da7210.c9
-rw-r--r--sound/soc/codecs/da7218.c141
-rw-r--r--sound/soc/codecs/da7219.c12
-rw-r--r--sound/soc/codecs/da732x.c17
-rw-r--r--sound/soc/codecs/da9055.c8
-rw-r--r--sound/soc/codecs/es7134.c6
-rw-r--r--sound/soc/codecs/es7241.c7
-rw-r--r--sound/soc/codecs/es8311.c27
-rw-r--r--sound/soc/codecs/es8316.c27
-rw-r--r--sound/soc/codecs/es8323.c32
-rw-r--r--sound/soc/codecs/es8326.c177
-rw-r--r--sound/soc/codecs/es8326.h4
-rw-r--r--sound/soc/codecs/es8328.c20
-rw-r--r--sound/soc/codecs/es8375.c30
-rw-r--r--sound/soc/codecs/es8389.c65
-rw-r--r--sound/soc/codecs/es9039q2m.c2008
-rw-r--r--sound/soc/codecs/fs210x.c5
-rw-r--r--sound/soc/codecs/hda.c7
-rw-r--r--sound/soc/codecs/hdac_hda.c2
-rw-r--r--sound/soc/codecs/hdac_hdmi.c2
-rw-r--r--sound/soc/codecs/inno_rk3036.c12
-rw-r--r--sound/soc/codecs/isabelle.c13
-rw-r--r--sound/soc/codecs/lm49453.c15
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c6
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c4
-rw-r--r--sound/soc/codecs/lpass-va-macro.c4
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c48
-rw-r--r--sound/soc/codecs/max98088.c12
-rw-r--r--sound/soc/codecs/max98090.c35
-rw-r--r--sound/soc/codecs/max98095.c24
-rw-r--r--sound/soc/codecs/max98371.c7
-rw-r--r--sound/soc/codecs/max98373-i2c.c10
-rw-r--r--sound/soc/codecs/max98388.c10
-rw-r--r--sound/soc/codecs/max98390.c10
-rw-r--r--sound/soc/codecs/max98396.c12
-rw-r--r--sound/soc/codecs/max9850.c11
-rw-r--r--sound/soc/codecs/max98520.c10
-rw-r--r--sound/soc/codecs/max9860.c23
-rw-r--r--sound/soc/codecs/max9867.c10
-rw-r--r--sound/soc/codecs/max98925.c8
-rw-r--r--sound/soc/codecs/max98926.c8
-rw-r--r--sound/soc/codecs/max98927.c14
-rw-r--r--sound/soc/codecs/mc13783.c12
-rw-r--r--sound/soc/codecs/ml26124.c6
-rw-r--r--sound/soc/codecs/mt6359.c8
-rw-r--r--sound/soc/codecs/nau8325.c11
-rw-r--r--sound/soc/codecs/nau8360-dsp.c634
-rw-r--r--sound/soc/codecs/nau8360-dsp.h122
-rw-r--r--sound/soc/codecs/nau8360.c2309
-rw-r--r--sound/soc/codecs/nau8360.h917
-rw-r--r--sound/soc/codecs/nau8540.c15
-rw-r--r--sound/soc/codecs/nau8810.c12
-rw-r--r--sound/soc/codecs/nau8821.c11
-rw-r--r--sound/soc/codecs/nau8822.c27
-rw-r--r--sound/soc/codecs/nau8824.c11
-rw-r--r--sound/soc/codecs/nau8825.c29
-rw-r--r--sound/soc/codecs/ntp8835.c7
-rw-r--r--sound/soc/codecs/ntp8918.c7
-rw-r--r--sound/soc/codecs/pcm5102a.c30
-rw-r--r--sound/soc/codecs/pcm6240.c8
-rw-r--r--sound/soc/codecs/rk3308_codec.c11
-rw-r--r--sound/soc/codecs/rk3328_codec.c9
-rw-r--r--sound/soc/codecs/rt1011.c44
-rw-r--r--sound/soc/codecs/rt1015.c91
-rw-r--r--sound/soc/codecs/rt1016.c14
-rw-r--r--sound/soc/codecs/rt1019.c10
-rw-r--r--sound/soc/codecs/rt1305.c14
-rw-r--r--sound/soc/codecs/rt1308.c14
-rw-r--r--sound/soc/codecs/rt1318.c13
-rw-r--r--sound/soc/codecs/rt1320-sdw.c12
-rw-r--r--sound/soc/codecs/rt274.c8
-rw-r--r--sound/soc/codecs/rt286.c8
-rw-r--r--sound/soc/codecs/rt298.c8
-rw-r--r--sound/soc/codecs/rt5514-spi.c37
-rw-r--r--sound/soc/codecs/rt5514.c39
-rw-r--r--sound/soc/codecs/rt5616.c23
-rw-r--r--sound/soc/codecs/rt5631.c10
-rw-r--r--sound/soc/codecs/rt5631.h8
-rw-r--r--sound/soc/codecs/rt5640.c22
-rw-r--r--sound/soc/codecs/rt5645.c23
-rw-r--r--sound/soc/codecs/rt5651.c12
-rw-r--r--sound/soc/codecs/rt5659.c14
-rw-r--r--sound/soc/codecs/rt5660.c14
-rw-r--r--sound/soc/codecs/rt5663.c10
-rw-r--r--sound/soc/codecs/rt5665.c14
-rw-r--r--sound/soc/codecs/rt5668.c22
-rw-r--r--sound/soc/codecs/rt5670.c12
-rw-r--r--sound/soc/codecs/rt5677-spi.c21
-rw-r--r--sound/soc/codecs/rt5677.c10
-rw-r--r--sound/soc/codecs/rt5682.c22
-rw-r--r--sound/soc/codecs/rt5682s.c34
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c4
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c1
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.h3
-rw-r--r--sound/soc/codecs/rt712-sdca.c136
-rw-r--r--sound/soc/codecs/rt712-sdca.h6
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c15
-rw-r--r--sound/soc/codecs/rt721-sdca.c500
-rw-r--r--sound/soc/codecs/rt721-sdca.h17
-rw-r--r--sound/soc/codecs/rt766-sdca.c2
-rw-r--r--sound/soc/codecs/rt9120.c9
-rw-r--r--sound/soc/codecs/rt9123.c13
-rw-r--r--sound/soc/codecs/rtq9124.c9
-rw-r--r--sound/soc/codecs/rtq9128.c9
-rw-r--r--sound/soc/codecs/sgtl5000.c11
-rw-r--r--sound/soc/codecs/si476x.c18
-rw-r--r--sound/soc/codecs/sma1303.c13
-rw-r--r--sound/soc/codecs/sma1307.c27
-rw-r--r--sound/soc/codecs/sn624x-sdca-sdw.c1888
-rw-r--r--sound/soc/codecs/sn624x-sdca.h152
-rw-r--r--sound/soc/codecs/src4xxx.c8
-rw-r--r--sound/soc/codecs/ssm2518.c13
-rw-r--r--sound/soc/codecs/ssm2602.c13
-rw-r--r--sound/soc/codecs/ssm3515.c10
-rw-r--r--sound/soc/codecs/ssm4567.c13
-rw-r--r--sound/soc/codecs/sta32x.c116
-rw-r--r--sound/soc/codecs/sta350.c15
-rw-r--r--sound/soc/codecs/sta529.c7
-rw-r--r--sound/soc/codecs/tas2552.c14
-rw-r--r--sound/soc/codecs/tas2562.c10
-rw-r--r--sound/soc/codecs/tas2764.c14
-rw-r--r--sound/soc/codecs/tas2770.c14
-rw-r--r--sound/soc/codecs/tas2780.c10
-rw-r--r--sound/soc/codecs/tas2781-comlib-i2c.c7
-rw-r--r--sound/soc/codecs/tas2781-comlib.c22
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c40
-rw-r--r--sound/soc/codecs/tas2781-i2c.c320
-rw-r--r--sound/soc/codecs/tas2783-sdw.c218
-rw-r--r--sound/soc/codecs/tas2783.h4
-rw-r--r--sound/soc/codecs/tas5086.c7
-rw-r--r--sound/soc/codecs/tas571x.c7
-rw-r--r--sound/soc/codecs/tas5720.c9
-rw-r--r--sound/soc/codecs/tas6424.c9
-rw-r--r--sound/soc/codecs/tfa9879.c9
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c14
-rw-r--r--sound/soc/codecs/tlv320adcx140.c12
-rw-r--r--sound/soc/codecs/tlv320aic23.c9
-rw-r--r--sound/soc/codecs/tlv320aic26.c8
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c41
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c2
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c102
-rw-r--r--sound/soc/codecs/tlv320aic3x.c42
-rw-r--r--sound/soc/codecs/tlv320dac33.c10
-rw-r--r--sound/soc/codecs/tscs454.c15
-rw-r--r--sound/soc/codecs/twl4030.c80
-rw-r--r--sound/soc/codecs/uda1334.c6
-rw-r--r--sound/soc/codecs/uda1342.c7
-rw-r--r--sound/soc/codecs/uda1380.c11
-rw-r--r--sound/soc/codecs/wcd-mbhc-v2.c8
-rw-r--r--sound/soc/codecs/wcd9335.c25
-rw-r--r--sound/soc/codecs/wcd934x.c2
-rw-r--r--sound/soc/codecs/wcd9378-sdca.c1080
-rw-r--r--sound/soc/codecs/wcd9378-sdca.h20
-rw-r--r--sound/soc/codecs/wcd9378-sdw.c51
-rw-r--r--sound/soc/codecs/wm2200.c10
-rw-r--r--sound/soc/codecs/wm5100.c10
-rw-r--r--sound/soc/codecs/wm8350.c24
-rw-r--r--sound/soc/codecs/wm8400.c9
-rw-r--r--sound/soc/codecs/wm8510.c12
-rw-r--r--sound/soc/codecs/wm8523.c13
-rw-r--r--sound/soc/codecs/wm8524.c6
-rw-r--r--sound/soc/codecs/wm8580.c20
-rw-r--r--sound/soc/codecs/wm8711.c13
-rw-r--r--sound/soc/codecs/wm8728.c9
-rw-r--r--sound/soc/codecs/wm8731.c13
-rw-r--r--sound/soc/codecs/wm8737.c11
-rw-r--r--sound/soc/codecs/wm8741.c13
-rw-r--r--sound/soc/codecs/wm8750.c13
-rw-r--r--sound/soc/codecs/wm8753.c20
-rw-r--r--sound/soc/codecs/wm8770.c11
-rw-r--r--sound/soc/codecs/wm8776.c13
-rw-r--r--sound/soc/codecs/wm8804.c15
-rw-r--r--sound/soc/codecs/wm8900.c18
-rw-r--r--sound/soc/codecs/wm8903.c18
-rw-r--r--sound/soc/codecs/wm8904.c20
-rw-r--r--sound/soc/codecs/wm8940.c13
-rw-r--r--sound/soc/codecs/wm8955.c42
-rw-r--r--sound/soc/codecs/wm8958-dsp2.c8
-rw-r--r--sound/soc/codecs/wm8960.c13
-rw-r--r--sound/soc/codecs/wm8961.c18
-rw-r--r--sound/soc/codecs/wm8962.c74
-rw-r--r--sound/soc/codecs/wm8971.c13
-rw-r--r--sound/soc/codecs/wm8974.c17
-rw-r--r--sound/soc/codecs/wm8978.c12
-rw-r--r--sound/soc/codecs/wm8983.c12
-rw-r--r--sound/soc/codecs/wm8985.c53
-rw-r--r--sound/soc/codecs/wm8988.c13
-rw-r--r--sound/soc/codecs/wm8990.c9
-rw-r--r--sound/soc/codecs/wm8991.c9
-rw-r--r--sound/soc/codecs/wm8993.c18
-rw-r--r--sound/soc/codecs/wm8994.c23
-rw-r--r--sound/soc/codecs/wm8995.c61
-rw-r--r--sound/soc/codecs/wm8996.c14
-rw-r--r--sound/soc/codecs/wm9081.c18
-rw-r--r--sound/soc/codecs/wm9713.c13
-rw-r--r--sound/soc/codecs/wm_hubs.c2
-rw-r--r--sound/soc/codecs/zl38060.c6
-rw-r--r--sound/soc/dwc/dwc-i2s.c9
-rw-r--r--sound/soc/fsl/Kconfig2
-rw-r--r--sound/soc/fsl/fsl_asrc.c110
-rw-r--r--sound/soc/fsl/fsl_asrc_common.h7
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c38
-rw-r--r--sound/soc/fsl/fsl_asrc_m2m.c17
-rw-r--r--sound/soc/fsl/fsl_easrc.c125
-rw-r--r--sound/soc/fsl/fsl_mqs.c14
-rw-r--r--sound/soc/fsl/fsl_xcvr.c26
-rw-r--r--sound/soc/generic/audio-graph-card.c1
-rw-r--r--sound/soc/generic/audio-graph-card2.c4
-rw-r--r--sound/soc/generic/simple-card-utils.c23
-rw-r--r--sound/soc/generic/simple-card.c13
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c7
-rw-r--r--sound/soc/img/img-i2s-in.c12
-rw-r--r--sound/soc/img/img-i2s-out.c13
-rw-r--r--sound/soc/img/img-parallel-out.c8
-rw-r--r--sound/soc/intel/atom/sst-mfld-dsp.h2
-rw-r--r--sound/soc/intel/atom/sst/sst.h2
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c14
-rw-r--r--sound/soc/intel/avs/boards/hdaudio.c8
-rw-r--r--sound/soc/intel/avs/pcm.c11
-rw-r--r--sound/soc/intel/avs/topology.c25
-rw-r--r--sound/soc/intel/avs/topology.h3
-rw-r--r--sound/soc/intel/boards/Kconfig1
-rw-r--r--sound/soc/intel/boards/sof_cirrus_common.c2
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-arl-match.c71
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-lnl-match.c71
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-mtl-match.c71
-rw-r--r--sound/soc/intel/common/soc-acpi-intel-ptl-match.c85
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.c281
-rw-r--r--sound/soc/intel/common/sof-function-topology-lib.h3
-rw-r--r--sound/soc/jz4740/jz4740-i2s.c7
-rw-r--r--sound/soc/kirkwood/kirkwood-i2s.c7
-rw-r--r--sound/soc/loongson/loongson_i2s.c6
-rw-r--r--sound/soc/mediatek/Kconfig27
-rw-r--r--sound/soc/mediatek/Makefile1
-rw-r--r--sound/soc/mediatek/an7581/Makefile9
-rw-r--r--sound/soc/mediatek/an7581/an7581-afe-common.h49
-rw-r--r--sound/soc/mediatek/an7581/an7581-afe-pcm.c515
-rw-r--r--sound/soc/mediatek/an7581/an7581-dai-etdm.c433
-rw-r--r--sound/soc/mediatek/an7581/an7581-reg.h114
-rw-r--r--sound/soc/mediatek/an7581/an7581-wm8960.c161
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c14
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h2
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c61
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c4
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c4
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c4
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-clk.c23
-rw-r--r--sound/soc/mediatek/mt6797/mt6797-afe-pcm.c8
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-afe-pcm.c8
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-dai-etdm.c2
-rw-r--r--sound/soc/mediatek/mt7986/mt7986-wm8960.c4
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c26
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-clk.c31
-rw-r--r--sound/soc/mediatek/mt8183/mt8183-afe-pcm.c21
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-clk.c95
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-gpio.c6
-rw-r--r--sound/soc/mediatek/mt8186/mt8186-afe-pcm.c23
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-clk.c115
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-afe-pcm.c36
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-audsys-clk.c12
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-adda.c5
-rw-r--r--sound/soc/mediatek/mt8188/mt8188-dai-dmic.c10
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-clk.c134
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-afe-pcm.c61
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-i2s.c18
-rw-r--r--sound/soc/mediatek/mt8189/mt8189-dai-tdm.c19
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-clk.c163
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-gpio.c94
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-afe-pcm.c23
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-adda.c44
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-i2s.c20
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-dai-tdm.c18
-rw-r--r--sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c37
-rw-r--r--sound/soc/mediatek/mt8195/mt8195-afe-pcm.c2
-rw-r--r--sound/soc/mediatek/mt8196/mt8196-afe-pcm.c14
-rw-r--r--sound/soc/mxs/mxs-saif.c10
-rw-r--r--sound/soc/pxa/mmp-sspa.c6
-rw-r--r--sound/soc/pxa/pxa-ssp.c11
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c6
-rw-r--r--sound/soc/pxa/pxa2xx-pcm-lib.c9
-rw-r--r--sound/soc/qcom/common.c61
-rw-r--r--sound/soc/qcom/common.h5
-rw-r--r--sound/soc/qcom/qdsp6/q6apm-lpass-dais.c2
-rw-r--r--sound/soc/qcom/qdsp6/q6asm-dai.c2
-rw-r--r--sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c54
-rw-r--r--sound/soc/qcom/sc8280xp.c132
-rw-r--r--sound/soc/qcom/sdm845.c49
-rw-r--r--sound/soc/renesas/rz-ssi.c9
-rw-r--r--sound/soc/renesas/siu_dai.c6
-rw-r--r--sound/soc/renesas/ssi.c13
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c4
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c25
-rw-r--r--sound/soc/rockchip/rockchip_i2s_tdm.c27
-rw-r--r--sound/soc/rockchip/rockchip_pdm.c14
-rw-r--r--sound/soc/rockchip/rockchip_sai.c31
-rw-r--r--sound/soc/samsung/aries_wm8994.c14
-rw-r--r--sound/soc/samsung/pcm.c19
-rw-r--r--sound/soc/samsung/spdif.c8
-rw-r--r--sound/soc/samsung/tm2_wm5110.c12
-rw-r--r--sound/soc/sdca/Kconfig6
-rw-r--r--sound/soc/sdca/sdca_asoc.c4
-rw-r--r--sound/soc/sdca/sdca_class.c148
-rw-r--r--sound/soc/sdca/sdca_class.h35
-rw-r--r--sound/soc/sdca/sdca_class_function.c50
-rw-r--r--sound/soc/sdca/sdca_device.c4
-rw-r--r--sound/soc/sdca/sdca_fdl.c18
-rw-r--r--sound/soc/sdca/sdca_functions.c15
-rw-r--r--sound/soc/sdw_utils/Makefile4
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt711.c1
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c6
-rw-r--r--sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c8
-rw-r--r--sound/soc/sdw_utils/soc_sdw_senary_amp.c80
-rw-r--r--sound/soc/sdw_utils/soc_sdw_senary_dmic.c49
-rw-r--r--sound/soc/sdw_utils/soc_sdw_senary_sdca.c63
-rw-r--r--sound/soc/sdw_utils/soc_sdw_senary_sdca_jack_common.c197
-rw-r--r--sound/soc/sdw_utils/soc_sdw_utils.c384
-rw-r--r--sound/soc/soc-component.c8
-rw-r--r--sound/soc/soc-compress.c5
-rw-r--r--sound/soc/soc-core.c175
-rw-r--r--sound/soc/soc-dai.c418
-rw-r--r--sound/soc/soc-dapm.c9
-rw-r--r--sound/soc/soc-generic-dmaengine-pcm.c10
-rw-r--r--sound/soc/soc-internal.h39
-rw-r--r--sound/soc/soc-ops.c4
-rw-r--r--sound/soc/soc-pcm.c144
-rw-r--r--sound/soc/sof/amd/Kconfig1
-rw-r--r--sound/soc/sof/amd/acp-common.c17
-rw-r--r--sound/soc/sof/amd/acp-dsp-offset.h17
-rw-r--r--sound/soc/sof/amd/acp.c389
-rw-r--r--sound/soc/sof/amd/acp.h16
-rw-r--r--sound/soc/sof/amd/acp7x.h37
-rw-r--r--sound/soc/sof/amd/pci-acp7x.c2
-rw-r--r--sound/soc/sof/imx/imx-common.c1
-rw-r--r--sound/soc/sof/imx/imx8.c26
-rw-r--r--sound/soc/sof/intel/apl.c1
-rw-r--r--sound/soc/sof/intel/cnl.c1
-rw-r--r--sound/soc/sof/intel/hda-dai.c26
-rw-r--r--sound/soc/sof/intel/icl.c1
-rw-r--r--sound/soc/sof/intel/mtl.c1
-rw-r--r--sound/soc/sof/intel/skl.c1
-rw-r--r--sound/soc/sof/intel/tgl.c1
-rw-r--r--sound/soc/sof/ipc4-priv.h10
-rw-r--r--sound/soc/sof/ipc4-topology.c63
-rw-r--r--sound/soc/sof/mediatek/mt8186/mt8186.c2
-rw-r--r--sound/soc/sof/mediatek/mt8195/mt8195.c2
-rw-r--r--sound/soc/sof/topology.c3
-rw-r--r--sound/soc/sprd/sprd-pcm-compress.c7
-rw-r--r--sound/soc/sprd/sprd-pcm-dma.c10
-rw-r--r--sound/soc/sti/uniperif_player.c29
-rw-r--r--sound/soc/sti/uniperif_reader.c27
-rw-r--r--sound/soc/stm/stm32_i2s.c12
-rw-r--r--sound/soc/stm/stm32_sai_sub.c15
-rw-r--r--sound/soc/stm/stm32_spdifrx.c2
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c13
-rw-r--r--sound/soc/sunxi/sun8i-codec.c13
-rw-r--r--sound/soc/tegra/tegra20_i2s.c10
-rw-r--r--sound/soc/tegra/tegra210_adx.c12
-rw-r--r--sound/soc/tegra/tegra210_adx.h2
-rw-r--r--sound/soc/tegra/tegra210_i2s.c13
-rw-r--r--sound/soc/tegra/tegra30_i2s.c10
-rw-r--r--sound/soc/ti/davinci-i2s.c16
-rw-r--r--sound/soc/ti/davinci-mcasp.c17
-rw-r--r--sound/soc/ti/omap-mcbsp.c14
-rw-r--r--sound/soc/uniphier/aio-cpu.c9
-rw-r--r--sound/soc/uniphier/aio-dma.c5
-rw-r--r--sound/soc/uniphier/aio.h2
-rw-r--r--sound/soc/ux500/Kconfig23
-rw-r--r--sound/soc/ux500/Makefile3
-rw-r--r--sound/soc/ux500/mop500.c167
-rw-r--r--sound/soc/ux500/mop500_ab8500.c437
-rw-r--r--sound/soc/ux500/mop500_ab8500.h17
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c33
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c2
-rw-r--r--sound/soc/xilinx/xlnx_formatter_pcm.c79
-rw-r--r--sound/soc/xtensa/xtfpga-i2s.c12
501 files changed, 20902 insertions, 3594 deletions
diff --git a/Documentation/devicetree/bindings/sound/adi,adau1372.yaml b/Documentation/devicetree/bindings/sound/adi,adau1372.yaml
index 9a7ff50a0a22..532550de62d7 100644
--- a/Documentation/devicetree/bindings/sound/adi,adau1372.yaml
+++ b/Documentation/devicetree/bindings/sound/adi,adau1372.yaml
@@ -52,11 +52,11 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
audio-codec@3c {
- compatible = "adi,adau1372";
- reg = <0x3c>;
- #sound-dai-cells = <0>;
- clock-names = "mclk";
- clocks = <&adau1372z_xtal>;
+ compatible = "adi,adau1372";
+ reg = <0x3c>;
+ #sound-dai-cells = <0>;
+ clock-names = "mclk";
+ clocks = <&adau1372z_xtal>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/adi,adau7118.yaml b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
index 11f59c29b575..8272cdaf66a8 100644
--- a/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
+++ b/Documentation/devicetree/bindings/sound/adi,adau7118.yaml
@@ -68,20 +68,20 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
adau7118_codec: audio-codec@14 {
- compatible = "adi,adau7118";
- reg = <0x14>;
- #sound-dai-cells = <0>;
- iovdd-supply = <&supply>;
- dvdd-supply = <&supply>;
- adi,pdm-clk-map = <1 1 0 0>;
- adi,decimation-ratio = <16>;
+ compatible = "adi,adau7118";
+ reg = <0x14>;
+ #sound-dai-cells = <0>;
+ iovdd-supply = <&supply>;
+ dvdd-supply = <&supply>;
+ adi,pdm-clk-map = <1 1 0 0>;
+ adi,decimation-ratio = <16>;
};
};
/* example with hw standalone mode */
adau7118_codec_hw: adau7118-codec-hw {
- compatible = "adi,adau7118";
- #sound-dai-cells = <0>;
- iovdd-supply = <&supply>;
- dvdd-supply = <&supply>;
+ compatible = "adi,adau7118";
+ #sound-dai-cells = <0>;
+ iovdd-supply = <&supply>;
+ dvdd-supply = <&supply>;
};
diff --git a/Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml b/Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml
new file mode 100644
index 000000000000..80d9e87f1470
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml
@@ -0,0 +1,41 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/airoha,an7581-afe.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Airoha AFE PCM controller for AN7581
+
+maintainers:
+ - Christian Marangi <ansuelsmth@gmail.com>
+
+properties:
+ compatible:
+ const: airoha,an7581-afe
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+
+ afe@1fbe2200 {
+ compatible = "airoha,an7581-afe";
+ reg = <0x1fbe2200 0x9000>;
+
+ interrupts = <GIC_SPI 48 IRQ_TYPE_LEVEL_HIGH>;
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml b/Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml
new file mode 100644
index 000000000000..8886fbb6b43c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml
@@ -0,0 +1,71 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/airoha,an7581-wm8960.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Airoha AN7581 sound card with WM8960 codec
+
+maintainers:
+ - Christian Marangi <ansuelsmth@gmail.com>
+
+allOf:
+ - $ref: sound-card-common.yaml#
+
+properties:
+ compatible:
+ const: airoha,an7581-wm8960-sound
+
+ platform:
+ type: object
+
+ additionalProperties: false
+
+ properties:
+ sound-dai:
+ items:
+ - description: The phandle of AN7581 platform.
+
+ required:
+ - sound-dai
+
+ codec:
+ type: object
+
+ additionalProperties: false
+
+ properties:
+ sound-dai:
+ items:
+ - description: The phandle of WM8960 i2c codec.
+
+ required:
+ - sound-dai
+
+required:
+ - compatible
+ - audio-routing
+ - platform
+ - codec
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "airoha,an7581-wm8960-sound";
+ model = "an7581-wm8960";
+ audio-routing =
+ "Headphone", "HP_L",
+ "Headphone", "HP_R",
+ "LINPUT1", "AMIC",
+ "RINPUT1", "AMIC";
+
+ platform {
+ sound-dai = <&afe>;
+ };
+
+ codec {
+ sound-dai = <&wm8960>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
index 6fdf605ad59c..2be3b34e4f5f 100644
--- a/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
+++ b/Documentation/devicetree/bindings/sound/amlogic,gx-sound-card.yaml
@@ -94,20 +94,20 @@ examples:
audio-routing = "I2S ENCODER I2S IN", "I2S FIFO Playback";
dai-link-0 {
- sound-dai = <&i2s_fifo>;
+ sound-dai = <&i2s_fifo>;
};
dai-link-1 {
- sound-dai = <&i2s_encoder>;
- dai-format = "i2s";
- mclk-fs = <256>;
+ sound-dai = <&i2s_encoder>;
+ dai-format = "i2s";
+ mclk-fs = <256>;
- codec-0 {
- sound-dai = <&codec0>;
- };
+ codec-0 {
+ sound-dai = <&codec0>;
+ };
- codec-1 {
- sound-dai = <&codec1>;
- };
+ codec-1 {
+ sound-dai = <&codec1>;
+ };
};
};
diff --git a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml
index d412531ef9a2..7a7534493e6b 100644
--- a/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml
+++ b/Documentation/devicetree/bindings/sound/asahi-kasei,ak4619.yaml
@@ -34,6 +34,12 @@ properties:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
+ powerdown-gpios:
+ maxItems: 1
+ description:
+ GPIO connected to the 'PDN' pin, if any. Needs to be set low to
+ power down the codec, hence the GPIO must be GPIO_ACTIVE_LOW.
+
required:
- compatible
- reg
@@ -42,6 +48,8 @@ unevaluatedProperties: false
examples:
- |
+ #include <dt-bindings/gpio/gpio.h>
+
i2c {
#address-cells = <1>;
#size-cells = <0>;
@@ -49,14 +57,16 @@ examples:
compatible = "asahi-kasei,ak4619";
reg = <0x10>;
+ powerdown-gpios = <&gpio1 0 GPIO_ACTIVE_LOW>;
+
clocks = <&rcar_sound>;
clock-names = "mclk";
#sound-dai-cells = <0>;
port {
- ak4619_endpoint: endpoint {
- remote-endpoint = <&rsnd_endpoint>;
- };
+ ak4619_endpoint: endpoint {
+ remote-endpoint = <&rsnd_endpoint>;
+ };
};
};
};
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
index d1cbfc5edd3a..565d723d0398 100644
--- a/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
+++ b/Documentation/devicetree/bindings/sound/audio-graph-port.yaml
@@ -39,6 +39,7 @@ definitions:
allOf:
- $ref: /schemas/graph.yaml#/$defs/endpoint-base
- $ref: /schemas/sound/dai-params.yaml#
+ - $ref: /schemas/sound/tdm-slot.yaml#
properties:
mclk-fs:
$ref: simple-card.yaml#/definitions/mclk-fs
@@ -48,6 +49,9 @@ definitions:
bitclock-inversion:
description: dai-link uses bit clock inversion
$ref: /schemas/types.yaml#/definitions/flag
+ continuous-clock:
+ description: dai-link bit clock runs continuously rather than gated
+ $ref: /schemas/types.yaml#/definitions/flag
frame-master:
description: Indicates dai-link frame master.
oneOf:
@@ -82,12 +86,6 @@ definitions:
- msb
- lsb
- dai-tdm-slot-num:
- description: Number of slots in use.
- $ref: /schemas/types.yaml#/definitions/uint32
- dai-tdm-slot-width:
- description: Width in bits for each slot.
- $ref: /schemas/types.yaml#/definitions/uint32
dai-tdm-slot-width-map:
description: Mapping of sample widths to slot widths. For hardware
that cannot support a fixed slot width or a slot width always
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
index 70f6c62aedca..1ba809888a88 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs35l45.yaml
@@ -53,6 +53,24 @@ properties:
maximum: 3
default: 2
+ cirrus,sync-lsw-txid:
+ description:
+ Transmitter ID to use for long software (LSW) data packets
+ on the SYNC bus.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 7
+ default: 0
+
+ cirrus,sync-sw-txid:
+ description:
+ Transmitter ID to use for short software (SW) data packets
+ on the SYNC bus.
+ $ref: /schemas/types.yaml#/definitions/uint32
+ minimum: 0
+ maximum: 7
+ default: 0
+
patternProperties:
"^cirrus,gpio-ctrl[1-3]$":
description:
@@ -135,25 +153,27 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
- cs35l45: cs35l45@2 {
- #sound-dai-cells = <1>;
- compatible = "cirrus,cs35l45";
- reg = <2>;
- spi-max-frequency = <5000000>;
- vdd-a-supply = <&dummy_vreg>;
- vdd-batt-supply = <&dummy_vreg>;
- reset-gpios = <&gpio 110 0>;
- cirrus,asp-sdout-hiz-ctrl = <(CS35L45_ASP_TX_HIZ_UNUSED |
- CS35L45_ASP_TX_HIZ_DISABLED)>;
- cirrus,gpio-ctrl1 {
- gpio-ctrl = <0x2>;
- };
- cirrus,gpio-ctrl2 {
- gpio-ctrl = <0x2>;
- };
- cirrus,gpio-ctrl3 {
- gpio-ctrl = <0x1>;
- gpio-dir = <0x1>;
- };
+ cs35l45: audio-codec@2 {
+ #sound-dai-cells = <1>;
+ compatible = "cirrus,cs35l45";
+ reg = <2>;
+ spi-max-frequency = <5000000>;
+ vdd-a-supply = <&dummy_vreg>;
+ vdd-batt-supply = <&dummy_vreg>;
+ reset-gpios = <&gpio 110 0>;
+ cirrus,asp-sdout-hiz-ctrl = <(CS35L45_ASP_TX_HIZ_UNUSED |
+ CS35L45_ASP_TX_HIZ_DISABLED)>;
+ cirrus,sync-lsw-txid = <0x1>;
+ cirrus,sync-sw-txid = <0x1>;
+ cirrus,gpio-ctrl1 {
+ gpio-ctrl = <0x2>;
+ };
+ cirrus,gpio-ctrl2 {
+ gpio-ctrl = <0x2>;
+ };
+ cirrus,gpio-ctrl3 {
+ gpio-ctrl = <0x1>;
+ gpio-dir = <0x1>;
+ };
};
};
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
index 336e11773694..61106294a1d5 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs4270.yaml
@@ -49,11 +49,11 @@ unevaluatedProperties: false
examples:
- |
i2c {
- #address-cells = <1>;
- #size-cells = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
- codec@48 {
- compatible = "cirrus,cs4270";
- reg = <0x48>;
- };
+ codec@48 {
+ compatible = "cirrus,cs4270";
+ reg = <0x48>;
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
index af599d8735e2..ab4c7d22dc0e 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l42.yaml
@@ -199,28 +199,28 @@ examples:
- |
#include <dt-bindings/sound/cs42l42.h>
i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- cs42l42: cs42l42@48 {
- compatible = "cirrus,cs42l42";
- reg = <0x48>;
- VA-supply = <&dummy_vreg>;
- VP-supply = <&dummy_vreg>;
- VCP-supply = <&dummy_vreg>;
- VD_FILT-supply = <&dummy_vreg>;
- VL-supply = <&dummy_vreg>;
-
- reset-gpios = <&axi_gpio_0 1 0>;
- interrupt-parent = <&gpio0>;
- interrupts = <55 8>;
-
- cirrus,ts-inv = <CS42L42_TS_INV_DIS>;
- cirrus,ts-dbnc-rise = <CS42L42_TS_DBNCE_1000>;
- cirrus,ts-dbnc-fall = <CS42L42_TS_DBNCE_0>;
- cirrus,btn-det-init-dbnce = <100>;
- cirrus,btn-det-event-dbnce = <10>;
- cirrus,bias-lvls = <0x0F 0x08 0x04 0x01>;
- cirrus,hs-bias-ramp-rate = <CS42L42_HSBIAS_RAMP_SLOW>;
- };
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@48 {
+ compatible = "cirrus,cs42l42";
+ reg = <0x48>;
+ VA-supply = <&dummy_vreg>;
+ VP-supply = <&dummy_vreg>;
+ VCP-supply = <&dummy_vreg>;
+ VD_FILT-supply = <&dummy_vreg>;
+ VL-supply = <&dummy_vreg>;
+
+ reset-gpios = <&axi_gpio_0 1 0>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <55 8>;
+
+ cirrus,ts-inv = <CS42L42_TS_INV_DIS>;
+ cirrus,ts-dbnc-rise = <CS42L42_TS_DBNCE_1000>;
+ cirrus,ts-dbnc-fall = <CS42L42_TS_DBNCE_0>;
+ cirrus,btn-det-init-dbnce = <100>;
+ cirrus,btn-det-event-dbnce = <10>;
+ cirrus,bias-lvls = <0x0F 0x08 0x04 0x01>;
+ cirrus,hs-bias-ramp-rate = <CS42L42_HSBIAS_RAMP_SLOW>;
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
index 7f8338e8ae36..94062c1d9d45 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42l84.yaml
@@ -43,14 +43,14 @@ examples:
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- jack_codec: codec@4b {
- compatible = "cirrus,cs42l84";
- reg = <0x4b>;
- reset-gpios = <&pinctrl_nub 4 GPIO_ACTIVE_LOW>;
- interrupts-extended = <&pinctrl_ap 180 IRQ_TYPE_LEVEL_LOW>;
- #sound-dai-cells = <0>;
- };
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ jack_codec: codec@4b {
+ compatible = "cirrus,cs42l84";
+ reg = <0x4b>;
+ reset-gpios = <&pinctrl_nub 4 GPIO_ACTIVE_LOW>;
+ interrupts-extended = <&pinctrl_ap 180 IRQ_TYPE_LEVEL_LOW>;
+ #sound-dai-cells = <0>;
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml
index a1ae548c4b7b..b5cdc7e537f8 100644
--- a/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml
+++ b/Documentation/devicetree/bindings/sound/cirrus,cs42xx8.yaml
@@ -75,37 +75,37 @@ unevaluatedProperties: false
examples:
- |
i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec@48 {
- compatible = "cirrus,cs42888";
- reg = <0x48>;
- clocks = <&codec_mclk 0>;
- clock-names = "mclk";
- VA-supply = <&reg_audio>;
- VD-supply = <&reg_audio>;
- VLS-supply = <&reg_audio>;
- VLC-supply = <&reg_audio>;
- reset-gpios = <&gpio 1>;
- };
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@48 {
+ compatible = "cirrus,cs42888";
+ reg = <0x48>;
+ clocks = <&codec_mclk 0>;
+ clock-names = "mclk";
+ VA-supply = <&reg_audio>;
+ VD-supply = <&reg_audio>;
+ VLS-supply = <&reg_audio>;
+ VLC-supply = <&reg_audio>;
+ reset-gpios = <&gpio 1>;
+ };
};
spi {
- #address-cells = <1>;
- #size-cells = <0>;
- cs-gpios = <&gpio 8 0>;
-
- codec@0 {
- compatible = "cirrus,cs42888";
- reg = <0>;
- spi-max-frequency = <6000000>;
- clocks = <&codec_mclk 0>;
- clock-names = "mclk";
- VA-supply = <&reg_audio>;
- VD-supply = <&reg_audio>;
- VLS-supply = <&reg_audio>;
- VLC-supply = <&reg_audio>;
- reset-gpios = <&gpio 1>;
- };
+ #address-cells = <1>;
+ #size-cells = <0>;
+ cs-gpios = <&gpio 8 0>;
+
+ codec@0 {
+ compatible = "cirrus,cs42888";
+ reg = <0>;
+ spi-max-frequency = <6000000>;
+ clocks = <&codec_mclk 0>;
+ clock-names = "mclk";
+ VA-supply = <&reg_audio>;
+ VD-supply = <&reg_audio>;
+ VLS-supply = <&reg_audio>;
+ VLC-supply = <&reg_audio>;
+ reset-gpios = <&gpio 1>;
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt
deleted file mode 100644
index 963e100514c2..000000000000
--- a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt
+++ /dev/null
@@ -1,49 +0,0 @@
-* Texas Instruments SoC audio setups with TLV320AIC3X Codec
-
-Required properties:
-- compatible : "ti,da830-evm-audio" : forDM365/DA8xx/OMAPL1x/AM33xx
-- ti,model : The user-visible name of this sound complex.
-- ti,audio-codec : The phandle of the TLV320AIC3x audio codec
-- ti,mcasp-controller : The phandle of the McASP controller
-- ti,audio-routing : A list of the connections between audio components.
- Each entry is a pair of strings, the first being the connection's sink,
- the second being the connection's source. Valid names for sources and
- sinks are the codec's pins, and the jacks on the board:
-
-Optional properties:
-- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec.
-- clocks : Reference to the master clock
-- clock-names : The clock should be named "mclk"
-- Either codec-clock-rate or the codec-clock reference has to be defined. If
- the both are defined the driver attempts to set referenced clock to the
- defined rate and takes the rate from the clock reference.
-
- Board connectors:
-
- * Headphone Jack
- * Line Out
- * Mic Jack
- * Line In
-
-
-Example:
-
-sound {
- compatible = "ti,da830-evm-audio";
- ti,model = "DA830 EVM";
- ti,audio-codec = <&tlv320aic3x>;
- ti,mcasp-controller = <&mcasp1>;
- ti,codec-clock-rate = <12000000>;
- ti,audio-routing =
- "Headphone Jack", "HPLOUT",
- "Headphone Jack", "HPROUT",
- "Line Out", "LLOUT",
- "Line Out", "RLOUT",
- "MIC3L", "Mic Bias 2V",
- "MIC3R", "Mic Bias 2V",
- "Mic Bias 2V", "Mic Jack",
- "LINE1L", "Line In",
- "LINE2L", "Line In",
- "LINE1R", "Line In",
- "LINE2R", "Line In";
-};
diff --git a/Documentation/devicetree/bindings/sound/dialog,da7219.yaml b/Documentation/devicetree/bindings/sound/dialog,da7219.yaml
index 19137abdba3e..852f27e9dffe 100644
--- a/Documentation/devicetree/bindings/sound/dialog,da7219.yaml
+++ b/Documentation/devicetree/bindings/sound/dialog,da7219.yaml
@@ -195,44 +195,44 @@ examples:
- |
#include <dt-bindings/interrupt-controller/irq.h>
i2c {
- #address-cells = <1>;
- #size-cells = <0>;
-
- codec: da7219@1a {
- compatible = "dlg,da7219";
- reg = <0x1a>;
-
- interrupt-parent = <&gpio6>;
- interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
-
- VDD-supply = <&vdd_reg>;
- VDDMIC-supply = <&vddmic_reg>;
- VDDIO-supply = <&vddio_reg>;
-
- #clock-cells = <1>;
- clock-output-names = "da7219-dai-wclk", "da7219-dai-bclk";
-
- clocks = <&clks 201>;
- clock-names = "mclk";
-
- dlg,micbias-lvl = <2600>;
- dlg,mic-amp-in-sel = "diff";
-
- da7219_aad {
- dlg,btn-cfg = <50>;
- dlg,mic-det-thr = <500>;
- dlg,jack-ins-deb = <20>;
- dlg,jack-ins-det-pty = "low";
- dlg,jack-det-rate = "32_64";
- dlg,jack-rem-deb = <1>;
-
- dlg,a-d-btn-thr = <0xa>;
- dlg,d-b-btn-thr = <0x16>;
- dlg,b-c-btn-thr = <0x21>;
- dlg,c-mic-btn-thr = <0x3E>;
-
- dlg,btn-avg = <4>;
- dlg,adc-1bit-rpt = <1>;
- };
- };
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec: audio-codec@1a {
+ compatible = "dlg,da7219";
+ reg = <0x1a>;
+
+ interrupt-parent = <&gpio6>;
+ interrupts = <11 IRQ_TYPE_LEVEL_LOW>;
+
+ VDD-supply = <&vdd_reg>;
+ VDDMIC-supply = <&vddmic_reg>;
+ VDDIO-supply = <&vddio_reg>;
+
+ #clock-cells = <1>;
+ clock-output-names = "da7219-dai-wclk", "da7219-dai-bclk";
+
+ clocks = <&clks 201>;
+ clock-names = "mclk";
+
+ dlg,micbias-lvl = <2600>;
+ dlg,mic-amp-in-sel = "diff";
+
+ da7219_aad {
+ dlg,btn-cfg = <50>;
+ dlg,mic-det-thr = <500>;
+ dlg,jack-ins-deb = <20>;
+ dlg,jack-ins-det-pty = "low";
+ dlg,jack-det-rate = "32_64";
+ dlg,jack-rem-deb = <1>;
+
+ dlg,a-d-btn-thr = <0xa>;
+ dlg,d-b-btn-thr = <0x16>;
+ dlg,b-c-btn-thr = <0x21>;
+ dlg,c-mic-btn-thr = <0x3E>;
+
+ dlg,btn-avg = <4>;
+ dlg,adc-1bit-rpt = <1>;
+ };
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/esstech,es9039q2m.yaml b/Documentation/devicetree/bindings/sound/esstech,es9039q2m.yaml
new file mode 100644
index 000000000000..15db30af4b6c
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/esstech,es9039q2m.yaml
@@ -0,0 +1,94 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/esstech,es9039q2m.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ESS Technology ES9039Q2M audio DAC
+
+maintainers:
+ - Karl Asseily <karl@asseily.com>
+
+description:
+ The ES9039Q2M is a 32-bit two-channel audio DAC supporting PCM to 768 kHz,
+ DSD64 to DSD1024, DoP and S/PDIF. It is controlled over I2C or SPI when the
+ MODE pin selects software mode; this binding covers the I2C interface. The
+ part contains an asynchronous sample rate converter, so the system clock need
+ not be synchronous with the audio bit or frame clocks.
+
+ The four supplies are all nominally 3.3V and the datasheet requires a set
+ order - AVDD first, then VCCA about 200us later, then AVCC_DAC1 and
+ AVCC_DAC2, and the reverse on the way down. DVDD, the part's 1.2V rail, is
+ on-chip and needs only its decoupling capacitor - it is not a supply input
+ and has no property here.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: esstech,es9039q2m
+
+ reg:
+ description:
+ I2C address, selected by the ADDR0 and ADDR1 pins - 0x48, 0x49, 0x4a or
+ 0x4b for GND/GND, GND/AVDD, AVDD/GND and AVDD/AVDD respectively.
+ enum: [ 0x48, 0x49, 0x4a, 0x4b ]
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: mclk
+
+ avdd-supply:
+ description:
+ 3.3V supply for the part's internal digital regulator, pin AVDD. It is
+ the first rail up and the last down, per the ordering described above.
+
+ vcca-supply:
+ description: 3.3V analogue supply, pin VCCA.
+
+ avcc-dac1-supply:
+ description:
+ 3.3V reference supply for the channel 1 analogue output stage, pin
+ AVCC_DAC1. The datasheet allows this rail to move by up to 15% where a
+ calibrating regulator tracks the DAC's output impedance.
+
+ avcc-dac2-supply:
+ description:
+ 3.3V reference supply for the channel 2 analogue output stage, pin
+ AVCC_DAC2, with the same 15% allowance as channel 1.
+
+ "#sound-dai-cells":
+ const: 0
+
+required:
+ - compatible
+ - reg
+ - avdd-supply
+ - vcca-supply
+ - avcc-dac1-supply
+ - avcc-dac2-supply
+ - "#sound-dai-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ audio-codec@48 {
+ compatible = "esstech,es9039q2m";
+ reg = <0x48>;
+ #sound-dai-cells = <0>;
+ clocks = <&dac_mclk>;
+ clock-names = "mclk";
+ avdd-supply = <&dac_3v3>;
+ vcca-supply = <&dac_3v3>;
+ avcc-dac1-supply = <&dac_avcc1>;
+ avcc-dac2-supply = <&dac_avcc2>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/everest,es8316.yaml b/Documentation/devicetree/bindings/sound/everest,es8316.yaml
index f4ff23120c5b..9060358a9080 100644
--- a/Documentation/devicetree/bindings/sound/everest,es8316.yaml
+++ b/Documentation/devicetree/bindings/sound/everest,es8316.yaml
@@ -84,6 +84,11 @@ properties:
"#sound-dai-cells":
const: 0
+ everest,jack-detect-inverted:
+ $ref: /schemas/types.yaml#/definitions/flag
+ description:
+ Defined to invert the jack detection.
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
index 4da735317e0f..17c938a565e5 100644
--- a/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
+++ b/Documentation/devicetree/bindings/sound/foursemi,fs2105s.yaml
@@ -63,7 +63,7 @@ properties:
a. Register initialization settings
b. DSP effect parameters
c. Multi-scene sound effect configurations(optional)
- It's gernerated by FourSemi's tuning tool.
+ It's generated by FourSemi's tuning tool.
required:
- compatible
diff --git a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
index d5727f8bfb0b..a53ec4f6f1c0 100644
--- a/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,easrc.yaml
@@ -55,6 +55,14 @@ properties:
- const: imx/easrc/easrc-imx8mn.bin
description: The coefficient table for the filters
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ patternProperties:
+ "^port@[0-3]$":
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for an independent conversion path
+
fsl,asrc-rate:
$ref: /schemas/types.yaml#/definitions/uint32
minimum: 8000
@@ -106,4 +114,41 @@ examples:
firmware-name = "imx/easrc/easrc-imx8mn.bin";
fsl,asrc-rate = <8000>;
fsl,asrc-format = <2>;
+
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ playback-only;
+ endpoint {
+ remote-endpoint = <&fe00_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ playback-only;
+ endpoint {
+ remote-endpoint = <&fe01_ep>;
+ };
+ };
+
+ port@2 {
+ reg = <2>;
+ capture-only;
+ endpoint {
+ remote-endpoint = <&fe02_ep>;
+ };
+ };
+
+ port@3 {
+ reg = <3>;
+ capture-only;
+ endpoint {
+ remote-endpoint = <&fe03_ep>;
+ };
+ };
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
index 608defc93c1e..67d2826eef3f 100644
--- a/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,imx-asrc.yaml
@@ -78,9 +78,19 @@ properties:
power-domains:
maxItems: 1
+ ports:
+ $ref: /schemas/graph.yaml#/properties/ports
+ patternProperties:
+ "^port@[0-2]$":
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+ description: port for an independent conversion path
+
port:
$ref: audio-graph-port.yaml#
unevaluatedProperties: false
+ deprecated: true
+ description: deprecated, use ports instead
fsl,asrc-rate:
$ref: /schemas/types.yaml#/definitions/uint32
@@ -125,6 +135,10 @@ required:
- fsl,asrc-width
allOf:
+ - not:
+ required:
+ - port
+ - ports
- $ref: dai-common.yaml#
- if:
properties:
@@ -180,11 +194,32 @@ examples:
fsl,asrc-rate = <48000>;
fsl,asrc-width = <16>;
- port {
- playback-only;
+ ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ port@0 {
+ reg = <0>;
+ playback-only;
+ endpoint {
+ remote-endpoint = <&fe00_ep>;
+ };
+ };
+
+ port@1 {
+ reg = <1>;
+ capture-only;
+ endpoint {
+ remote-endpoint = <&fe01_ep>;
+ };
+ };
- asrc_endpoint: endpoint {
- remote-endpoint = <&fe00_ep>;
+ port@2 {
+ reg = <2>;
+ capture-only;
+ endpoint {
+ remote-endpoint = <&fe02_ep>;
+ };
};
- };
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/fsl,sai.yaml b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
index ba65b3f3d066..eb0b9d8d907f 100644
--- a/Documentation/devicetree/bindings/sound/fsl,sai.yaml
+++ b/Documentation/devicetree/bindings/sound/fsl,sai.yaml
@@ -260,13 +260,13 @@ examples:
playback-only;
sai1_endpoint0: endpoint {
- dai-tdm-slot-num = <8>;
- dai-tdm-slot-width = <32>;
- dai-tdm-slot-width-map = <32 8 32>;
- dai-format = "dsp_a";
- bitclock-master;
- frame-master;
- remote-endpoint = <&mcodec01_ep>;
+ dai-tdm-slot-num = <8>;
+ dai-tdm-slot-width = <32>;
+ dai-tdm-slot-width-map = <32 8 32>;
+ dai-format = "dsp_a";
+ bitclock-master;
+ frame-master;
+ remote-endpoint = <&mcodec01_ep>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml
index 5171f984630b..b12698264724 100644
--- a/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml
+++ b/Documentation/devicetree/bindings/sound/hisilicon,hi6210-i2s.yaml
@@ -48,6 +48,10 @@ properties:
The dai cell indexes reference the following interfaces:
0: S2 interface
+ ports:
+ $ref: audio-graph-port.yaml#/definitions/ports
+ unevaluatedProperties: false
+
required:
- compatible
- reg
diff --git a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
index 950e3eab2942..27636466ba63 100644
--- a/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
+++ b/Documentation/devicetree/bindings/sound/imx-audio-card.yaml
@@ -105,10 +105,10 @@ examples:
format = "i2s";
fsl,mclk-equal-bclk;
cpu {
- sound-dai = <&sai1>;
+ sound-dai = <&sai1>;
};
codec {
- sound-dai = <&ak4458_1>, <&ak4458_2>;
+ sound-dai = <&ak4458_1>, <&ak4458_2>;
};
};
fe-dai-link {
diff --git a/Documentation/devicetree/bindings/sound/imx-audmux.yaml b/Documentation/devicetree/bindings/sound/imx-audmux.yaml
index dab45c310670..501af1add3c1 100644
--- a/Documentation/devicetree/bindings/sound/imx-audmux.yaml
+++ b/Documentation/devicetree/bindings/sound/imx-audmux.yaml
@@ -101,10 +101,10 @@ examples:
mux-ssi1 {
fsl,audmux-port = <0>;
fsl,port-config = <
- IMX_AUDMUX_V2_PTCR_SYN 0
- IMX_AUDMUX_V2_PTCR_TFSEL(2) 0
- IMX_AUDMUX_V2_PTCR_TCSEL(2) 0
- IMX_AUDMUX_V2_PTCR_TFSDIR 0
+ IMX_AUDMUX_V2_PTCR_SYN 0
+ IMX_AUDMUX_V2_PTCR_TFSEL(2) 0
+ IMX_AUDMUX_V2_PTCR_TCSEL(2) 0
+ IMX_AUDMUX_V2_PTCR_TFSDIR 0
IMX_AUDMUX_V2_PTCR_TCLKDIR IMX_AUDMUX_V2_PDCR_RXDSEL(2)
>;
};
diff --git a/Documentation/devicetree/bindings/sound/invensense,ics43432.yaml b/Documentation/devicetree/bindings/sound/invensense,ics43432.yaml
index 7bd984817aa9..054f80390b8d 100644
--- a/Documentation/devicetree/bindings/sound/invensense,ics43432.yaml
+++ b/Documentation/devicetree/bindings/sound/invensense,ics43432.yaml
@@ -42,10 +42,10 @@ examples:
compatible = "invensense,ics43432";
port {
- endpoint {
- remote-endpoint = <&i2s1_endpoint>;
- dai-format = "i2s";
- };
+ endpoint {
+ remote-endpoint = <&i2s1_endpoint>;
+ dai-format = "i2s";
+ };
};
};
diff --git a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml
index dc7f4afbb777..69ea611d4250 100644
--- a/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml
+++ b/Documentation/devicetree/bindings/sound/loongson,ls-audio-card.yaml
@@ -79,7 +79,7 @@ examples:
sound-dai = <&i2s>;
};
codec {
- sound-dai = <&es8323>;
+ sound-dai = <&es8323>;
};
};
@@ -104,6 +104,6 @@ examples:
};
codec {
- sound-dai = <&es8388>;
+ sound-dai = <&es8388>;
};
};
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml
index 30f331366566..10df1e7c7eb9 100644
--- a/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt2701-audio.yaml
@@ -32,7 +32,7 @@ properties:
maxItems: 1
clocks:
- minItems: 34
+ minItems: 33
items:
- description: audio infra sys clock
- description: top audio mux 1
@@ -74,7 +74,7 @@ properties:
- description: audio APLL root pd
clock-names:
- minItems: 34
+ minItems: 33
items:
- const: infra_sys_audio_clk
- const: top_audio_mux1_sel
@@ -132,8 +132,10 @@ allOf:
then:
properties:
clocks:
- maxItems: 34
+ minItems: 33
+ maxItems: 33
clock-names:
- maxItems: 34
+ minItems: 33
+ maxItems: 33
additionalProperties: false
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml
index d8993b5d457a..4c66160e9e1b 100644
--- a/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8173-afe-pcm.yaml
@@ -84,15 +84,15 @@ examples:
<&topckgen CLK_TOP_I2S2_M_SEL>,
<&topckgen CLK_TOP_I2S3_M_SEL>,
<&topckgen CLK_TOP_I2S3_B_SEL>;
- clock-names = "infra_sys_audio_clk",
- "top_pdn_audio",
- "top_pdn_aud_intbus",
- "bck0",
- "bck1",
- "i2s0_m",
- "i2s1_m",
- "i2s2_m",
- "i2s3_m",
- "i2s3_b";
- memory-region = <&afe_dma_mem>;
+ clock-names = "infra_sys_audio_clk",
+ "top_pdn_audio",
+ "top_pdn_aud_intbus",
+ "bck0",
+ "bck1",
+ "i2s0_m",
+ "i2s1_m",
+ "i2s2_m",
+ "i2s3_m",
+ "i2s3_b";
+ memory-region = <&afe_dma_mem>;
};
diff --git a/Documentation/devicetree/bindings/sound/mediatek,mt8183-audio.yaml b/Documentation/devicetree/bindings/sound/mediatek,mt8183-audio.yaml
index 031b0fa7b4dc..261247ca2c87 100644
--- a/Documentation/devicetree/bindings/sound/mediatek,mt8183-audio.yaml
+++ b/Documentation/devicetree/bindings/sound/mediatek,mt8183-audio.yaml
@@ -182,47 +182,47 @@ examples:
<&topckgen CLK_TOP_APLL12_DIV4>,
<&topckgen CLK_TOP_APLL12_DIVB>,
<&clk26m>;
- clock-names = "aud_afe_clk",
- "aud_dac_clk",
- "aud_dac_predis_clk",
- "aud_adc_clk",
- "aud_adc_adda6_clk",
- "aud_apll22m_clk",
- "aud_apll24m_clk",
- "aud_apll1_tuner_clk",
- "aud_apll2_tuner_clk",
- "aud_i2s1_bclk_sw",
- "aud_i2s2_bclk_sw",
- "aud_i2s3_bclk_sw",
- "aud_i2s4_bclk_sw",
- "aud_tdm_clk",
- "aud_tml_clk",
- "aud_infra_clk",
- "mtkaif_26m_clk",
- "top_mux_audio",
- "top_mux_aud_intbus",
- "top_syspll_d2_d4",
- "top_mux_aud_1",
- "top_apll1_ck",
- "top_mux_aud_2",
- "top_apll2_ck",
- "top_mux_aud_eng1",
- "top_apll1_d8",
- "top_mux_aud_eng2",
- "top_apll2_d8",
- "top_i2s0_m_sel",
- "top_i2s1_m_sel",
- "top_i2s2_m_sel",
- "top_i2s3_m_sel",
- "top_i2s4_m_sel",
- "top_i2s5_m_sel",
- "top_apll12_div0",
- "top_apll12_div1",
- "top_apll12_div2",
- "top_apll12_div3",
- "top_apll12_div4",
- "top_apll12_divb",
- "top_clk26m_clk";
+ clock-names = "aud_afe_clk",
+ "aud_dac_clk",
+ "aud_dac_predis_clk",
+ "aud_adc_clk",
+ "aud_adc_adda6_clk",
+ "aud_apll22m_clk",
+ "aud_apll24m_clk",
+ "aud_apll1_tuner_clk",
+ "aud_apll2_tuner_clk",
+ "aud_i2s1_bclk_sw",
+ "aud_i2s2_bclk_sw",
+ "aud_i2s3_bclk_sw",
+ "aud_i2s4_bclk_sw",
+ "aud_tdm_clk",
+ "aud_tml_clk",
+ "aud_infra_clk",
+ "mtkaif_26m_clk",
+ "top_mux_audio",
+ "top_mux_aud_intbus",
+ "top_syspll_d2_d4",
+ "top_mux_aud_1",
+ "top_apll1_ck",
+ "top_mux_aud_2",
+ "top_apll2_ck",
+ "top_mux_aud_eng1",
+ "top_apll1_d8",
+ "top_mux_aud_eng2",
+ "top_apll2_d8",
+ "top_i2s0_m_sel",
+ "top_i2s1_m_sel",
+ "top_i2s2_m_sel",
+ "top_i2s3_m_sel",
+ "top_i2s4_m_sel",
+ "top_i2s5_m_sel",
+ "top_apll12_div0",
+ "top_apll12_div1",
+ "top_apll12_div2",
+ "top_apll12_div3",
+ "top_apll12_div4",
+ "top_apll12_divb",
+ "top_clk26m_clk";
};
...
diff --git a/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml b/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml
index 037f21443ad1..d5b358838e2a 100644
--- a/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8186-mt6366-da7219-max98357.yaml
@@ -167,30 +167,30 @@ examples:
"HDMI1", "TX";
hs-playback-dai-link {
- link-name = "I2S0";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&da7219>;
- };
+ link-name = "I2S0";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&da7219>;
+ };
};
hs-capture-dai-link {
- link-name = "I2S1";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&da7219>;
- };
+ link-name = "I2S1";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&da7219>;
+ };
};
spk-dp-playback-dai-link {
- link-name = "I2S3";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&anx_bridge_dp>, <&max98357a>;
- };
+ link-name = "I2S3";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&anx_bridge_dp>, <&max98357a>;
+ };
};
};
diff --git a/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml b/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml
index ed93f18ef985..7f553575c73d 100644
--- a/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8186-mt6366-rt1019-rt5682s.yaml
@@ -171,30 +171,30 @@ examples:
"HDMI1", "TX";
hs-playback-dai-link {
- link-name = "I2S0";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&rt5682s 0>;
- };
+ link-name = "I2S0";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&rt5682s 0>;
+ };
};
hs-capture-dai-link {
- link-name = "I2S1";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&rt5682s 0>;
- };
+ link-name = "I2S1";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&rt5682s 0>;
+ };
};
spk-hdmi-playback-dai-link {
- link-name = "I2S3";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&it6505dptx>, <&rt1019p>;
- };
+ link-name = "I2S3";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&it6505dptx>, <&rt1019p>;
+ };
};
};
diff --git a/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml b/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
index c4e68f31aaab..841aab0178e3 100644
--- a/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8192-mt6359-rt1015-rt5682.yaml
@@ -165,38 +165,38 @@ examples:
"Speakers", "Speaker";
spk-playback-dai-link {
- link-name = "I2S3";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&rt1015p>;
- };
+ link-name = "I2S3";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&rt1015p>;
+ };
};
hs-playback-dai-link {
- link-name = "I2S8";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&rt5682 0>;
- };
+ link-name = "I2S8";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&rt5682 0>;
+ };
};
hs-capture-dai-link {
- link-name = "I2S9";
- dai-format = "i2s";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&rt5682 0>;
- };
+ link-name = "I2S9";
+ dai-format = "i2s";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&rt5682 0>;
+ };
};
displayport-dai-link {
- link-name = "TDM";
- dai-format = "dsp_a";
- codec {
- sound-dai = <&anx_bridge_dp>;
- };
+ link-name = "TDM";
+ dai-format = "dsp_a";
+ codec {
+ sound-dai = <&anx_bridge_dp>;
+ };
};
};
diff --git a/Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml b/Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml
index 356e1feee962..c6bd286780c9 100644
--- a/Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml
+++ b/Documentation/devicetree/bindings/sound/mt8195-mt6359.yaml
@@ -156,46 +156,46 @@ examples:
"Ext Spk", "Speaker";
mm-dai-link {
- link-name = "ETDM1_IN_BE";
- mediatek,clk-provider = "cpu";
+ link-name = "ETDM1_IN_BE";
+ mediatek,clk-provider = "cpu";
};
hs-playback-dai-link {
- link-name = "ETDM1_OUT_BE";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&headset_codec>;
- };
+ link-name = "ETDM1_OUT_BE";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&headset_codec>;
+ };
};
hs-capture-dai-link {
- link-name = "ETDM2_IN_BE";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&headset_codec>;
- };
+ link-name = "ETDM2_IN_BE";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&headset_codec>;
+ };
};
spk-playback-dai-link {
- link-name = "ETDM2_OUT_BE";
- mediatek,clk-provider = "cpu";
- codec {
- sound-dai = <&spk_amplifier>;
- };
+ link-name = "ETDM2_OUT_BE";
+ mediatek,clk-provider = "cpu";
+ codec {
+ sound-dai = <&spk_amplifier>;
+ };
};
hdmi-dai-link {
- link-name = "ETDM3_OUT_BE";
- codec {
- sound-dai = <&hdmi_tx>;
- };
+ link-name = "ETDM3_OUT_BE";
+ codec {
+ sound-dai = <&hdmi_tx>;
+ };
};
displayport-dai-link {
- link-name = "DPTX_BE";
- codec {
- sound-dai = <&dp_tx>;
- };
+ link-name = "DPTX_BE";
+ codec {
+ sound-dai = <&dp_tx>;
+ };
};
};
diff --git a/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
new file mode 100644
index 000000000000..3edab944a4e9
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/nuvoton,nau8360.yaml
@@ -0,0 +1,115 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/nuvoton,nau8360.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton NAU83G60 Stereo Class-D Amplifier with DSP
+
+description:
+ Stereo Class-D Amplifier with DSP and I/V-sense.
+ This device supports I2C.
+
+maintainers:
+ - Neo Chang <YLCHANG2@nuvoton.com>
+
+properties:
+ compatible:
+ enum:
+ - nuvoton,nau8360
+
+ reg:
+ description: The I2C address is determined by the hardware pins.
+ enum: [0x1a, 0x1b, 0x4a, 0x4b]
+
+ "#sound-dai-cells":
+ const: 0
+
+ clocks:
+ maxItems: 1
+
+ clock-names:
+ const: mclk
+
+ firmware-name:
+ description:
+ List of DSP core firmware filenames. Both Left and Right DSP firmware
+ must be provided and loaded. In PBTL mode, the Left DSP must use the
+ same binary file as the Right DSP.
+ items:
+ - description: Left DSP core firmware filename.
+ - description: Right DSP core firmware filename.
+
+ nuvoton,pbtl-enable:
+ type: boolean
+ description: NAU83G60 supports PBTL mode for mono output.
+
+ nuvoton,dac-cur-enable:
+ type: boolean
+ description:
+ Adjust DAC output current to match speaker impedance and prevent
+ hardware damage. +3.2dB when present, 0dB by default.
+
+ nuvoton,dsp-rx-slot-mapping:
+ description:
+ Configures the TDM slot routing for DSP RX functions.
+ Each integer assigns a specific DSP internal function to a TDM slot.
+ The indices of the array correspond to the following hardware functions
+ (0) DACL
+ (1) DACR
+ (2) ANCL
+ (3) ANCR
+ Assigned slots must be unique, except 255 which disables the function.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 4
+ maxItems: 4
+ items:
+ enum: [ 0, 1, 2, 3, 4, 5, 6, 7, 255 ]
+
+ nuvoton,dsp-tx-slot-mapping:
+ description:
+ Configures the TDM slot routing for DSP TX functions.
+ Each integer assigns a specific DSP internal function to a TDM slot.
+ The indices of the array correspond to the following hardware functions
+ (0) AECL (Acoustic echo cancellation left)
+ (1) AECR (Acoustic echo cancellation right)
+ (2) ISNSL (Current sense left)
+ (3) ISNSR (Current sense right)
+ (4) VSNSL (Voltage sense left)
+ (5) VSNSR (Voltage sense right)
+ (6) TJ (Junction temperature)
+ (7) VBAT (Battery voltage)
+ Assigned slots must be unique, except 255 which disables the function.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 8
+ maxItems: 8
+ items:
+ enum: [ 0, 1, 2, 3, 4, 5, 6, 7, 255 ]
+
+required:
+ - compatible
+ - reg
+ - firmware-name
+
+allOf:
+ - $ref: dai-common.yaml#
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ codec@1a {
+ compatible = "nuvoton,nau8360";
+ reg = <0x1a>;
+ #sound-dai-cells = <0>;
+ firmware-name = "NAU83G60.kcs.bin.l", "NAU83G60.kcs.bin.r";
+ nuvoton,dac-cur-enable;
+ nuvoton,pbtl-enable;
+ nuvoton,dsp-rx-slot-mapping = <0 1 2 3>;
+ nuvoton,dsp-tx-slot-mapping = <0 1 2 3 4 5 6 7>;
+ };
+ };
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml
index 4878c424ef03..78587139f155 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6apm-lpass-dais.yaml
@@ -75,11 +75,11 @@ examples:
#size-cells = <0>;
dai@10 {
- reg = <PRIMARY_MI2S_RX>;
- clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT
- LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
- <&q6prmcc LPASS_CLK_ID_MCLK_1
- LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
- clock-names = "bclk", "mclk";
+ reg = <PRIMARY_MI2S_RX>;
+ clocks = <&q6prmcc LPASS_CLK_ID_PRI_MI2S_IBIT
+ LPASS_CLK_ATTRIBUTE_COUPLE_NO>,
+ <&q6prmcc LPASS_CLK_ID_MCLK_1
+ LPASS_CLK_ATTRIBUTE_COUPLE_NO>;
+ clock-names = "bclk", "mclk";
};
};
diff --git a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml
index 3b03e2acd67e..63f53837bd9f 100644
--- a/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,q6dsp-lpass-ports.yaml
@@ -108,10 +108,15 @@ patternProperties:
properties:
reg:
contains:
- # TDM DAI ID range from PRIMARY_TDM_RX_0 - QUINARY_TDM_TX_7
+ # TDM DAI ID range from PRIMARY_TDM_RX_0 - QUINARY_TDM_TX_7 and
+ # AIF_TDM_RX_0 - AIF_TDM_TX_12
items:
- minimum: 24
- maximum: 103
+ oneOf:
+ - minimum: 24
+ maximum: 103
+ - minimum: 179
+ maximum: 204
+
then:
required:
- qcom,tdm-sync-mode
@@ -127,7 +132,8 @@ patternProperties:
contains:
# MI2S DAI ID range PRIMARY_MI2S_RX - QUATERNARY_MI2S_TX and
# QUINARY_MI2S_RX - QUINARY_MI2S_TX and
- # LPI_MI2S_RX_0 - LPI_MI2S_TX_6
+ # LPI_MI2S_RX_0 - LPI_MI2S_TX_6 and
+ # AIF_MI2S_RX_0 - AIF_MI2S_TX_12
items:
oneOf:
- minimum: 16
@@ -136,6 +142,8 @@ patternProperties:
maximum: 128
- minimum: 137
maximum: 152
+ - minimum: 153
+ maximum: 178
then:
required:
- qcom,sd-lines
diff --git a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
index 1536fcd96d68..fc7bccdace0f 100644
--- a/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
+++ b/Documentation/devicetree/bindings/sound/qcom,sm8250.yaml
@@ -54,6 +54,7 @@ properties:
- qcom,sm8250-sndcard
- qcom,sm8450-sndcard
- qcom,x1e80100-sndcard
+ - thundercomm,qcs6490-rubikpi3-sndcard
audio-routing:
$ref: /schemas/types.yaml#/definitions/non-unique-string-array
diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd9378-sdw.yaml b/Documentation/devicetree/bindings/sound/qcom,wcd9378-sdw.yaml
new file mode 100644
index 000000000000..62d85b1dca7f
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/qcom,wcd9378-sdw.yaml
@@ -0,0 +1,105 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/qcom,wcd9378-sdw.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Qualcomm WCD9378 (Tambora) SoundWire codec
+
+maintainers:
+ - Srinivas Kandagatla <srinivas.kandagatla@oss.qualcomm.com>
+
+description:
+ Qualcomm WCD9378 (Tambora) SoundWire codec. The same silicon can be
+ fused for a mobile (non-SDCA) mode or an SDCA-compliant mode; the
+ qcom,sdca-compliant property selects the latter and is required by
+ this driver.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: sdw20217011000
+
+ reg:
+ maxItems: 1
+
+ qcom,sdca-compliant:
+ description:
+ Present when the codec is fused as SDCA-compliant. The mobile
+ (non-SDCA) variant carries the same class-ID compatible and is
+ handled by a different driver.
+ type: boolean
+
+ qcom,port-mapping:
+ description:
+ Each entry maps a slave data port to a master data port.
+ Entries are in order starting from slave port 1; slave port 0
+ is reserved and not represented. SDCA-compliant mode uses 8
+ entries covering DP1..DP8.
+ $ref: /schemas/types.yaml#/definitions/uint32-array
+ minItems: 8
+ maxItems: 8
+
+ reset-gpios:
+ description: GPIO used to release the codec from reset.
+ maxItems: 1
+
+ vdd-buck-supply:
+ description: 1.8 V analog buck supply.
+
+ vdd-rxtx-supply:
+ description: 1.8 V RX/TX supply.
+
+ vdd-io-supply:
+ description: Digital I/O supply.
+
+ vdd-mic-bias-supply:
+ description: Mic bias supply.
+
+ '#sound-dai-cells':
+ const: 1
+
+required:
+ - compatible
+ - reg
+ - qcom,sdca-compliant
+ - qcom,port-mapping
+ - reset-gpios
+ - vdd-buck-supply
+ - vdd-rxtx-supply
+ - vdd-io-supply
+ - vdd-mic-bias-supply
+ - '#sound-dai-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+
+ soundwire@7630000 {
+ reg = <0x07630000 0x10000>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ wcd9378c_sdw: audio-codec@0,3 {
+ compatible = "sdw20217011000";
+ reg = <0 3>;
+
+ qcom,sdca-compliant;
+
+ qcom,port-mapping = <2 2 3 4 5 6 7 8>;
+
+ reset-gpios = <&tlmm 191 GPIO_ACTIVE_LOW>;
+
+ vdd-buck-supply = <&vreg_l15b_1p8>;
+ vdd-rxtx-supply = <&vreg_l15b_1p8>;
+ vdd-io-supply = <&vreg_l18b_1p2>;
+ vdd-mic-bias-supply = <&vreg_bob1>;
+
+ #sound-dai-cells = <1>;
+ };
+ };
+...
diff --git a/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml b/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml
index ae27ae78b1b2..f76aad0d3016 100644
--- a/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml
+++ b/Documentation/devicetree/bindings/sound/realtek,rt5677.yaml
@@ -44,6 +44,14 @@ properties:
'#gpio-cells':
const: 2
+ clocks:
+ items:
+ - description: phandle and clock specifier for the codec MCLK1 input.
+
+ clock-names:
+ items:
+ - const: mclk1
+
realtek,pow-ldo2-gpio:
maxItems: 1
description: CODEC's POW_LDO2 pin.
@@ -127,6 +135,8 @@ examples:
interrupts = <3 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
+ clocks = <&osc>;
+ clock-names = "mclk1";
realtek,pow-ldo2-gpio = <&gpio 3 GPIO_ACTIVE_HIGH>;
realtek,reset-gpio = <&gpio 3 GPIO_ACTIVE_LOW>;
realtek,in1-differential;
diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
index e8a2acb92646..d3516fb1b130 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
+++ b/Documentation/devicetree/bindings/sound/renesas,rsnd.yaml
@@ -432,14 +432,14 @@ examples:
"ssi.1", "ssi.0";
rcar_sound,dvc {
- dvc0: dvc-0 {
- dmas = <&audma0 0xbc>;
- dma-names = "tx";
- };
- dvc1: dvc-1 {
- dmas = <&audma0 0xbe>;
- dma-names = "tx";
- };
+ dvc0: dvc-0 {
+ dmas = <&audma0 0xbc>;
+ dma-names = "tx";
+ };
+ dvc1: dvc-1 {
+ dmas = <&audma0 0xbe>;
+ dma-names = "tx";
+ };
};
rcar_sound,mix {
diff --git a/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml b/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml
index 1394f78281fc..d696fb33d9b8 100644
--- a/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml
+++ b/Documentation/devicetree/bindings/sound/renesas,rz-ssi.yaml
@@ -95,20 +95,20 @@ examples:
ssi0: ssi@10049c00 {
compatible = "renesas,r9a07g044-ssi",
"renesas,rz-ssi";
- reg = <0x10049c00 0x400>;
- interrupts = <GIC_SPI 326 IRQ_TYPE_LEVEL_HIGH>,
- <GIC_SPI 327 IRQ_TYPE_EDGE_RISING>,
- <GIC_SPI 328 IRQ_TYPE_EDGE_RISING>;
- interrupt-names = "int_req", "dma_rx", "dma_tx";
- clocks = <&cpg CPG_MOD R9A07G044_SSI0_PCLK2>,
- <&cpg CPG_MOD R9A07G044_SSI0_PCLK_SFR>,
- <&audio_clk1>,
- <&audio_clk2>;
- clock-names = "ssi", "ssi_sfr", "audio_clk1", "audio_clk2";
- power-domains = <&cpg>;
- resets = <&cpg R9A07G044_SSI0_RST_M2_REG>;
- dmas = <&dmac 0x2655>,
- <&dmac 0x2656>;
- dma-names = "tx", "rx";
- #sound-dai-cells = <0>;
+ reg = <0x10049c00 0x400>;
+ interrupts = <GIC_SPI 326 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 327 IRQ_TYPE_EDGE_RISING>,
+ <GIC_SPI 328 IRQ_TYPE_EDGE_RISING>;
+ interrupt-names = "int_req", "dma_rx", "dma_tx";
+ clocks = <&cpg CPG_MOD R9A07G044_SSI0_PCLK2>,
+ <&cpg CPG_MOD R9A07G044_SSI0_PCLK_SFR>,
+ <&audio_clk1>,
+ <&audio_clk2>;
+ clock-names = "ssi", "ssi_sfr", "audio_clk1", "audio_clk2";
+ power-domains = <&cpg>;
+ resets = <&cpg R9A07G044_SSI0_RST_M2_REG>;
+ dmas = <&dmac 0x2655>,
+ <&dmac 0x2656>;
+ dma-names = "tx", "rx";
+ #sound-dai-cells = <0>;
};
diff --git a/Documentation/devicetree/bindings/sound/stericsson,ux500-msp-i2s.yaml b/Documentation/devicetree/bindings/sound/stericsson,ux500-msp-i2s.yaml
new file mode 100644
index 000000000000..bdfcd5376cfe
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/stericsson,ux500-msp-i2s.yaml
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/stericsson,ux500-msp-i2s.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ST-Ericsson Ux500 Multichannel Serial Port
+
+maintainers:
+ - Linus Walleij <linusw@kernel.org>
+
+description:
+ The Ux500 Multichannel Serial Port (MSP) is a synchronous serial audio
+ interface supporting I2S and PCM/TDM protocols. Data is transferred to and
+ from memory using the Ux500 DMA controller.
+
+allOf:
+ - $ref: dai-common.yaml#
+
+properties:
+ compatible:
+ const: stericsson,ux500-msp-i2s
+
+ reg:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 1
+
+ v-ape-supply:
+ description: Analog power engine supply
+
+ dmas:
+ minItems: 1
+ maxItems: 2
+
+ dma-names:
+ minItems: 1
+ maxItems: 2
+ items:
+ enum: [ rx, tx ]
+
+ clocks:
+ items:
+ - description: MSP functional clock
+ - description: MSP APB bus clock
+
+ clock-names:
+ items:
+ - const: msp
+ - const: apb_pclk
+
+ resets:
+ maxItems: 1
+
+ '#sound-dai-cells':
+ const: 0
+
+ port:
+ $ref: audio-graph-port.yaml#
+ unevaluatedProperties: false
+
+required:
+ - compatible
+ - reg
+ - interrupts
+ - v-ape-supply
+ - dmas
+ - dma-names
+ - clocks
+ - clock-names
+ - resets
+ - '#sound-dai-cells'
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/interrupt-controller/irq.h>
+ #include <dt-bindings/reset/stericsson,db8500-prcc-reset.h>
+
+ msp@80123000 {
+ compatible = "stericsson,ux500-msp-i2s";
+ reg = <0x80123000 0x1000>;
+ interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
+ v-ape-supply = <&db8500_vape_reg>;
+ dmas = <&dma 31 0 0x12>, <&dma 31 0 0x10>;
+ dma-names = "rx", "tx";
+ clocks = <&prcc_kclk 1 3>, <&prcc_pclk 1 3>;
+ clock-names = "msp", "apb_pclk";
+ resets = <&prcc_reset DB8500_PRCC_1 DB8500_PRCC_1_RESET_MSP0>;
+ #sound-dai-cells = <0>;
+ };
+
+...
diff --git a/Documentation/devicetree/bindings/sound/ti,da830-evm-audio.yaml b/Documentation/devicetree/bindings/sound/ti,da830-evm-audio.yaml
new file mode 100644
index 000000000000..b4a1cf67582b
--- /dev/null
+++ b/Documentation/devicetree/bindings/sound/ti,da830-evm-audio.yaml
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/sound/ti,da830-evm-audio.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments SoC audio setups with TLV320AIC3X Codec
+
+maintainers:
+ - Eduard Bostina <egbostina@gmail.com>
+
+properties:
+ compatible:
+ const: ti,da830-evm-audio
+
+ ti,model:
+ $ref: /schemas/types.yaml#/definitions/string
+ description: The user-visible name of this sound complex.
+
+ ti,audio-codec:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of the TLV320AIC3x audio codec
+
+ ti,mcasp-controller:
+ $ref: /schemas/types.yaml#/definitions/phandle
+ description: The phandle of the McASP controller
+
+ ti,audio-routing:
+ $ref: /schemas/types.yaml#/definitions/non-unique-string-array
+ description:
+ A list of the connections between audio components. Each entry is a
+ pair of strings, the first being the connection's sink, the second
+ being the connection's source. Valid names for sources and sinks
+ are the codec's pins, and the jacks on the board (Headphone Jack,
+ Line Out, Mic Jack, Line In).
+
+ ti,codec-clock-rate:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description: The Codec Clock rate (in Hz) applied to the Codec.
+
+ clocks:
+ maxItems: 1
+ description: Reference to the master clock
+
+ clock-names:
+ const: mclk
+
+required:
+ - compatible
+ - ti,model
+ - ti,audio-codec
+ - ti,mcasp-controller
+ - ti,audio-routing
+
+anyOf:
+ - required:
+ - ti,codec-clock-rate
+ - required:
+ - clocks
+
+additionalProperties: false
+
+examples:
+ - |
+ sound {
+ compatible = "ti,da830-evm-audio";
+ ti,model = "DA830 EVM";
+ ti,audio-codec = <&tlv320aic3x>;
+ ti,mcasp-controller = <&mcasp1>;
+ ti,codec-clock-rate = <12000000>;
+ ti,audio-routing =
+ "Headphone Jack", "HPLOUT",
+ "Headphone Jack", "HPROUT",
+ "Line Out", "LLOUT",
+ "Line Out", "RLOUT",
+ "MIC3L", "Mic Bias 2V",
+ "MIC3R", "Mic Bias 2V",
+ "Mic Bias 2V", "Mic Jack",
+ "LINE1L", "Line In",
+ "LINE2L", "Line In",
+ "LINE1R", "Line In",
+ "LINE2R", "Line In";
+ };
diff --git a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
index aa5a317b5a36..50c7792b24f8 100644
--- a/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tas2781.yaml
@@ -219,9 +219,7 @@ allOf:
- if:
properties:
compatible:
- contains:
- enum:
- - ti,tas2781
+ const: ti,tas2781
then:
properties:
reg:
diff --git a/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml
index c2c2835a9e1d..f2ab274b02cd 100644
--- a/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tas5805m.yaml
@@ -45,13 +45,13 @@ examples:
#address-cells = <1>;
#size-cells = <0>;
tas5805m: tas5805m@2c {
- reg = <0x2c>;
- compatible = "ti,tas5805m";
+ reg = <0x2c>;
+ compatible = "ti,tas5805m";
- pvdd-supply = <&audiopwr>;
- pdn-gpios = <&tlmm 160 0>;
+ pvdd-supply = <&audiopwr>;
+ pdn-gpios = <&tlmm 160 0>;
- ti,dsp-config-name = "mono_pbtl_48khz";
+ ti,dsp-config-name = "mono_pbtl_48khz";
};
};
...
diff --git a/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml b/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml
index 10299064cbc6..3bdaab1e5084 100644
--- a/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tlv320dac3100.yaml
@@ -121,6 +121,6 @@ examples:
AVDD-supply = <&regulator>;
IOVDD-supply = <&regulator>;
DVDD-supply = <&regulator>;
- };
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/ti,tpa6130a2.yaml b/Documentation/devicetree/bindings/sound/ti,tpa6130a2.yaml
index a42bf9bde694..5634b601ce65 100644
--- a/Documentation/devicetree/bindings/sound/ti,tpa6130a2.yaml
+++ b/Documentation/devicetree/bindings/sound/ti,tpa6130a2.yaml
@@ -50,6 +50,6 @@ examples:
reg = <0x60>;
Vdd-supply = <&vmmc2>;
power-gpio = <&gpio4 2 GPIO_ACTIVE_HIGH>;
- };
+ };
};
diff --git a/Documentation/devicetree/bindings/sound/ux500-mop500.txt b/Documentation/devicetree/bindings/sound/ux500-mop500.txt
deleted file mode 100644
index 48e071c96b46..000000000000
--- a/Documentation/devicetree/bindings/sound/ux500-mop500.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-* MOP500 Audio Machine Driver
-
-This node is responsible for linking together all ux500 Audio Driver components.
-
-Required properties:
- - compatible : "stericsson,snd-soc-mop500"
-
-Non-standard properties:
- - stericsson,cpu-dai : Phandle to the CPU-side DAI
- - stericsson,audio-codec : Phandle to the Audio CODEC
- - stericsson,card-name : Over-ride default card name
-
-Example:
-
- sound {
- compatible = "stericsson,snd-soc-mop500";
-
- stericsson,cpu-dai = <&msp1 &msp3>;
- stericsson,audio-codec = <&codec>;
- };
-
- msp1: msp@80124000 {
- compatible = "stericsson,ux500-msp-i2s";
- reg = <0x80124000 0x1000>;
- interrupts = <0 62 0x4>;
- v-ape-supply = <&db8500_vape_reg>;
- };
-
- msp3: msp@80125000 {
- compatible = "stericsson,ux500-msp-i2s";
- reg = <0x80125000 0x1000>;
- interrupts = <0 62 0x4>;
- v-ape-supply = <&db8500_vape_reg>;
- };
-
- codec: ab8500-codec {
- compatible = "stericsson,ab8500-codec";
- stericsson,earpeice-cmv = <950>; /* Units in mV. */
- };
diff --git a/Documentation/devicetree/bindings/sound/ux500-msp.txt b/Documentation/devicetree/bindings/sound/ux500-msp.txt
deleted file mode 100644
index 7dd1b96160f5..000000000000
--- a/Documentation/devicetree/bindings/sound/ux500-msp.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-* ux500 MSP (CPU-side Digital Audio Interface)
-
-Required properties:
- - compatible :"stericsson,ux500-msp-i2s"
- - reg : Physical base address and length of the device's registers.
-
-Optional properties:
- - interrupts : The interrupt output from the device.
- - <name>-supply : Phandle to the regulator <name> supply
-
-Example:
-
- sound {
- compatible = "stericsson,snd-soc-mop500";
-
- stericsson,platform-pcm-dma = <&pcm>;
- stericsson,cpu-dai = <&msp1 &msp3>;
- stericsson,audio-codec = <&codec>;
- };
-
- pcm: ux500-pcm {
- compatible = "stericsson,ux500-pcm";
- };
-
- msp1: msp@80124000 {
- compatible = "stericsson,ux500-msp-i2s";
- reg = <0x80124000 0x1000>;
- interrupts = <0 62 0x4>;
- v-ape-supply = <&db8500_vape_reg>;
- };
-
- msp3: msp@80125000 {
- compatible = "stericsson,ux500-msp-i2s";
- reg = <0x80125000 0x1000>;
- interrupts = <0 62 0x4>;
- v-ape-supply = <&db8500_vape_reg>;
- };
-
- codec: ab8500-codec {
- compatible = "stericsson,ab8500-codec";
- stericsson,earpeice-cmv = <950>; /* Units in mV. */
- };
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index cd55cfb8302d..838def27c9cf 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -561,6 +561,8 @@ patternProperties:
description: Seiko Epson Corp.
"^esp,.*":
description: Espressif Systems Co. Ltd.
+ "^esstech,.*":
+ description: ESS Technology, Inc.
"^est,.*":
description: ESTeem Wireless Modems
"^eswin,.*":
diff --git a/MAINTAINERS b/MAINTAINERS
index 35ae53312c6b..87194b5ce81a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -767,6 +767,14 @@ F: Documentation/devicetree/bindings/phy/airoha,en7581-pcie-phy.yaml
F: drivers/phy/phy-airoha-pcie-regs.h
F: drivers/phy/phy-airoha-pcie.c
+AIROHA SOUND DRIVER
+M: Christian Marangi <ansuelsmth@gmail.com>
+L: linux-sound@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/sound/airoha,an7581-afe.yaml
+F: Documentation/devicetree/bindings/sound/airoha,an7581-wm8960.yaml
+F: sound/soc/mediatek/an7581/*
+
AIROHA SPI SNFI DRIVER
M: Lorenzo Bianconi <lorenzo@kernel.org>
M: Ray Liu <ray.liu@airoha.com>
@@ -9786,6 +9794,13 @@ F: include/linux/errseq.h
F: lib/errseq.c
F: lib/tests/errseq_kunit.c
+ES9039Q2M AUDIO CODEC DRIVER
+M: Karl Asseily <karl@asseily.com>
+L: linux-sound@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/sound/esstech,es9039q2m.yaml
+F: sound/soc/codecs/es9039q2m.c
+
ESD CAN NETWORK DRIVERS
M: Stefan Mätje <stefan.maetje@esd.eu>
R: socketcan@esd.eu
@@ -30471,9 +30486,12 @@ F: tools/testing/selftests/cgroup/test_zswap.c
SENARYTECH AUDIO CODEC DRIVER
M: bo liu <bo.liu@senarytech.com>
+M: qianghua.wang <qianghua.wang@senarytech.com>
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
F: sound/hda/codecs/senarytech.c
+F: sound/soc/codecs/sn624x*
+F: sound/soc/sdw_utils/soc_sdw_senary*
ZTE DINGHAI ETHERNET DRIVER
M: Junyang Han <han.junyang@zte.com.cn>
diff --git a/drivers/firmware/cirrus/cs_dsp.c b/drivers/firmware/cirrus/cs_dsp.c
index bb93b24c659d..04015280ac5d 100644
--- a/drivers/firmware/cirrus/cs_dsp.c
+++ b/drivers/firmware/cirrus/cs_dsp.c
@@ -3505,7 +3505,7 @@ EXPORT_SYMBOL_NS_GPL(cs_dsp_chunk_write, "FW_CS_DSP");
*
* As cs_dsp_chunk_write only writes data when a whole DSP word is ready to
* be written out it is possible that some data will remain in the cache, this
- * function will pad that data with zeros upto a whole DSP word and write out.
+ * function will pad that data with zeros up to a whole DSP word and write out.
*
* Return: Zero for success, a negative number on error.
*/
diff --git a/drivers/gpib/fmh_gpib/fmh_gpib.c b/drivers/gpib/fmh_gpib/fmh_gpib.c
index 5e10e9353fed..4ab9b0a317fd 100644
--- a/drivers/gpib/fmh_gpib/fmh_gpib.c
+++ b/drivers/gpib/fmh_gpib/fmh_gpib.c
@@ -1458,11 +1458,15 @@ static int fmh_gpib_attach_impl(struct gpib_board *board, const struct gpib_boar
e_priv->irq = irq;
if (acquire_dma) {
- e_priv->dma_channel = dma_request_slave_channel(board->dev, "rxtx");
- if (!e_priv->dma_channel) {
+ struct dma_chan *tmp_chan;
+
+ tmp_chan = dma_request_chan(board->dev, "rxtx");
+ retval = PTR_ERR_OR_ZERO(tmp_chan);
+ if (retval) {
dev_err(board->dev, "failed to acquire dma channel \"rxtx\".\n");
- return -EIO;
+ return retval;
}
+ e_priv->dma_channel = tmp_chan;
}
/*
* in the future we might want to know the half-fifo size
diff --git a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
index 3a99703dbc4a..e6c792d9312f 100644
--- a/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
+++ b/include/dt-bindings/sound/qcom,q6dsp-lpass-ports.h
@@ -156,6 +156,58 @@
#define LPI_MI2S_TX_5 150
#define LPI_MI2S_RX_6 151
#define LPI_MI2S_TX_6 152
+#define AIF_MI2S_RX_0 153
+#define AIF_MI2S_TX_0 154
+#define AIF_MI2S_RX_1 155
+#define AIF_MI2S_TX_1 156
+#define AIF_MI2S_RX_2 157
+#define AIF_MI2S_TX_2 158
+#define AIF_MI2S_RX_3 159
+#define AIF_MI2S_TX_3 160
+#define AIF_MI2S_RX_4 161
+#define AIF_MI2S_TX_4 162
+#define AIF_MI2S_RX_5 163
+#define AIF_MI2S_TX_5 164
+#define AIF_MI2S_RX_6 165
+#define AIF_MI2S_TX_6 166
+#define AIF_MI2S_RX_7 167
+#define AIF_MI2S_TX_7 168
+#define AIF_MI2S_RX_8 169
+#define AIF_MI2S_TX_8 170
+#define AIF_MI2S_RX_9 171
+#define AIF_MI2S_TX_9 172
+#define AIF_MI2S_RX_10 173
+#define AIF_MI2S_TX_10 174
+#define AIF_MI2S_RX_11 175
+#define AIF_MI2S_TX_11 176
+#define AIF_MI2S_RX_12 177
+#define AIF_MI2S_TX_12 178
+#define AIF_TDM_RX_0 179
+#define AIF_TDM_TX_0 180
+#define AIF_TDM_RX_1 181
+#define AIF_TDM_TX_1 182
+#define AIF_TDM_RX_2 183
+#define AIF_TDM_TX_2 184
+#define AIF_TDM_RX_3 185
+#define AIF_TDM_TX_3 186
+#define AIF_TDM_RX_4 187
+#define AIF_TDM_TX_4 188
+#define AIF_TDM_RX_5 189
+#define AIF_TDM_TX_5 190
+#define AIF_TDM_RX_6 191
+#define AIF_TDM_TX_6 192
+#define AIF_TDM_RX_7 193
+#define AIF_TDM_TX_7 194
+#define AIF_TDM_RX_8 195
+#define AIF_TDM_TX_8 196
+#define AIF_TDM_RX_9 197
+#define AIF_TDM_TX_9 198
+#define AIF_TDM_RX_10 199
+#define AIF_TDM_TX_10 200
+#define AIF_TDM_RX_11 201
+#define AIF_TDM_TX_11 202
+#define AIF_TDM_RX_12 203
+#define AIF_TDM_TX_12 204
#define LPASS_CLK_ID_PRI_MI2S_IBIT 1
#define LPASS_CLK_ID_PRI_MI2S_EBIT 2
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index fe33a20abc61..f322e501ff62 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -1760,15 +1760,6 @@ void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
#define dma_request_channel(mask, x, y) \
__dma_request_channel(&(mask), x, y, NULL)
-/* Deprecated, please use dma_request_chan() directly */
-static inline struct dma_chan * __deprecated
-dma_request_slave_channel(struct device *dev, const char *name)
-{
- struct dma_chan *ch = dma_request_chan(dev, name);
-
- return IS_ERR(ch) ? NULL : ch;
-}
-
static inline struct dma_chan
*dma_request_slave_channel_compat(const dma_cap_mask_t mask,
dma_filter_fn fn, void *fn_param,
diff --git a/include/linux/firmware/cirrus/cs_dsp.h b/include/linux/firmware/cirrus/cs_dsp.h
index 4e3baa557068..6aa1e1b2b4a5 100644
--- a/include/linux/firmware/cirrus/cs_dsp.h
+++ b/include/linux/firmware/cirrus/cs_dsp.h
@@ -96,7 +96,7 @@ struct cs_dsp_alg_region {
struct cs_dsp_coeff_ctl {
struct list_head list;
struct cs_dsp *dsp;
- void *cache;
+ void *cache __counted_by_ptr(len);
const char *fw_name;
/* Subname is needed to match with firmware */
const char *subname;
diff --git a/include/sound/sdca.h b/include/sound/sdca.h
index 2bdf4e333e04..db2672b3aae2 100644
--- a/include/sound/sdca.h
+++ b/include/sound/sdca.h
@@ -60,20 +60,16 @@ enum sdca_quirk {
SDCA_QUIRKS_SKIP_FUNC_TYPE_PATCHING,
};
-#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA)
+#if IS_ENABLED(CONFIG_SND_SOC_SDCA)
-void sdca_lookup_functions(struct sdw_slave *slave);
void sdca_lookup_swft(struct sdw_slave *slave);
-void sdca_lookup_interface_revision(struct sdw_slave *slave);
bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk);
int sdca_dev_register_functions(struct sdw_slave *slave);
void sdca_dev_unregister_functions(struct sdw_slave *slave);
#else
-static inline void sdca_lookup_functions(struct sdw_slave *slave) {}
static inline void sdca_lookup_swft(struct sdw_slave *slave) {}
-static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {}
static inline bool sdca_device_quirk_match(struct sdw_slave *slave, enum sdca_quirk quirk)
{
return false;
@@ -88,4 +84,21 @@ static inline void sdca_dev_unregister_functions(struct sdw_slave *slave) {}
#endif
+/*
+ * Called from the SoundWire bus during peripheral enumeration; gated on
+ * ACPI to avoid a soundwire_bus <-> snd_soc_sdca module cycle on DT builds
+ * (where the bodies are stubs anyway).
+ */
+#if IS_ENABLED(CONFIG_ACPI) && IS_ENABLED(CONFIG_SND_SOC_SDCA)
+
+void sdca_lookup_functions(struct sdw_slave *slave);
+void sdca_lookup_interface_revision(struct sdw_slave *slave);
+
+#else
+
+static inline void sdca_lookup_functions(struct sdw_slave *slave) {}
+static inline void sdca_lookup_interface_revision(struct sdw_slave *slave) {}
+
+#endif
+
#endif
diff --git a/include/sound/sdca_class.h b/include/sound/sdca_class.h
new file mode 100644
index 000000000000..794a756472cd
--- /dev/null
+++ b/include/sound/sdca_class.h
@@ -0,0 +1,70 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * The MIPI SDCA specification is available for public downloads at
+ * https://www.mipi.org/mipi-sdca-v1-0-download
+ *
+ * Copyright (C) 2025 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef __SDCA_CLASS_H__
+#define __SDCA_CLASS_H__
+
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+
+struct device;
+struct regmap;
+struct sdw_slave;
+struct sdca_function_data;
+
+/**
+ * struct sdca_class_ops - optional codec-provided class callbacks
+ * @populate_function: fill @function (entities, clusters, init_table, ...)
+ * from static tables in place of sdca_parse_function() on
+ * DT/non-DisCo platforms. Called once per SDCA function with
+ * @dev pointing at that function's device. Pure data source;
+ * performs no bus I/O. Return 0 on success or a negative errno.
+ */
+struct sdca_class_ops {
+ int (*populate_function)(struct device *dev,
+ struct sdca_function_data *function);
+};
+
+struct sdca_class_drv {
+ struct device *dev;
+ struct regmap *dev_regmap;
+ struct sdw_slave *sdw;
+
+ struct sdca_interrupt_info *irq_info;
+
+ const struct sdca_class_ops *ops;
+
+ struct mutex regmap_lock;
+ /* Serialise function initialisations */
+ struct mutex init_lock;
+ struct work_struct boot_work;
+};
+
+/* Library helpers used by codec-specific SDCA SoundWire drivers. */
+int sdca_class_probe(struct sdw_slave *sdw,
+ struct sdca_class_drv *drv,
+ const struct sdca_class_ops *ops);
+void sdca_class_remove(struct sdca_class_drv *drv);
+
+/*
+ * PM helpers. Codec drivers embed sdca_class_drv in their own priv,
+ * own dev_set_drvdata(), and compose these into their own dev_pm_ops:
+ *
+ * static int wcd_runtime_suspend(struct device *dev) {
+ * struct wcd_priv *priv = dev_get_drvdata(dev);
+ * return sdca_class_runtime_suspend(&priv->class);
+ * }
+ */
+int sdca_class_runtime_suspend(struct sdca_class_drv *drv);
+int sdca_class_runtime_resume(struct sdca_class_drv *drv);
+int sdca_class_system_suspend(struct sdca_class_drv *drv);
+int sdca_class_system_resume(struct sdca_class_drv *drv);
+
+#endif /* __SDCA_CLASS_H__ */
diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h
index bd8c3a033577..68faec56970e 100644
--- a/include/sound/simple_card_utils.h
+++ b/include/sound/simple_card_utils.h
@@ -219,12 +219,14 @@ static inline int simple_util_parse_aux_devs(struct simple_util_priv *priv, char
int simple_util_init_jack(struct snd_soc_card *card,
struct simple_util_jack *sjack,
int is_hp, char *prefix, char *pin);
+void simple_util_remove_jack(struct simple_util_jack *sjack);
int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix);
int simple_util_init_priv(struct simple_util_priv *priv,
struct link_info *li);
void simple_util_remove(struct platform_device *pdev);
int graph_util_card_probe(struct snd_soc_card *card);
+int graph_util_card_remove(struct snd_soc_card *card);
int graph_util_is_ports0(struct device_node *port);
int graph_util_parse_dai(struct simple_util_priv *priv, struct device_node *ep,
struct snd_soc_dai_link_component *dlc, int *is_single_link);
diff --git a/include/sound/soc-component.h b/include/sound/soc-component.h
index c49b59630101..6c1acc984ecb 100644
--- a/include/sound/soc-component.h
+++ b/include/sound/soc-component.h
@@ -258,10 +258,16 @@ struct snd_soc_component {
void *priv;
};
-#define for_each_component_dais(component, dai)\
- list_for_each_entry(dai, &(component)->dai_list, list)
-#define for_each_component_dais_safe(component, dai, _dai)\
- list_for_each_entry_safe(dai, _dai, &(component)->dai_list, list)
+#define for_each_component_dais(component, dai) \
+ for (dai = snd_soc_dai_from_list((component)->dai_list.next); \
+ snd_soc_dai_to_list(dai) != &(component)->dai_list; \
+ dai = snd_soc_dai_from_list(snd_soc_dai_to_list(dai)->next))
+
+#define for_each_component_dais_safe(component, dai, _dai) \
+ for (dai = snd_soc_dai_from_list((component)->dai_list.next), \
+ _dai = snd_soc_dai_from_list(snd_soc_dai_to_list(dai)->next); \
+ snd_soc_dai_to_list(dai) != &(component)->dai_list; \
+ dai = _dai, _dai = snd_soc_dai_from_list(snd_soc_dai_to_list(_dai)->next))
/**
* snd_soc_component_to_dapm() - Returns the DAPM context associated with a
diff --git a/include/sound/soc-dai.h b/include/sound/soc-dai.h
index ba3ae56c6b06..c044909ef10d 100644
--- a/include/sound/soc-dai.h
+++ b/include/sound/soc-dai.h
@@ -212,8 +212,11 @@ int snd_soc_dai_get_channel_map(const struct snd_soc_dai *dai,
unsigned int *tx_num, unsigned int *tx_slot,
unsigned int *rx_num, unsigned int *rx_slot);
+struct snd_soc_component *snd_soc_dai_to_component(const struct snd_soc_dai *dai);
+struct snd_soc_dai_driver *snd_soc_dai_to_driver(const struct snd_soc_dai *dai);
int snd_soc_dai_is_dummy(const struct snd_soc_dai *dai);
-
+int snd_soc_dai_add_controls(struct snd_soc_dai *dai,
+ const struct snd_kcontrol_new *controls, int num_controls);
int snd_soc_dai_hw_params(struct snd_soc_dai *dai,
struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params);
@@ -228,18 +231,7 @@ void snd_soc_dai_suspend(struct snd_soc_dai *dai);
void snd_soc_dai_resume(struct snd_soc_dai *dai);
int snd_soc_dai_compress_new(struct snd_soc_dai *dai, struct snd_soc_pcm_runtime *rtd);
bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int stream);
-void snd_soc_dai_action(struct snd_soc_dai *dai,
- int stream, int action);
-static inline void snd_soc_dai_activate(struct snd_soc_dai *dai,
- int stream)
-{
- snd_soc_dai_action(dai, stream, 1);
-}
-static inline void snd_soc_dai_deactivate(struct snd_soc_dai *dai,
- int stream)
-{
- snd_soc_dai_action(dai, stream, -1);
-}
+
int snd_soc_dai_active(const struct snd_soc_dai *dai);
int snd_soc_pcm_dai_probe(struct snd_soc_pcm_runtime *rtd, int order);
@@ -277,7 +269,12 @@ int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai,
struct snd_compr_stream *cstream,
struct snd_compr_metadata *metadata);
-const char *snd_soc_dai_name_get(const struct snd_soc_dai *dai);
+int snd_soc_dai_matches_args(const struct snd_soc_dai *dai,
+ const struct of_phandle_args *args2);
+int snd_soc_dai_matches_dlc(struct snd_soc_dai *dai,
+ const struct snd_soc_dai_link_component *dlc);
+const char *snd_soc_dai_name(const struct snd_soc_dai *dai);
+int snd_soc_dai_id(const struct snd_soc_dai *dai);
struct snd_soc_dai_ops {
/* DAI driver callbacks */
@@ -489,69 +486,51 @@ struct snd_soc_dai {
void *priv;
};
+const struct snd_soc_pcm_stream *
+snd_soc_dai_pcm_stream_get_i(const struct snd_soc_dai *dai, int stream);
static inline const struct snd_soc_pcm_stream *
-snd_soc_dai_get_pcm_stream(const struct snd_soc_dai *dai, int stream)
-{
- return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
- &dai->driver->playback : &dai->driver->capture;
-}
-
-#define snd_soc_dai_get_widget_playback(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_PLAYBACK)
-#define snd_soc_dai_get_widget_capture(dai) snd_soc_dai_get_widget(dai, SNDRV_PCM_STREAM_CAPTURE)
-static inline
-struct snd_soc_dapm_widget *snd_soc_dai_get_widget(struct snd_soc_dai *dai, int stream)
-{
- return dai->stream[stream].widget;
-}
-
-#define snd_soc_dai_set_widget_playback(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_PLAYBACK, widget)
-#define snd_soc_dai_set_widget_capture(dai, widget) snd_soc_dai_set_widget(dai, SNDRV_PCM_STREAM_CAPTURE, widget)
-static inline
-void snd_soc_dai_set_widget(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget *widget)
-{
- dai->stream[stream].widget = widget;
-}
-
-#define snd_soc_dai_dma_data_get_playback(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_PLAYBACK)
-#define snd_soc_dai_dma_data_get_capture(dai) snd_soc_dai_dma_data_get(dai, SNDRV_PCM_STREAM_CAPTURE)
-#define snd_soc_dai_get_dma_data(dai, ss) snd_soc_dai_dma_data_get(dai, ss->stream)
-static inline void *snd_soc_dai_dma_data_get(const struct snd_soc_dai *dai, int stream)
-{
- return dai->stream[stream].dma_data;
-}
-
-#define snd_soc_dai_dma_data_set_playback(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_PLAYBACK, data)
-#define snd_soc_dai_dma_data_set_capture(dai, data) snd_soc_dai_dma_data_set(dai, SNDRV_PCM_STREAM_CAPTURE, data)
-#define snd_soc_dai_set_dma_data(dai, ss, data) snd_soc_dai_dma_data_set(dai, ss->stream, data)
-static inline void snd_soc_dai_dma_data_set(struct snd_soc_dai *dai, int stream, void *data)
+snd_soc_dai_pcm_stream_get_s(const struct snd_soc_dai *dai, struct snd_pcm_substream *substream)
{
- dai->stream[stream].dma_data = data;
+ return snd_soc_dai_pcm_stream_get_i(dai, substream->stream);
}
-
-static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, void *playback, void *capture)
+#define snd_soc_dai_pcm_stream_get(dai, x) _Generic((x), \
+ int : snd_soc_dai_pcm_stream_get_i, \
+ struct snd_pcm_substream * : snd_soc_dai_pcm_stream_get_s)(dai, x)
+
+#define snd_soc_dai_stream_widget_get_playback(dai) snd_soc_dai_stream_widget_get(dai, SNDRV_PCM_STREAM_PLAYBACK)
+#define snd_soc_dai_stream_widget_get_capture(dai) snd_soc_dai_stream_widget_get(dai, SNDRV_PCM_STREAM_CAPTURE)
+struct snd_soc_dapm_widget *snd_soc_dai_stream_widget_get(struct snd_soc_dai *dai, int stream);
+
+#define snd_soc_dai_stream_widget_set_playback(dai, widget) snd_soc_dai_stream_widget_set(dai, SNDRV_PCM_STREAM_PLAYBACK, widget)
+#define snd_soc_dai_stream_widget_set_capture(dai, widget) snd_soc_dai_stream_widget_set(dai, SNDRV_PCM_STREAM_CAPTURE, widget)
+void snd_soc_dai_stream_widget_set(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget *widget);
+
+#define snd_soc_dai_stream_dma_data_get_playback(dai) snd_soc_dai_stream_dma_data_get(dai, SNDRV_PCM_STREAM_PLAYBACK)
+#define snd_soc_dai_stream_dma_data_get_capture(dai) snd_soc_dai_stream_dma_data_get(dai, SNDRV_PCM_STREAM_CAPTURE)
+void *snd_soc_dai_stream_dma_data_get_i(const struct snd_soc_dai *dai, int stream);
+static inline void *snd_soc_dai_stream_dma_data_get_s(const struct snd_soc_dai *dai, struct snd_pcm_substream *substream)
{
- snd_soc_dai_dma_data_set_playback(dai, playback);
- snd_soc_dai_dma_data_set_capture(dai, capture);
+ return snd_soc_dai_stream_dma_data_get_i(dai, substream->stream);
}
-
-static inline unsigned int snd_soc_dai_tdm_mask_get(const struct snd_soc_dai *dai,
- int stream)
+#define snd_soc_dai_stream_dma_data_get(dai, x) _Generic((x), \
+ int : snd_soc_dai_stream_dma_data_get_i, \
+ struct snd_pcm_substream * : snd_soc_dai_stream_dma_data_get_s)(dai, x)
+
+#define snd_soc_dai_stream_dma_data_set_playback(dai, data) snd_soc_dai_stream_dma_data_set(dai, SNDRV_PCM_STREAM_PLAYBACK, data)
+#define snd_soc_dai_stream_dma_data_set_capture(dai, data) snd_soc_dai_stream_dma_data_set(dai, SNDRV_PCM_STREAM_CAPTURE, data)
+void snd_soc_dai_stream_dma_data_set_i(struct snd_soc_dai *dai, int stream, void *data);
+static inline void snd_soc_dai_stream_dma_data_set_s(struct snd_soc_dai *dai, struct snd_pcm_substream *substream, void *data)
{
- return dai->stream[stream].tdm_mask;
+ snd_soc_dai_stream_dma_data_set_i(dai, substream->stream, data);
}
+#define snd_soc_dai_stream_dma_data_set(dai, x, data) _Generic((x), \
+ int : snd_soc_dai_stream_dma_data_set_i, \
+ struct snd_pcm_substream * : snd_soc_dai_stream_dma_data_set_s)(dai, x, data)
-static inline void snd_soc_dai_tdm_mask_set(struct snd_soc_dai *dai, int stream,
- unsigned int tdm_mask)
-{
- dai->stream[stream].tdm_mask = tdm_mask;
-}
+unsigned int snd_soc_dai_stream_tdm_mask_get(const struct snd_soc_dai *dai, int stream);
+void snd_soc_dai_stream_tdm_mask_set(struct snd_soc_dai *dai, int stream, unsigned int tdm_mask);
-static inline unsigned int snd_soc_dai_stream_active(const struct snd_soc_dai *dai,
- int stream)
-{
- /* see snd_soc_dai_action() for setup */
- return dai->stream[stream].active;
-}
+unsigned int snd_soc_dai_stream_active(const struct snd_soc_dai *dai, int stream);
static inline void snd_soc_dai_set_drvdata(struct snd_soc_dai *dai,
void *data)
@@ -564,45 +543,43 @@ static inline void *snd_soc_dai_get_drvdata(struct snd_soc_dai *dai)
return dev_get_drvdata(dai->dev);
}
-/**
- * snd_soc_dai_set_stream() - Configures a DAI for stream operation
- * @dai: DAI
- * @stream: STREAM (opaque structure depending on DAI type)
- * @direction: Stream direction(Playback/Capture)
- * Some subsystems, such as SoundWire, don't have a notion of direction and we reuse
- * the ASoC stream direction to configure sink/source ports.
- * Playback maps to source ports and Capture for sink ports.
- *
- * This should be invoked with NULL to clear the stream set previously.
- * Returns 0 on success, a negative error code otherwise.
- */
-static inline int snd_soc_dai_set_stream(struct snd_soc_dai *dai,
- void *stream, int direction)
-{
- if (dai->driver->ops->set_stream)
- return dai->driver->ops->set_stream(dai, stream, direction);
- else
- return -ENOTSUPP;
-}
-
-/**
- * snd_soc_dai_get_stream() - Retrieves stream from DAI
- * @dai: DAI
- * @direction: Stream direction(Playback/Capture)
- *
- * This routine only retrieves that was previously configured
- * with snd_soc_dai_get_stream()
- *
- * Returns pointer to stream or an ERR_PTR value, e.g.
- * ERR_PTR(-ENOTSUPP) if callback is not supported;
- */
-static inline void *snd_soc_dai_get_stream(struct snd_soc_dai *dai,
- int direction)
+int snd_soc_dai_set_stream(struct snd_soc_dai *dai, void *stream, int direction);
+void *snd_soc_dai_get_stream(struct snd_soc_dai *dai, int direction);
+
+struct snd_soc_dai *snd_soc_dai_register(struct snd_soc_component *component,
+ struct snd_soc_dai_driver *dai_drv,
+ bool legacy_dai_naming);
+void snd_soc_dai_unregister(struct snd_soc_dai *dai);
+struct snd_soc_dai *snd_soc_dai_from_list(struct list_head *list);
+struct list_head *snd_soc_dai_to_list(struct snd_soc_dai *dai);
+unsigned int snd_soc_dai_get_symmetric_rate(struct snd_soc_dai *dai);
+unsigned int snd_soc_dai_get_symmetric_channels(struct snd_soc_dai *dai);
+unsigned int snd_soc_dai_get_symmetric_sample_bits(struct snd_soc_dai *dai);
+void snd_soc_dai_set_priv(struct snd_soc_dai *dai, void *priv);
+void *snd_soc_dai_to_priv(struct snd_soc_dai *dai);
+
+/* REMOVE ME */
+#define snd_soc_dai_get_pcm_stream snd_soc_dai_pcm_stream_get
+#define snd_soc_dai_get_widget_playback(dai) snd_soc_dai_stream_widget_get(dai, SNDRV_PCM_STREAM_PLAYBACK)
+#define snd_soc_dai_get_widget_capture(dai) snd_soc_dai_stream_widget_get(dai, SNDRV_PCM_STREAM_CAPTURE)
+#define snd_soc_dai_get_widget snd_soc_dai_stream_widget_get
+#define snd_soc_dai_set_widget_playback(dai, widget) snd_soc_dai_stream_widget_set(dai, SNDRV_PCM_STREAM_PLAYBACK, widget)
+#define snd_soc_dai_set_widget_capture(dai, widget) snd_soc_dai_stream_widget_set(dai, SNDRV_PCM_STREAM_CAPTURE, widget)
+#define snd_soc_dai_set_widget snd_soc_dai_stream_widget_set
+#define snd_soc_dai_dma_data_get_playback(dai) snd_soc_dai_stream_dma_data_get(dai, SNDRV_PCM_STREAM_PLAYBACK)
+#define snd_soc_dai_dma_data_get_capture(dai) snd_soc_dai_stream_dma_data_get(dai, SNDRV_PCM_STREAM_CAPTURE)
+#define snd_soc_dai_get_dma_data snd_soc_dai_stream_dma_data_get
+#define snd_soc_dai_dma_data_get snd_soc_dai_stream_dma_data_get
+#define snd_soc_dai_dma_data_set_playback(dai, data) snd_soc_dai_stream_dma_data_set(dai, SNDRV_PCM_STREAM_PLAYBACK, data)
+#define snd_soc_dai_dma_data_set_capture(dai, data) snd_soc_dai_stream_dma_data_set(dai, SNDRV_PCM_STREAM_CAPTURE, data)
+#define snd_soc_dai_set_dma_data snd_soc_dai_stream_dma_data_set
+#define snd_soc_dai_dma_data_set snd_soc_dai_stream_dma_data_set
+#define snd_soc_dai_tdm_mask_get snd_soc_dai_stream_tdm_mask_get
+#define snd_soc_dai_tdm_mask_set snd_soc_dai_stream_tdm_mask_set
+static inline void snd_soc_dai_init_dma_data(struct snd_soc_dai *dai, void *playback, void *capture)
{
- if (dai->driver->ops->get_stream)
- return dai->driver->ops->get_stream(dai, direction);
- else
- return ERR_PTR(-ENOTSUPP);
+ snd_soc_dai_stream_dma_data_set_playback(dai, playback);
+ snd_soc_dai_stream_dma_data_set_capture(dai, capture);
}
#endif
diff --git a/include/sound/soc.h b/include/sound/soc.h
index 5afc34b147b5..553bb7e2be98 100644
--- a/include/sound/soc.h
+++ b/include/sound/soc.h
@@ -555,8 +555,6 @@ int snd_soc_add_component_controls(struct snd_soc_component *component,
const struct snd_kcontrol_new *controls, unsigned int num_controls);
int snd_soc_add_card_controls(struct snd_soc_card *soc_card,
const struct snd_kcontrol_new *controls, int num_controls);
-int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
- const struct snd_kcontrol_new *controls, int num_controls);
int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo);
int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
@@ -1389,19 +1387,12 @@ void snd_soc_dlc_use_cpu_as_platform(struct snd_soc_dai_link_component *platform
struct of_phandle_args *snd_soc_copy_dai_args(struct device *dev,
const struct of_phandle_args *args);
struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_args);
-struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
- struct snd_soc_dai_driver *dai_drv,
- bool legacy_dai_naming);
-void snd_soc_unregister_dai(struct snd_soc_dai *dai);
struct snd_soc_dai *snd_soc_find_dai(
const struct snd_soc_dai_link_component *dlc);
struct snd_soc_dai *snd_soc_find_dai_with_mutex(
const struct snd_soc_dai_link_component *dlc);
-void soc_pcm_set_dai_params(struct snd_soc_dai *dai,
- struct snd_pcm_hw_params *params);
-
#include <sound/soc-dai.h>
static inline
@@ -1551,4 +1542,11 @@ static inline void _snd_soc_dpcm_mutex_assert_held_r(struct snd_soc_pcm_runtime
#include <sound/soc-card.h>
#include <sound/soc-jack.h>
+/*
+ * REMOVE ME
+ */
+#define snd_soc_add_dai_controls snd_soc_dai_add_controls
+#define snd_soc_register_dai snd_soc_dai_register
+#define snd_soc_unregister_dai snd_soc_dai_unregister
+
#endif
diff --git a/include/sound/soc_sdw_utils.h b/include/sound/soc_sdw_utils.h
index 9fbb69b9052d..418466438ea1 100644
--- a/include/sound/soc_sdw_utils.h
+++ b/include/sound/soc_sdw_utils.h
@@ -292,4 +292,24 @@ int asoc_sdw_es9356_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai
int asoc_sdw_es9356_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
int asoc_sdw_es9356_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai);
+/* Senary SN624x SDCA */
+int asoc_sdw_senary_sdca_jack_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback);
+int asoc_sdw_senary_sdca_jack_exit(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link);
+int asoc_sdw_senary_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai);
+int asoc_sdw_senary_amp_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback);
+int asoc_sdw_senary_amp_exit(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_link);
+int asoc_sdw_senary_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai);
+int asoc_sdw_senary_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_dai *dai);
+
#endif
diff --git a/include/sound/tas2781-dsp.h b/include/sound/tas2781-dsp.h
index e845687decf9..3380dfd526de 100644
--- a/include/sound/tas2781-dsp.h
+++ b/include/sound/tas2781-dsp.h
@@ -56,6 +56,10 @@ enum tasdevice_dsp_dev_idx {
TASDEVICE_DSP_TAS_2781_QUAD,
TASDEVICE_DSP_TAS_5825_MONO,
TASDEVICE_DSP_TAS_5825_DUAL,
+ TASDEVICE_DSP_TAS_2573_MONO,
+ TASDEVICE_DSP_TAS_2573_DUAL,
+ TASDEVICE_DSP_TAS_2573_21,
+ TASDEVICE_DSP_TAS_2573_QUAD,
TASDEVICE_DSP_TAS_MAX_DEVICE
};
@@ -142,6 +146,7 @@ struct tasdevice_fw {
struct tasdevice_calibration *calibrations;
struct fct_param_address fct_par_addr;
struct device *dev;
+ int calibration_config_id;
};
enum tasdevice_fw_state {
@@ -207,6 +212,14 @@ struct tasdevice_rca {
*/
int capture_profile_id;
/*
+ * Primarily designed for speaker calibration scenarios with special
+ * requirements. For regular use cases, when the default value -1 is
+ * set, calibration will directly reuse the current playback
+ * configuration; non-negative values can be customized for special
+ * calibration demands.
+ */
+ int calibration_profile_id;
+ /*
* Since version 0x105, the keyword 'init' was introduced into the
* profile, which is used for chip initialization, particularly to
* store common settings for other non-initialization profiles.
diff --git a/include/sound/tas2781.h b/include/sound/tas2781.h
index b763da613769..7367c76959aa 100644
--- a/include/sound/tas2781.h
+++ b/include/sound/tas2781.h
@@ -91,6 +91,30 @@
/* prm_TE_1_Beta1 */
#define TAS2563_TE_DT_REG TASDEVICE_REG(0x00, 0x0f, 0x70)
+#define TAS2573_FCT_INT_LATCH TASDEVICE_REG(0x00, 0x00, 0x67)
+/* YM57 */
+#define TAS2573_FCT_OUTPUT_R0 TASDEVICE_REG(0x00, 0x65, 0x74)
+/* YM58 */
+#define TAS2573_FCT_OUTPUT_R0_LOW TASDEVICE_REG(0x00, 0x65, 0x78)
+/* YM59 */
+#define TAS2573_FCT_OUTPUT_INV_R0 TASDEVICE_REG(0x00, 0x65, 0x7C)
+/* YM61 */
+#define TAS2573_FCT_OUTPUT_POWERTOT TASDEVICE_REG(0x00, 0x66, 0x0C)
+/* YM64 */
+#define TAS2573_FCT_OUTPUT_F0 TASDEVICE_REG(0x00, 0x65, 0x18)
+/* YM147 */
+#define TAS2573_FCT_STATUS_CTRL TASDEVICE_REG(0x00, 0x68, 0x74)
+/* YM148 */
+#define TAS2573_FCT_STATUS_BINNING TASDEVICE_REG(0x00, 0x68, 0x78)
+/* YM664 */
+#define TAS2573_RE_OUT TASDEVICE_REG(0x00, 0x7A, 0x18)
+/* YM952 */
+#define TAS2573_SILENCE_DETECTED TASDEVICE_REG(0x00, 0x83, 0x60)
+/* YM954 */
+#define TAS2573_OPEN_CIRCUIT TASDEVICE_REG(0x00, 0x83, 0x68)
+/* YM955 */
+#define TAS2573_SHORTCKT TASDEVICE_REG(0x00, 0x83, 0x6C)
+
#define TAS2781_PRM_INT_MASK_REG TASDEVICE_REG(0x00, 0x00, 0x3b)
#define TAS2781_PRM_CLK_CFG_REG TASDEVICE_REG(0x00, 0x00, 0x5c)
#define TAS2781_PRM_RSVD_REG TASDEVICE_REG(0x00, 0x01, 0x19)
diff --git a/sound/hda/core/intel-nhlt.c b/sound/hda/core/intel-nhlt.c
index 6d72a871bda0..af4d469dbd76 100644
--- a/sound/hda/core/intel-nhlt.c
+++ b/sound/hda/core/intel-nhlt.c
@@ -351,7 +351,7 @@ int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt,
int i;
if (!nhlt) {
- dev_err(dev, "%s: NHLT table is missing (query for SSP%d)\n",
+ dev_dbg(dev, "%s: NHLT table is missing (query for SSP%d)\n",
__func__, virtual_bus_id);
return -EINVAL;
}
@@ -369,7 +369,7 @@ int intel_nhlt_ssp_device_type(struct device *dev, struct nhlt_acpi_table *nhlt,
epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
}
- dev_err(dev, "%s: No match for SSP%d in NHLT table\n", __func__,
+ dev_dbg(dev, "%s: No match for SSP%d in NHLT table\n", __func__,
virtual_bus_id);
dev_dbg(dev, "Available endpoints:\n");
diff --git a/sound/soc/amd/acp-es8336.c b/sound/soc/amd/acp-es8336.c
index 34e953d1a3a7..0cb0ee76191d 100644
--- a/sound/soc/amd/acp-es8336.c
+++ b/sound/soc/amd/acp-es8336.c
@@ -30,7 +30,6 @@
static unsigned long acp2x_machine_id;
static struct snd_soc_jack st_jack;
-static struct device *codec_dev;
static struct gpio_desc *gpio_pa;
static int sof_es8316_speaker_power_event(struct snd_soc_dapm_widget *w,
@@ -191,6 +190,7 @@ static const struct acpi_gpio_mapping acpi_es8336_gpios[] = {
static int st_es8336_late_probe(struct snd_soc_card *card)
{
+ struct device *codec_dev __free(put_device) = NULL;
struct acpi_device *adev;
int ret;
@@ -213,7 +213,6 @@ static int st_es8336_late_probe(struct snd_soc_card *card)
if (IS_ERR(gpio_pa)) {
ret = dev_err_probe(card->dev, PTR_ERR(gpio_pa),
"could not get pa-enable GPIO\n");
- put_device(codec_dev);
return ret;
}
return 0;
diff --git a/sound/soc/amd/acp/Kconfig b/sound/soc/amd/acp/Kconfig
index e880e44b9763..b7b7b4f4a55c 100644
--- a/sound/soc/amd/acp/Kconfig
+++ b/sound/soc/amd/acp/Kconfig
@@ -167,7 +167,7 @@ config SND_SOC_AMD_SOF_SDW_MACH
config SND_SOC_AMD_LEGACY_SDW_MACH
tristate "AMD Legacy(No DSP) Soundwire Machine Driver Support"
- depends on X86 && PCI && ACPI
+ depends on X86 && PCI && ACPI && EFI
depends on SOUNDWIRE
select SND_SOC_AMD_SDW_MACH_COMMON
select SND_SOC_SDW_UTILS
@@ -181,7 +181,7 @@ config SND_SOC_AMD_LEGACY_SDW_MACH
select SND_SOC_RT715_SDW
select SND_SOC_RT715_SDCA_SDW
select SND_SOC_RT722_SDCA_SDW
- select SND_SOC_TAS2783_SDW
+ select SND_SOC_TAS2783_SDW if SND_SOC_SDCA && EFI
help
This option enables Legacy(No DSP) sound card support for SoundWire
enabled AMD platforms along with ACP PDM controller.
diff --git a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
index c522b421e1ec..eb0e9a6af6ef 100644
--- a/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
+++ b/sound/soc/amd/acp/acp3x-es83xx/acp3x-es83xx.c
@@ -10,6 +10,7 @@
#include <sound/soc-dapm.h>
#include <sound/jack.h>
#include <sound/soc-acpi.h>
+#include <acpi/acpi_bus.h>
#include <linux/clk.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
@@ -41,6 +42,11 @@ struct acp3x_es83xx_private {
struct snd_soc_dapm_route mic_map[2];
};
+static void acp3x_es83xx_put_codec_device(void *data)
+{
+ put_device(data);
+}
+
static const unsigned int channels[] = {
DUAL_CHANNEL,
};
@@ -441,6 +447,12 @@ static int acp3x_es83xx_probe(struct snd_soc_card *card)
return -ENOMEM;
}
+ ret = devm_add_action_or_reset(dev,
+ acp3x_es83xx_put_codec_device,
+ codec_dev);
+ if (ret)
+ return ret;
+
priv->codec_dev = codec_dev;
priv->quirk = (unsigned long)dmi_id->driver_data;
acp_drvdata->mach_priv = priv;
diff --git a/sound/soc/amd/acp/soc_amd_sdw_common.h b/sound/soc/amd/acp/soc_amd_sdw_common.h
index 3930cc46fa58..17e4e97fb3d1 100644
--- a/sound/soc/amd/acp/soc_amd_sdw_common.h
+++ b/sound/soc/amd/acp/soc_amd_sdw_common.h
@@ -23,6 +23,19 @@
#define ACP71_PCI_REV 0x71
#define ACP72_PCI_REV 0x72
+/**
+ * struct amd_pdm_pdata - platform data passed via mach->pdata to machine driver
+ * @pdm_sel: active PDM controller (ACP7X_PDM_DMIC0 or ACP7X_PDM_DMIC1),
+ * non-zero when a PDM controller was identified via ACPI _DSD
+ *
+ * Carries the PDM controller selection for ACP7.B/7.F platforms, derived
+ * from the acp-audio-ep-port ACPI _DSD property and passed via mach->pdata
+ * to the machine driver.
+ */
+struct amd_pdm_pdata {
+ unsigned int pdm_sel;
+};
+
#define SOC_JACK_JDSRC(quirk) ((quirk) & GENMASK(3, 0))
#define ASOC_SDW_FOUR_SPK BIT(4)
#define ASOC_SDW_ACP_DMIC BIT(5)
diff --git a/sound/soc/atmel/atmel-pcm-dma.c b/sound/soc/atmel/atmel-pcm-dma.c
index 7306e04da513..e4b73ae3df4f 100644
--- a/sound/soc/atmel/atmel-pcm-dma.c
+++ b/sound/soc/atmel/atmel-pcm-dma.c
@@ -47,7 +47,7 @@ static const struct snd_pcm_hardware atmel_pcm_dma_hardware = {
* atmel_pcm_dma_irq: SSC interrupt handler for DMAENGINE enabled SSC
*
* We use DMAENGINE to send/receive data to/from SSC so this ISR is only to
- * check if any overrun occured.
+ * check if any overrun occurred.
*/
static void atmel_pcm_dma_irq(u32 ssc_sr,
struct snd_pcm_substream *substream)
diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h
index 4925e03c3c30..7848e15efbe1 100644
--- a/sound/soc/bcm/cygnus-ssp.h
+++ b/sound/soc/bcm/cygnus-ssp.h
@@ -27,7 +27,7 @@ struct ringbuf_regs {
unsigned wraddr;
unsigned baseaddr;
unsigned endaddr;
- unsigned fmark; /* freemark for play, fullmark for caputure */
+ unsigned fmark; /* freemark for play, fullmark for capture */
unsigned period_bytes;
unsigned buf_size;
};
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index b4f5b24cde45..7596227fbccf 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1136,11 +1136,15 @@ static int pm860x_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static const u64 pm860x_selectable_formats = SND_SOC_POSSIBLE_DAIFMT_I2S;
+
static const struct snd_soc_dai_ops pm860x_pcm_dai_ops = {
.mute_stream = pm860x_mute_stream,
.hw_params = pm860x_pcm_hw_params,
.set_fmt = pm860x_pcm_set_dai_fmt,
.set_sysclk = pm860x_set_dai_sysclk,
+ .auto_selectable_formats = &pm860x_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1149,6 +1153,8 @@ static const struct snd_soc_dai_ops pm860x_i2s_dai_ops = {
.hw_params = pm860x_i2s_hw_params,
.set_fmt = pm860x_i2s_set_dai_fmt,
.set_sysclk = pm860x_set_dai_sysclk,
+ .auto_selectable_formats = &pm860x_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 8e251e1d84b4..ba59dabeeccd 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -123,6 +123,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_ES8328_I2C
imply SND_SOC_ES8375
imply SND_SOC_ES8389
+ imply SND_SOC_ES9039Q2M
imply SND_SOC_ES9356
imply SND_SOC_ES7134
imply SND_SOC_ES7241
@@ -173,6 +174,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_MT6660
imply SND_SOC_NAU8315
imply SND_SOC_NAU8325
+ imply SND_SOC_NAU8360
imply SND_SOC_NAU8540
imply SND_SOC_NAU8810
imply SND_SOC_NAU8821
@@ -255,6 +257,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_SIMPLE_MUX
imply SND_SOC_SMA1303
imply SND_SOC_SMA1307
+ imply SND_SOC_SN624X_SDCA_SDW
imply SND_SOC_SPDIF
imply SND_SOC_SRC4XXX_I2C
imply SND_SOC_SSM2305
@@ -1315,6 +1318,19 @@ config SND_SOC_ES8389
tristate "Everest Semi ES8389 CODEC"
depends on I2C
+config SND_SOC_ES9039Q2M
+ tristate "ESS Technology ES9039Q2M CODEC"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for the ESS Technology ES9039Q2M, a 32-bit
+ two-channel audio DAC with an asynchronous sample rate converter,
+ supporting PCM, DSD, DoP and S/PDIF input. This driver covers the
+ I2C control interface, which the MODE pin selects.
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-soc-es9039q2m.
+
config SND_SOC_ES9356
tristate "Everest Semi ES9356 CODEC SDW"
depends on SND_SOC_SDCA
@@ -2097,6 +2113,18 @@ config SND_SOC_SMA1307
If you are using a system with an SMA1307 amplifier connected
via I2C, enable this option.
+config SND_SOC_SN624X_SDCA_SDW
+ tristate "Senary SN624x SDCA Codec - SDW"
+ depends on SND_SOC_SDCA
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+ help
+ Enable support for Senary SN624x SDCA SoundWire codecs.
+ These multi-function devices provide headset jack, speaker
+ amplifier, and digital microphone endpoints on the SoundWire
+ bus.
+
config SND_SOC_SPDIF
tristate "S/PDIF CODEC"
@@ -2524,6 +2552,17 @@ config SND_SOC_WCD939X_SDW
The WCD9390/9395 is a audio codec IC Integrated in
Qualcomm SoCs like SM8650.
+config SND_SOC_WCD9378_SDCA
+ tristate "Qualcomm Tambora (WCD9378) SDCA codec"
+ depends on SOUNDWIRE
+ depends on SND_SOC_SDCA_CLASS
+ help
+ This enables support for the Qualcomm Tambora (WCD9378) headset
+ codec when driven via the SDCA class driver on ARM platforms
+ without ACPI/DisCo tables. It provides the static SDCA topology
+ and SoundWire data port properties transcribed from the factory
+ ACPI tables.
+
config SND_SOC_WM0010
tristate
depends on SPI_MASTER
@@ -2901,6 +2940,16 @@ config SND_SOC_NAU8825
tristate
depends on I2C
+config SND_SOC_NAU8360
+ tristate "Nuvoton Technology Corporation NAU83G60 Stereo Smart Amplifier"
+ depends on I2C
+ select FW_LOADER
+ help
+ The NAU83G60 is a stereo 30W+30W smart amplifier with integrated
+ low-latency Advanced Audio DSP running the KCS (Klippel Controlled
+ Sound) speaker control algorithm. KCS provides mechanical and
+ thermal speaker protection with voltage and current sensing.
+
config SND_SOC_NTPFW
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 3d122ace75ad..3826f9e4fce8 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -140,6 +140,7 @@ 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-es9039q2m-y := es9039q2m.o
snd-soc-es9356-y := es9356.o
snd-soc-framer-y := framer-codec.o
snd-soc-fs-amp-lib-y := fs-amp-lib.o
@@ -198,6 +199,7 @@ snd-soc-mt6359-accdet-y := mt6359-accdet.o
snd-soc-mt6660-y := mt6660.o
snd-soc-nau8315-y := nau8315.o
snd-soc-nau8325-y := nau8325.o
+snd-soc-nau8360-y := nau8360.o nau8360-dsp.o
snd-soc-nau8540-y := nau8540.o
snd-soc-nau8810-y := nau8810.o
snd-soc-nau8821-y := nau8821.o
@@ -301,6 +303,7 @@ snd-soc-sigmadsp-regmap-y := sigmadsp-regmap.o
snd-soc-si476x-y := si476x.o
snd-soc-sma1303-y := sma1303.o
snd-soc-sma1307-y := sma1307.o
+snd-soc-sn624x-sdca-y := sn624x-sdca-sdw.o
snd-soc-spdif-tx-y := spdif_transmitter.o
snd-soc-spdif-rx-y := spdif_receiver.o
snd-soc-src4xxx-y := src4xxx.o
@@ -366,6 +369,7 @@ snd-soc-wcd938x-y := wcd938x.o
snd-soc-wcd938x-sdw-y := wcd938x-sdw.o
snd-soc-wcd939x-y := wcd939x.o
snd-soc-wcd939x-sdw-y := wcd939x-sdw.o
+snd-soc-wcd9378-y := wcd9378-sdw.o wcd9378-sdca.o
snd-soc-wm-adsp-y := wm_adsp.o
snd-soc-wm-adsp-test-y := wm_adsp_fw_find_test.o
snd-soc-wm0010-y := wm0010.o
@@ -584,6 +588,7 @@ 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_ES9039Q2M) += snd-soc-es9039q2m.o
obj-$(CONFIG_SND_SOC_ES9356) += snd-soc-es9356.o
obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o
obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o
@@ -637,6 +642,7 @@ obj-$(CONFIG_SND_SOC_MT6359_ACCDET) += mt6359-accdet.o
obj-$(CONFIG_SND_SOC_MT6660) += snd-soc-mt6660.o
obj-$(CONFIG_SND_SOC_NAU8315) += snd-soc-nau8315.o
obj-$(CONFIG_SND_SOC_NAU8325) += snd-soc-nau8325.o
+obj-$(CONFIG_SND_SOC_NAU8360) += snd-soc-nau8360.o
obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
obj-$(CONFIG_SND_SOC_NAU8821) += snd-soc-nau8821.o
@@ -742,6 +748,7 @@ obj-$(CONFIG_SND_SOC_SIGMADSP_REGMAP) += snd-soc-sigmadsp-regmap.o
obj-$(CONFIG_SND_SOC_SI476X) += snd-soc-si476x.o
obj-$(CONFIG_SND_SOC_SMA1303) += snd-soc-sma1303.o
obj-$(CONFIG_SND_SOC_SMA1307) += snd-soc-sma1307.o
+obj-$(CONFIG_SND_SOC_SN624X_SDCA_SDW) += snd-soc-sn624x-sdca.o
obj-$(CONFIG_SND_SOC_SPDIF) += snd-soc-spdif-rx.o snd-soc-spdif-tx.o
obj-$(CONFIG_SND_SOC_SRC4XXX) += snd-soc-src4xxx.o
obj-$(CONFIG_SND_SOC_SRC4XXX_I2C) += snd-soc-src4xxx-i2c.o
@@ -819,6 +826,7 @@ ifdef CONFIG_SND_SOC_WCD939X_SDW
# avoid link failure by forcing sdw code built-in when needed
obj-$(CONFIG_SND_SOC_WCD939X) += snd-soc-wcd939x-sdw.o
endif
+obj-$(CONFIG_SND_SOC_WCD9378_SDCA) += snd-soc-wcd9378.o
obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o
obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o
obj-$(CONFIG_SND_SOC_WM2000) += snd-soc-wm2000.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index e1a0e35836e6..3e3ff9bfe00d 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -299,7 +299,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
SND_SOC_DAPM_REGULATOR_SUPPLY("V-AUD", 0, 0),
SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC1", 0, 0),
SND_SOC_DAPM_REGULATOR_SUPPLY("V-AMIC2", 0, 0),
- SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0, 0),
/* Power */
SND_SOC_DAPM_SUPPLY("Audio Power",
@@ -324,7 +323,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
SND_SOC_DAPM_AIF_OUT("AD_OUT1", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AD_OUT2", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AD_OUT3", NULL, 0, SND_SOC_NOPM, 0, 0),
- SND_SOC_DAPM_AIF_OUT("AD_OUT4", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AD_OUT57", NULL, 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AD_OUT68", NULL, 0, SND_SOC_NOPM, 0, 0),
@@ -584,36 +582,6 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("AD5768 Enable", AB8500_ADPATHENA,
AB8500_ADPATHENA_ENAD5768, 0, NULL, 0),
- /* Digital Microphone path */
-
- SND_SOC_DAPM_INPUT("DMic 1"),
- SND_SOC_DAPM_INPUT("DMic 2"),
- SND_SOC_DAPM_INPUT("DMic 3"),
- SND_SOC_DAPM_INPUT("DMic 4"),
- SND_SOC_DAPM_INPUT("DMic 5"),
- SND_SOC_DAPM_INPUT("DMic 6"),
-
- SND_SOC_DAPM_MIXER("DMIC1",
- AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC1, 0,
- NULL, 0),
- SND_SOC_DAPM_MIXER("DMIC2",
- AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC2, 0,
- NULL, 0),
- SND_SOC_DAPM_MIXER("DMIC3",
- AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC3, 0,
- NULL, 0),
- SND_SOC_DAPM_MIXER("DMIC4",
- AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC4, 0,
- NULL, 0),
- SND_SOC_DAPM_MIXER("DMIC5",
- AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC5, 0,
- NULL, 0),
- SND_SOC_DAPM_MIXER("DMIC6",
- AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC6, 0,
- NULL, 0),
- SND_SOC_DAPM_MIXER("AD4 Channel Volume",
- SND_SOC_NOPM, 0, 0,
- NULL, 0),
/* Acoustical Noise Cancellation path */
SND_SOC_DAPM_MUX("ANC Source",
@@ -648,6 +616,40 @@ static const struct snd_soc_dapm_widget ab8500_dapm_widgets[] = {
NULL, 0),
};
+static const struct snd_soc_dapm_widget ab8500_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("V-DMIC", 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AD_OUT4", NULL, 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_INPUT("DMic 1"),
+ SND_SOC_DAPM_INPUT("DMic 2"),
+ SND_SOC_DAPM_INPUT("DMic 3"),
+ SND_SOC_DAPM_INPUT("DMic 4"),
+ SND_SOC_DAPM_INPUT("DMic 5"),
+ SND_SOC_DAPM_INPUT("DMic 6"),
+
+ SND_SOC_DAPM_MIXER("DMIC1",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC1, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC2",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC2, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC3",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC3, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC4",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC4, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC5",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC5, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("DMIC6",
+ AB8500_DIGMICCONF, AB8500_DIGMICCONF_ENDMIC6, 0,
+ NULL, 0),
+ SND_SOC_DAPM_MIXER("AD4 Channel Volume",
+ SND_SOC_NOPM, 0, 0,
+ NULL, 0),
+};
+
/*
* DAPM-routes
*/
@@ -853,35 +855,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"AD_OUT68", NULL, "Main Supply"},
{"AD_OUT68", NULL, "AD6 Channel Volume"},
- /* Digital Microphone path */
-
- {"DMic 1", NULL, "V-DMIC"},
- {"DMic 2", NULL, "V-DMIC"},
- {"DMic 3", NULL, "V-DMIC"},
- {"DMic 4", NULL, "V-DMIC"},
- {"DMic 5", NULL, "V-DMIC"},
- {"DMic 6", NULL, "V-DMIC"},
-
- {"DMIC1", NULL, "DMic 1"},
- {"DMIC2", NULL, "DMic 2"},
- {"DMIC3", NULL, "DMic 3"},
- {"DMIC4", NULL, "DMic 4"},
- {"DMIC5", NULL, "DMic 5"},
- {"DMIC6", NULL, "DMic 6"},
-
- {"AD1 Source Select", "DMic 1", "DMIC1"},
- {"AD2 Source Select", "DMic 2", "DMIC2"},
- {"AD3 Source Select", "DMic 3", "DMIC3"},
- {"AD5 Source Select", "DMic 5", "DMIC5"},
- {"AD6 Source Select", "DMic 6", "DMIC6"},
-
- {"AD4 Channel Volume", NULL, "DMIC4"},
- {"AD4 Channel Volume", NULL, "AD34 Enable"},
-
- {"ab8500_0c", NULL, "AD_OUT4"},
- {"AD_OUT4", NULL, "Main Supply"},
- {"AD_OUT4", NULL, "AD4 Channel Volume"},
-
/* LineIn Bypass path */
{"LINL to HSL Volume", NULL, "LINL Enable"},
@@ -911,7 +884,6 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"Sidetone Left Source", "Headset Left", "DA_IN1"},
{"Sidetone Right Source", "LineIn Right", "AD2 Channel Volume"},
{"Sidetone Right Source", "Mic 1", "AD3 Channel Volume"},
- {"Sidetone Right Source", "DMic 4", "AD4 Channel Volume"},
{"Sidetone Right Source", "Headset Right", "DA_IN2"},
{"STFIR1 Control", NULL, "Sidetone Left Source"},
@@ -924,6 +896,37 @@ static const struct snd_soc_dapm_route ab8500_dapm_routes[] = {
{"DA2 Enable", NULL, "STFIR2 Volume"},
};
+static const struct snd_soc_dapm_route ab8500_dmic_dapm_routes[] = {
+ {"DMic 1", NULL, "V-DMIC"},
+ {"DMic 2", NULL, "V-DMIC"},
+ {"DMic 3", NULL, "V-DMIC"},
+ {"DMic 4", NULL, "V-DMIC"},
+ {"DMic 5", NULL, "V-DMIC"},
+ {"DMic 6", NULL, "V-DMIC"},
+
+ {"DMIC1", NULL, "DMic 1"},
+ {"DMIC2", NULL, "DMic 2"},
+ {"DMIC3", NULL, "DMic 3"},
+ {"DMIC4", NULL, "DMic 4"},
+ {"DMIC5", NULL, "DMic 5"},
+ {"DMIC6", NULL, "DMic 6"},
+
+ {"AD1 Source Select", "DMic 1", "DMIC1"},
+ {"AD2 Source Select", "DMic 2", "DMIC2"},
+ {"AD3 Source Select", "DMic 3", "DMIC3"},
+ {"AD5 Source Select", "DMic 5", "DMIC5"},
+ {"AD6 Source Select", "DMic 6", "DMIC6"},
+
+ {"AD4 Channel Volume", NULL, "DMIC4"},
+ {"AD4 Channel Volume", NULL, "AD34 Enable"},
+
+ {"ab8500_0c", NULL, "AD_OUT4"},
+ {"AD_OUT4", NULL, "Main Supply"},
+ {"AD_OUT4", NULL, "AD4 Channel Volume"},
+
+ {"Sidetone Right Source", "DMic 4", "AD4 Channel Volume"},
+};
+
static const struct snd_soc_dapm_route ab8500_dapm_routes_mic1a_vamicx[] = {
{"MIC1A V-AMICx Enable", NULL, "V-AMIC1"},
{"MIC1A V-AMICx Enable", NULL, "V-AMIC2"},
@@ -1950,9 +1953,22 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
+static const u64 ab8500_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_GATED |
+ SND_SOC_POSSIBLE_DAIFMT_CONT |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops ab8500_codec_ops = {
.set_fmt = ab8500_codec_set_dai_fmt,
.set_tdm_slot = ab8500_codec_set_dai_tdm_slot,
+ .auto_selectable_formats = &ab8500_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ab8500_codec_dai[] = {
@@ -2043,13 +2059,30 @@ static int ab8500_codec_probe(struct snd_soc_component *component)
{
struct device *dev = component->dev;
struct device_node *np = dev->of_node;
+ struct ab8500 *ab8500 = dev_get_drvdata(dev->parent);
struct ab8500_codec_platform_data codec_pdata;
+ struct snd_soc_dapm_context *dapm;
int status;
dev_dbg(dev, "%s: Enter.\n", __func__);
ab8500_codec_of_probe(dev, np, &codec_pdata);
+ if (is_ab8500(ab8500)) {
+ dapm = snd_soc_component_to_dapm(component);
+ status = snd_soc_dapm_new_controls(dapm,
+ ab8500_dmic_dapm_widgets,
+ ARRAY_SIZE(ab8500_dmic_dapm_widgets));
+ if (status)
+ return status;
+
+ status = snd_soc_dapm_add_routes(dapm,
+ ab8500_dmic_dapm_routes,
+ ARRAY_SIZE(ab8500_dmic_dapm_routes));
+ if (status)
+ return status;
+ }
+
status = ab8500_audio_init_audioblock(component);
if (status < 0) {
dev_err(dev, "%s: failed to init audio-block (%d)!\n",
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
index cc174ec3a1f8..0830d7d2dd7c 100644
--- a/sound/soc/codecs/adau1372.c
+++ b/sound/soc/codecs/adau1372.c
@@ -881,12 +881,24 @@ static const struct snd_soc_component_driver adau1372_driver = {
.endianness = 1,
};
+static const u64 adau1372_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops adau1372_dai_ops = {
.set_fmt = adau1372_set_dai_fmt,
.set_tdm_slot = adau1372_set_tdm_slot,
.set_tristate = adau1372_set_tristate,
.hw_params = adau1372_hw_params,
.startup = adau1372_startup,
+ .auto_selectable_formats = &adau1372_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define ADAU1372_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 5dd961b233ce..aa7d3a4b9f58 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1196,10 +1196,22 @@ static int adau1373_set_dai_sysclk(struct snd_soc_dai *dai,
return 0;
}
+static const u64 adau1373_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops adau1373_dai_ops = {
.hw_params = adau1373_hw_params,
.set_sysclk = adau1373_set_dai_sysclk,
.set_fmt = adau1373_set_dai_fmt,
+ .auto_selectable_formats = &adau1373_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define ADAU1373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 329ab01b62c0..4afbf46ffaa3 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -4,7 +4,7 @@
*
* Copyright 2011 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- * based on an inital version by Cliff Cai <cliff.cai@analog.com>
+ * based on an initial version by Cliff Cai <cliff.cai@analog.com>
*/
#include <linux/module.h>
@@ -627,11 +627,22 @@ static int adau1701_startup(struct snd_pcm_substream *substream,
#define ADAU1701_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 adau1701_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops adau1701_dai_ops = {
.set_fmt = adau1701_set_dai_fmt,
.hw_params = adau1701_hw_params,
.mute_stream = adau1701_mute_stream,
.startup = adau1701_startup,
+ .auto_selectable_formats = &adau1701_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -688,7 +699,7 @@ static int adau1701_probe(struct snd_soc_component *component)
*/
adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
- /* initalize with pre-configured pll mode settings */
+ /* initialize with pre-configured pll mode settings */
ret = adau1701_reset(component, adau1701->pll_clkdiv, 0);
if (ret < 0)
goto exit_regulators_disable;
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 26d7eb437ad1..207189cfd3a6 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -776,6 +776,17 @@ static int adau17x1_startup(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 adau17x1_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
const struct snd_soc_dai_ops adau17x1_dai_ops = {
.hw_params = adau17x1_hw_params,
.set_sysclk = adau17x1_set_dai_sysclk,
@@ -783,6 +794,8 @@ const struct snd_soc_dai_ops adau17x1_dai_ops = {
.set_pll = adau17x1_set_dai_pll,
.set_tdm_slot = adau17x1_set_dai_tdm_slot,
.startup = adau17x1_startup,
+ .auto_selectable_formats = &adau17x1_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
@@ -989,9 +1002,7 @@ int adau17x1_resume(struct snd_soc_component *component)
if (adau->switch_mode)
adau->switch_mode(component->dev);
- regcache_sync(adau->regmap);
-
- return 0;
+ return regcache_sync(adau->regmap);
}
EXPORT_SYMBOL_GPL(adau17x1_resume);
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index b42f35b1a603..b9d985393421 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -741,6 +741,17 @@ static int adau1977_set_tristate(struct snd_soc_dai *dai, int tristate)
ADAU1977_SAI_OVERTEMP_DRV_HIZ, val);
}
+static const u64 adau1977_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops adau1977_dai_ops = {
.startup = adau1977_startup,
.hw_params = adau1977_hw_params,
@@ -748,6 +759,8 @@ static const struct snd_soc_dai_ops adau1977_dai_ops = {
.set_fmt = adau1977_set_dai_fmt,
.set_tdm_slot = adau1977_set_tdm_slot,
.set_tristate = adau1977_set_tristate,
+ .auto_selectable_formats = &adau1977_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver adau1977_dai = {
diff --git a/sound/soc/codecs/adau7118.c b/sound/soc/codecs/adau7118.c
index 5ddea63a6612..9749299bc7f2 100644
--- a/sound/soc/codecs/adau7118.c
+++ b/sound/soc/codecs/adau7118.c
@@ -418,12 +418,24 @@ static int adau7118_component_probe(struct snd_soc_component *component)
return ret;
}
+static const u64 adau7118_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops adau7118_ops = {
.hw_params = adau7118_hw_params,
.set_channel_map = adau7118_set_channel_map,
.set_fmt = adau7118_set_fmt,
.set_tdm_slot = adau7118_set_tdm_slot,
.set_tristate = adau7118_set_tristate,
+ .auto_selectable_formats = &adau7118_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver adau7118_dai = {
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 6ea7cb78cd44..5ebc3afccccf 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -147,9 +147,16 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 ak4101_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ak4101_dai_ops = {
.hw_params = ak4104_hw_params,
.set_fmt = ak4104_set_dai_fmt,
+ .auto_selectable_formats = &ak4101_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ak4104_dai = {
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
index 03dadba0b038..2b96b82d4e40 100644
--- a/sound/soc/codecs/ak4118.c
+++ b/sound/soc/codecs/ak4118.c
@@ -236,9 +236,16 @@ static int ak4118_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+/* NOTE: Common formats only are auto detected */
+static const u64 ak4118_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ak4118_dai_ops = {
.hw_params = ak4118_hw_params,
.set_fmt = ak4118_set_dai_fmt,
+ .auto_selectable_formats = &ak4118_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ak4118_dai = {
diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c
index f81cd8cebdd8..a74be99e877d 100644
--- a/sound/soc/codecs/ak4458.c
+++ b/sound/soc/codecs/ak4458.c
@@ -591,12 +591,21 @@ static int ak4458_startup(struct snd_pcm_substream *substream,
&ak4458_rate_constraints);
}
+static const u64 ak4458_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_PDM;
+
static const struct snd_soc_dai_ops ak4458_dai_ops = {
.startup = ak4458_startup,
.hw_params = ak4458_hw_params,
.set_fmt = ak4458_set_dai_fmt,
.mute_stream = ak4458_set_dai_mute,
.set_tdm_slot = ak4458_set_tdm_slot,
+ .auto_selectable_formats = &ak4458_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 37555674cd15..6cabad4c9f74 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -346,11 +346,17 @@ static int ak4535_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+static const u64 ak4535_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ak4535_dai_ops = {
.hw_params = ak4535_hw_params,
.set_fmt = ak4535_set_dai_fmt,
.mute_stream = ak4535_mute,
.set_sysclk = ak4535_set_dai_sysclk,
+ .auto_selectable_formats = &ak4535_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c
index d9c9f6b20028..bae8e8b6a430 100644
--- a/sound/soc/codecs/ak4619.c
+++ b/sound/soc/codecs/ak4619.c
@@ -152,6 +152,7 @@
struct ak4619_priv {
struct regmap *regmap;
struct snd_pcm_hw_constraint_list constraint;
+ struct gpio_desc *pdn_gpio;
int deemph_en;
unsigned int playback_rate;
unsigned int sysclk;
@@ -799,7 +800,26 @@ static const struct snd_soc_dai_ops ak4619_dai_ops = {
.num_auto_selectable_formats = ARRAY_SIZE(ak4619_dai_formats),
};
+static int ak4619_suspend(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+
+ regcache_cache_only(regmap, true);
+ regcache_mark_dirty(regmap);
+ return 0;
+}
+
+static int ak4619_resume(struct snd_soc_component *component)
+{
+ struct regmap *regmap = dev_get_regmap(component->dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ return regcache_sync(regmap);
+}
+
static const struct snd_soc_component_driver soc_component_dev_ak4619 = {
+ .suspend = ak4619_suspend,
+ .resume = ak4619_resume,
.set_bias_level = ak4619_set_bias_level,
.controls = ak4619_snd_controls,
.num_controls = ARRAY_SIZE(ak4619_snd_controls),
@@ -875,10 +895,20 @@ static int ak4619_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, ak4619);
+ ak4619->pdn_gpio = devm_gpiod_get_optional(dev, "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(ak4619->pdn_gpio))
+ return dev_err_probe(dev, PTR_ERR(ak4619->pdn_gpio),
+ "powerdown GPIO request failed\n");
+ if (ak4619->pdn_gpio)
+ msleep(10);
+
ak4619->regmap = devm_regmap_init_i2c(i2c, &ak4619_regmap_cfg);
if (IS_ERR(ak4619->regmap)) {
ret = PTR_ERR(ak4619->regmap);
dev_err(dev, "regmap_init() failed: %d\n", ret);
+ if (ak4619->pdn_gpio)
+ gpiod_set_value_cansleep(ak4619->pdn_gpio, 1);
return ret;
}
@@ -887,6 +917,8 @@ static int ak4619_i2c_probe(struct i2c_client *i2c)
if (ret < 0) {
dev_err(dev, "Failed to register ak4619 component: %d\n",
ret);
+ if (ak4619->pdn_gpio)
+ gpiod_set_value_cansleep(ak4619->pdn_gpio, 1);
return ret;
}
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 08ec2035b270..a0de45660b00 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -493,12 +493,18 @@ static int ak4642_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static const u64 ak4642_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ak4642_dai_ops = {
.startup = ak4642_dai_startup,
.shutdown = ak4642_dai_shutdown,
.set_sysclk = ak4642_dai_set_sysclk,
.set_fmt = ak4642_dai_set_fmt,
.hw_params = ak4642_dai_hw_params,
+ .auto_selectable_formats = &ak4642_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ak4642_dai = {
@@ -533,8 +539,7 @@ static int ak4642_resume(struct snd_soc_component *component)
struct regmap *regmap = dev_get_regmap(component->dev, NULL);
regcache_cache_only(regmap, false);
- regcache_sync(regmap);
- return 0;
+ return regcache_sync(regmap);
}
static int ak4642_probe(struct snd_soc_component *component)
{
@@ -607,14 +612,13 @@ static struct clk *ak4642_of_parse_mcko(struct device *dev)
struct device_node *np = dev->of_node;
struct clk *clk;
const char *clk_name = np->name;
- const char *parent_clk_name = NULL;
+ const char *parent_clk_name;
u32 rate;
if (of_property_read_u32(np, "clock-frequency", &rate))
return NULL;
- if (of_property_read_bool(np, "clocks"))
- parent_clk_name = of_clk_get_parent_name(np, 0);
+ parent_clk_name = of_clk_get_parent_name(np, 0);
of_property_read_string(np, "clock-output-names", &clk_name);
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index aac54e9d54ee..1d804f03a21d 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -582,10 +582,17 @@ static int ak4671_set_bias_level(struct snd_soc_component *component,
#define AK4671_FORMATS SNDRV_PCM_FMTBIT_S16_LE
+static const u64 ak4671_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A;
+
static const struct snd_soc_dai_ops ak4671_dai_ops = {
.hw_params = ak4671_hw_params,
.set_sysclk = ak4671_set_dai_sysclk,
.set_fmt = ak4671_set_dai_fmt,
+ .auto_selectable_formats = &ak4671_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ak4671_dai = {
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index 6525d50b7ab2..933ca90b7e28 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -128,10 +128,16 @@ static int ak5386_hw_free(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 ak5386_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ak5386_dai_ops = {
.set_fmt = ak5386_set_dai_fmt,
.hw_params = ak5386_hw_params,
.hw_free = ak5386_hw_free,
+ .auto_selectable_formats = &ak5386_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ak5386_dai = {
diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c
index 691db6565c20..0c9cdf9a53bb 100644
--- a/sound/soc/codecs/ak5558.c
+++ b/sound/soc/codecs/ak5558.c
@@ -285,12 +285,19 @@ static int ak5558_startup(struct snd_pcm_substream *substream,
&ak5558_rate_constraints);
}
+static const u64 ak5558_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops ak5558_dai_ops = {
.startup = ak5558_startup,
.hw_params = ak5558_hw_params,
.set_fmt = ak5558_set_dai_fmt,
.set_tdm_slot = ak5558_set_tdm_slot,
+ .auto_selectable_formats = &ak5558_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ak5558_dai = {
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index f5a666c49361..96bb1d592772 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -822,12 +822,23 @@ static int alc5623_set_bias_level(struct snd_soc_component *component,
| SNDRV_PCM_FMTBIT_S24_LE \
| SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 alc5623_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops alc5623_dai_ops = {
.hw_params = alc5623_pcm_hw_params,
.mute_stream = alc5623_mute,
.set_fmt = alc5623_set_dai_fmt,
.set_sysclk = alc5623_set_dai_sysclk,
.set_pll = alc5623_set_dai_pll,
+ .auto_selectable_formats = &alc5623_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 2fd3dd7ef8b3..a5e95f3fc247 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1003,12 +1003,25 @@ static int alc5632_set_bias_level(struct snd_soc_component *component,
| SNDRV_PCM_FMTBIT_S24_LE \
| SNDRV_PCM_FMTBIT_S32_LE)
+
+static const u64 alc5632_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops alc5632_dai_ops = {
.hw_params = alc5632_pcm_hw_params,
.mute_stream = alc5632_mute,
.set_fmt = alc5632_set_dai_fmt,
.set_sysclk = alc5632_set_dai_sysclk,
.set_pll = alc5632_set_dai_pll,
+ .auto_selectable_formats = &alc5632_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/arizona-jack.c b/sound/soc/codecs/arizona-jack.c
index 819d080b1188..6587d46a1867 100644
--- a/sound/soc/codecs/arizona-jack.c
+++ b/sound/soc/codecs/arizona-jack.c
@@ -699,6 +699,7 @@ err:
snd_soc_jack_report(info->jack, SND_JACK_HEADPHONE,
SND_JACK_LINEOUT | SND_JACK_HEADPHONE);
+ pm_runtime_put_autosuspend(arizona->dev);
info->hpdet_active = false;
}
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 4411165c3734..7c296c5b24f7 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -2016,6 +2016,27 @@ static int arizona_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
+static const u64 arizona_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /*
+ * 2nd priority
+ *
+ * _DSP_B / _LEFT_J can't be clock consumer (= arizona_set_fmt()).
+ */
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+};
+
const struct snd_soc_dai_ops arizona_dai_ops = {
.startup = arizona_startup,
.set_fmt = arizona_set_fmt,
@@ -2023,6 +2044,8 @@ const struct snd_soc_dai_ops arizona_dai_ops = {
.hw_params = arizona_hw_params,
.set_sysclk = arizona_dai_set_sysclk,
.set_tristate = arizona_set_tristate,
+ .auto_selectable_formats = arizona_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(arizona_selectable_formats),
};
EXPORT_SYMBOL_GPL(arizona_dai_ops);
diff --git a/sound/soc/codecs/cpcap.c b/sound/soc/codecs/cpcap.c
index fc41e1547bda..dda68165b817 100644
--- a/sound/soc/codecs/cpcap.c
+++ b/sound/soc/codecs/cpcap.c
@@ -1244,11 +1244,24 @@ static int cpcap_hifi_set_mute(struct snd_soc_dai *dai, int mute, int direction)
return regmap_update_bits(cpcap->regmap, reg, mask, val);
}
+static const u64 cpcap_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cpcap_dai_hifi_ops = {
.hw_params = cpcap_hifi_hw_params,
.set_sysclk = cpcap_hifi_set_dai_sysclk,
.set_fmt = cpcap_hifi_set_dai_fmt,
.mute_stream = cpcap_hifi_set_mute,
+ .auto_selectable_formats = &cpcap_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1406,6 +1419,8 @@ static const struct snd_soc_dai_ops cpcap_dai_voice_ops = {
.set_sysclk = cpcap_voice_set_dai_sysclk,
.set_fmt = cpcap_voice_set_dai_fmt,
.mute_stream = cpcap_voice_set_mute,
+ .auto_selectable_formats = &cpcap_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1607,21 +1622,12 @@ static int cpcap_soc_probe(struct snd_soc_component *component)
{
struct platform_device *pdev = to_platform_device(component->dev);
struct snd_soc_card *card = component->card;
- struct cpcap_audio *cpcap;
+ struct cpcap_audio *cpcap = dev_get_drvdata(component->dev);
int err;
- cpcap = devm_kzalloc(component->dev, sizeof(*cpcap), GFP_KERNEL);
- if (!cpcap)
- return -ENOMEM;
-
snd_soc_component_set_drvdata(component, cpcap);
cpcap->component = component;
- cpcap->vaudio = devm_regulator_get(component->dev, "VAUDIO");
- if (IS_ERR(cpcap->vaudio))
- return dev_err_probe(component->dev, PTR_ERR(cpcap->vaudio),
- "Cannot get VAUDIO regulator\n");
-
err = snd_soc_card_jack_new(card, "Headphones",
SND_JACK_HEADSET | SND_JACK_BTN_0,
&cpcap->jack);
@@ -1645,13 +1651,13 @@ static int cpcap_soc_probe(struct snd_soc_component *component)
if (cpcap->hsirq < 0)
return cpcap->hsirq;
- err = devm_request_threaded_irq(component->dev, cpcap->hsirq, NULL,
- cpcap_hs_irq_thread,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- "cpcap-codec-hs",
- component);
+ err = request_threaded_irq(cpcap->hsirq, NULL,
+ cpcap_hs_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-hs",
+ component);
if (err) {
dev_warn(component->dev, "no HS irq%i: %i\n",
cpcap->hsirq, err);
@@ -1659,25 +1665,27 @@ static int cpcap_soc_probe(struct snd_soc_component *component)
}
cpcap->mb2irq = platform_get_irq_byname(pdev, "mb2");
- if (cpcap->mb2irq < 0)
- return cpcap->mb2irq;
-
- err = devm_request_threaded_irq(component->dev, cpcap->mb2irq, NULL,
- cpcap_mb2_irq_thread,
- IRQF_TRIGGER_RISING |
- IRQF_TRIGGER_FALLING |
- IRQF_ONESHOT,
- "cpcap-codec-mb2",
- component);
+ if (cpcap->mb2irq < 0) {
+ err = cpcap->mb2irq;
+ goto err_free_hsirq;
+ }
+
+ err = request_threaded_irq(cpcap->mb2irq, NULL,
+ cpcap_mb2_irq_thread,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT,
+ "cpcap-codec-mb2",
+ component);
if (err) {
dev_warn(component->dev, "no MB2 irq%i: %i\n",
cpcap->mb2irq, err);
- return err;
+ goto err_free_hsirq;
}
err = cpcap_audio_reset(component, false);
if (err)
- return err;
+ goto err_free_mb2irq;
cpcap_hs_irq_thread(cpcap->hsirq, component);
@@ -1685,6 +1693,13 @@ static int cpcap_soc_probe(struct snd_soc_component *component)
enable_irq_wake(cpcap->mb2irq);
return 0;
+
+err_free_mb2irq:
+ free_irq(cpcap->mb2irq, component);
+err_free_hsirq:
+ free_irq(cpcap->hsirq, component);
+
+ return err;
}
static void cpcap_soc_remove(struct snd_soc_component *component)
@@ -1693,6 +1708,9 @@ static void cpcap_soc_remove(struct snd_soc_component *component)
disable_irq_wake(cpcap->hsirq);
disable_irq_wake(cpcap->mb2irq);
+
+ free_irq(cpcap->mb2irq, component);
+ free_irq(cpcap->hsirq, component);
}
static int cpcap_set_bias_level(struct snd_soc_component *component,
@@ -1739,11 +1757,24 @@ static int cpcap_codec_probe(struct platform_device *pdev)
{
struct device_node *codec_node =
of_get_child_by_name(pdev->dev.parent->of_node, "audio-codec");
+ struct cpcap_audio *cpcap;
+
if (!codec_node)
return -ENODEV;
pdev->dev.of_node = codec_node;
+ cpcap = devm_kzalloc(&pdev->dev, sizeof(*cpcap), GFP_KERNEL);
+ if (!cpcap)
+ return -ENOMEM;
+
+ cpcap->vaudio = devm_regulator_get(&pdev->dev, "VAUDIO");
+ if (IS_ERR(cpcap->vaudio))
+ return dev_err_probe(&pdev->dev, PTR_ERR(cpcap->vaudio),
+ "Cannot get VAUDIO regulator\n");
+
+ platform_set_drvdata(pdev, cpcap);
+
return devm_snd_soc_register_component(&pdev->dev, &soc_codec_dev_cpcap,
cpcap_dai, ARRAY_SIZE(cpcap_dai));
}
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index e217da124549..b8c19f175701 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -578,7 +578,7 @@ static int cs35l33_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
| CS35L33_X_LOC);
}
- /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ /* disconnect {vp,vbst}_mon routes: enable later if set in tx_mask*/
snd_soc_dapm_del_routes(dapm, cs35l33_vp_vbst_mon_route,
ARRAY_SIZE(cs35l33_vp_vbst_mon_route));
@@ -663,12 +663,18 @@ static int cs35l33_component_set_sysclk(struct snd_soc_component *component,
return 0;
}
+static const u64 cs35l33_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A;
+
static const struct snd_soc_dai_ops cs35l33_ops = {
.startup = cs35l33_pcm_startup,
.set_tristate = cs35l33_set_tristate,
.set_fmt = cs35l33_set_dai_fmt,
.hw_params = cs35l33_pcm_hw_params,
.set_tdm_slot = cs35l33_set_tdm_slot,
+ .auto_selectable_formats = &cs35l33_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs35l33_dai = {
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index 3f265bd760f7..a72be629a31e 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -297,7 +297,7 @@ static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
CS35L34_X_STATE | CS35L34_X_LOC,
CS35L34_X_STATE | CS35L34_X_LOC);
- /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ /* disconnect {vp,vbst}_mon routes: enable later if set in tx_mask*/
while (slot >= 0) {
/* configure VMON_TX_LOC */
if (slot_num == 0)
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index b2439ec3c19f..302d838f4c44 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -657,17 +657,24 @@ static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
+static const u64 cs35l35_selectable_formats_i2s = SND_SOC_POSSIBLE_DAIFMT_I2S;
+static const u64 cs35l35_selectable_formats_pdm = SND_SOC_POSSIBLE_DAIFMT_PDM;
+
static const struct snd_soc_dai_ops cs35l35_ops = {
.startup = cs35l35_pcm_startup,
.set_fmt = cs35l35_set_dai_fmt,
.hw_params = cs35l35_hw_params,
.set_sysclk = cs35l35_dai_set_sysclk,
+ .auto_selectable_formats = &cs35l35_selectable_formats_i2s,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
.startup = cs35l35_pdm_startup,
.set_fmt = cs35l35_set_dai_fmt,
.hw_params = cs35l35_hw_params,
+ .auto_selectable_formats = &cs35l35_selectable_formats_pdm,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs35l35_dai[] = {
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index 89645327945f..5fc8ec292e3c 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -947,10 +947,66 @@ static const struct cs35l36_pll_config *cs35l36_get_clk_config(
return NULL;
}
+static void cs35l36_mask_to_slots(struct cs35l36_private *cs35l36,
+ unsigned long mask, unsigned int base_reg,
+ unsigned int nchan)
+{
+ unsigned int chan = 0, shift;
+ int slot;
+
+ /* Two 6-bit slot fields per register, at bits 0 and 16 */
+ for_each_set_bit(slot, &mask, BITS_PER_TYPE(mask)) {
+ if (chan == nchan) {
+ dev_warn(cs35l36->dev,
+ "Too many slots in TDM mask: %lx\n", mask);
+ return;
+ }
+
+ shift = (chan % 2) * CS35L36_ASP_TX2_SLOT_SHIFT;
+ regmap_update_bits(cs35l36->regmap, base_reg + (chan / 2) * 4,
+ CS35L36_ASP_RX1_SLOT_MASK << shift,
+ slot << shift);
+ chan++;
+ }
+}
+
+static int cs35l36_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs35l36_private *cs35l36 =
+ snd_soc_component_get_drvdata(dai->component);
+
+ /* Note: rx/tx is from point of view of the CPU end */
+ if (!slots || !rx_mask)
+ rx_mask = BIT(0); /* ASPRX1 in slot 0 */
+
+ if (!slots || !tx_mask)
+ tx_mask = GENMASK(7, 0); /* ASPTX1..8 in slots 0..7 */
+
+ cs35l36_mask_to_slots(cs35l36, rx_mask, CS35L36_ASP_RX1_SLOT, 1);
+ cs35l36_mask_to_slots(cs35l36, tx_mask, CS35L36_ASP_TX1_TX2_SLOT, 8);
+
+ return 0;
+}
+
+static const u64 cs35l36_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_GATED |
+ SND_SOC_POSSIBLE_DAIFMT_CONT |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs35l36_ops = {
.set_fmt = cs35l36_set_dai_fmt,
.hw_params = cs35l36_pcm_hw_params,
.set_sysclk = cs35l36_dai_set_sysclk,
+ .auto_selectable_formats = &cs35l36_selectable_formats,
+ .num_auto_selectable_formats = 1,
+ .set_tdm_slot = cs35l36_set_tdm_slot,
};
#define CS35L36_RATES ( \
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index b2a076706c79..d286025183f4 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -953,11 +953,21 @@ static void cs35l41_component_remove(struct snd_soc_component *component)
wm_adsp2_component_remove(&cs35l41->dsp, component);
}
+static const u64 cs35l41_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs35l41_ops = {
.set_fmt = cs35l41_set_dai_fmt,
.hw_params = cs35l41_pcm_hw_params,
.set_sysclk = cs35l41_dai_set_sysclk,
.set_channel_map = cs35l41_set_channel_map,
+ .auto_selectable_formats = &cs35l41_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define CS35L41_RATES ( \
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 764dbaa35042..57982b8ae645 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -51,6 +51,8 @@ static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_WKI2C_CTL, 0x00000030 },
{ CS35L45_REFCLK_INPUT, 0x00000510 },
{ CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
+ { CS35L45_SYNC_TX_RX_ENABLES, 0x00000200 },
+ { CS35L45_SYNC_SW_TX_ID, 0x00000000 },
{ CS35L45_ASP_ENABLES1, 0x00000000 },
{ CS35L45_ASP_CONTROL1, 0x00000028 },
{ CS35L45_ASP_CONTROL2, 0x18180200 },
@@ -134,6 +136,8 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_PWRMGT_STS:
case CS35L45_REFCLK_INPUT:
case CS35L45_GLOBAL_SAMPLE_RATE:
+ case CS35L45_SYNC_TX_RX_ENABLES:
+ case CS35L45_SYNC_SW_TX_ID:
case CS35L45_ASP_ENABLES1:
case CS35L45_ASP_CONTROL1:
case CS35L45_ASP_CONTROL2:
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index a032bb23b4ac..9eef7c8b1837 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -6,6 +6,7 @@
//
// Author: James Schulman <james.schulman@cirrus.com>
+#include <linux/bitfield.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@@ -195,6 +196,44 @@ static int cs35l45_activate_ctl(struct snd_soc_component *component,
return 0;
}
+static int cs35l45_sync_en_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = cs35l45->sync_en;
+
+ return 0;
+}
+
+static int cs35l45_sync_en_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l45_private *cs35l45 =
+ snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm =
+ snd_soc_component_to_dapm(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ if ((bool)ucontrol->value.integer.value[0] == cs35l45->sync_en) {
+ snd_soc_dapm_mutex_unlock(dapm);
+ return 0;
+ }
+
+ regmap_assign_bits(cs35l45->regmap, CS35L45_BLOCK_ENABLES2, CS35L45_SYNC_EN_MASK,
+ (bool)ucontrol->value.integer.value[0]);
+
+ cs35l45->sync_en = (bool)ucontrol->value.integer.value[0];
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 1;
+}
+
static int cs35l45_amplifier_mode_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -591,6 +630,18 @@ static SOC_ENUM_SINGLE_DECL(amplifier_mode_enum, SND_SOC_NOPM, 0,
static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 1000, 300, 0);
static const DECLARE_TLV_DB_SCALE(cs35l45_dig_pcm_vol_tlv, -10225, 25, true);
+static const struct snd_kcontrol_new cs35l45_sync_controls[] = {
+ SOC_SINGLE_BOOL_EXT("SYNC Switch", 0, cs35l45_sync_en_get, cs35l45_sync_en_put),
+ SOC_SINGLE("SYNC LSW RX Switch", CS35L45_SYNC_TX_RX_ENABLES,
+ CS35L45_SYNC_LSW_RX_EN_SHIFT, 1, 0),
+ SOC_SINGLE("SYNC LSW TX Switch", CS35L45_SYNC_TX_RX_ENABLES,
+ CS35L45_SYNC_LSW_TX_EN_SHIFT, 1, 0),
+ SOC_SINGLE("SYNC SW RX Switch", CS35L45_SYNC_TX_RX_ENABLES,
+ CS35L45_SYNC_SW_RX_EN_SHIFT, 1, 0),
+ SOC_SINGLE("SYNC SW TX Switch", CS35L45_SYNC_TX_RX_ENABLES,
+ CS35L45_SYNC_SW_TX_EN_SHIFT, 1, 0),
+};
+
static const struct snd_kcontrol_new cs35l45_controls[] = {
SOC_ENUM_EXT("Amplifier Mode", amplifier_mode_enum,
cs35l45_amplifier_mode_get, cs35l45_amplifier_mode_put),
@@ -846,12 +897,22 @@ static int cs35l45_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
return 0;
}
+static const u64 cs35l45_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs35l45_asp_dai_ops = {
.set_fmt = cs35l45_asp_set_fmt,
.hw_params = cs35l45_asp_hw_params,
.set_tdm_slot = cs35l45_asp_set_tdm_slot,
.set_sysclk = cs35l45_asp_set_sysclk,
.mute_stream = cs35l45_mute_stream,
+ .auto_selectable_formats = &cs35l45_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs35l45_dai[] = {
@@ -880,6 +941,19 @@ static struct snd_soc_dai_driver cs35l45_dai[] = {
static int cs35l45_component_probe(struct snd_soc_component *component)
{
struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_add_component_controls(component, cs35l45_controls,
+ ARRAY_SIZE(cs35l45_controls));
+ if (ret < 0)
+ return ret;
+
+ if (cs35l45->sync_pin_set) {
+ ret = snd_soc_add_component_controls(component, cs35l45_sync_controls,
+ ARRAY_SIZE(cs35l45_sync_controls));
+ if (ret < 0)
+ return ret;
+ }
return wm_adsp2_component_probe(&cs35l45->dsp, component);
}
@@ -901,9 +975,6 @@ static const struct snd_soc_component_driver cs35l45_component = {
.dapm_routes = cs35l45_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(cs35l45_dapm_routes),
- .controls = cs35l45_controls,
- .num_controls = ARRAY_SIZE(cs35l45_controls),
-
.name = "cs35l45",
.endianness = 1,
@@ -1064,6 +1135,19 @@ static int cs35l45_sys_resume(struct device *dev)
return 0;
}
+static void cs35l45_apply_sync_property_config(struct cs35l45_private *cs35l45)
+{
+ unsigned int val;
+
+ if (device_property_read_u32(cs35l45->dev, "cirrus,sync-lsw-txid", &val) == 0)
+ regmap_update_bits(cs35l45->regmap, CS35L45_SYNC_SW_TX_ID,
+ CS35L45_SYNC_LSW_TXID_MASK, val << CS35L45_SYNC_LSW_TXID_SHIFT);
+
+ if (device_property_read_u32(cs35l45->dev, "cirrus,sync-sw-txid", &val) == 0)
+ regmap_update_bits(cs35l45->regmap, CS35L45_SYNC_SW_TX_ID,
+ CS35L45_SYNC_SW_TXID_MASK, val);
+}
+
static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
{
struct device_node *node = cs35l45->dev->of_node;
@@ -1110,10 +1194,15 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
val << CS35L45_GPIO_POL_SHIFT);
ret = of_property_read_u32(child, "gpio-ctrl", &val);
- if (!ret)
+ if (!ret) {
+ if ((i == 0) && (val == CS35L45_GP1_CTRL_MDSYNC)) {
+ cs35l45->sync_pin_set = true;
+ cs35l45_apply_sync_property_config(cs35l45);
+ }
regmap_update_bits(cs35l45->regmap, pad_regs[i],
CS35L45_GPIO_CTRL_MASK,
val << CS35L45_GPIO_CTRL_SHIFT);
+ }
ret = of_property_read_u32(child, "gpio-invert", &val);
if (!ret) {
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index 7a790d2acac7..f8a4713ae92f 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -35,6 +35,8 @@
#define CS35L45_PWRMGT_STS 0x0000290C
#define CS35L45_REFCLK_INPUT 0x00002C04
#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
+#define CS35L45_SYNC_TX_RX_ENABLES 0x00003400
+#define CS35L45_SYNC_SW_TX_ID 0x00003408
#define CS35L45_BOOST_CCM_CFG 0x00003808
#define CS35L45_BOOST_DCM_CFG 0x0000380C
#define CS35L45_BOOST_OV_CFG 0x0000382C
@@ -183,6 +185,8 @@
#define CS35L45_ASP_EN_SHIFT 27
#define CS35L45_AMP_DRE_EN_SHIFT 20
#define CS35L45_AMP_DRE_EN_MASK BIT(20)
+#define CS35L45_SYNC_EN_SHIFT 8
+#define CS35L45_SYNC_EN_MASK BIT(8)
#define CS35L45_MEM_RDY_SHIFT 1
#define CS35L45_MEM_RDY_MASK BIT(1)
@@ -220,6 +224,22 @@
#define CS35L45_44P100_KHZ 0x0B
#define CS35L45_88P200_KHZ 0x0C
+/* SYNC_TX_RX_ENABLES */
+#define CS35L45_SYNC_LSW_RX_EN_SHIFT 19
+#define CS35L45_SYNC_LSW_RX_EN_MASK BIT(19)
+#define CS35L45_SYNC_LSW_TX_EN_SHIFT 18
+#define CS35L45_SYNC_LSW_TX_EN_MASK BIT(18)
+#define CS35L45_SYNC_SW_RX_EN_SHIFT 17
+#define CS35L45_SYNC_SW_RX_EN_MASK BIT(17)
+#define CS35L45_SYNC_SW_TX_EN_SHIFT 16
+#define CS35L45_SYNC_SW_TX_EN_MASK BIT(16)
+
+/* SYNC_SW_TX_ID */
+#define CS35L45_SYNC_LSW_TXID_SHIFT 8
+#define CS35L45_SYNC_LSW_TXID_MASK GENMASK(10, 8)
+#define CS35L45_SYNC_SW_TXID_SHIFT 0
+#define CS35L45_SYNC_SW_TXID_MASK GENMASK(2, 0)
+
/* ASP_ENABLES_1 */
#define CS35L45_ASP_RX2_EN_SHIFT 17
#define CS35L45_ASP_RX1_EN_SHIFT 16
@@ -320,6 +340,7 @@
#define CS35L45_GPIO_CTRL_MASK GENMASK(22, 20)
#define CS35L45_GPIO_INVERT_SHIFT 19
#define CS35L45_GPIO_INVERT_MASK BIT(19)
+#define CS35L45_GP1_CTRL_MDSYNC 0x2
/* CS35L45_IRQ1_EINT_1 */
#define CS35L45_BST_UVP_ERR_SHIFT 7
@@ -493,6 +514,8 @@ struct cs35l45_private {
struct regulator *vdd_a;
bool initialized;
bool sysclk_set;
+ bool sync_en;
+ bool sync_pin_set;
u8 slot_width;
u8 slot_count;
int amplifier_mode;
diff --git a/sound/soc/codecs/cs35l56-shared-test.c b/sound/soc/codecs/cs35l56-shared-test.c
index 4f52c8a192e5..34bb3d44b51f 100644
--- a/sound/soc/codecs/cs35l56-shared-test.c
+++ b/sound/soc/codecs/cs35l56-shared-test.c
@@ -29,6 +29,7 @@ struct cs35l56_shared_test_priv {
struct faux_device *gpio_dev;
struct cs35l56_shared_test_mock_gpio *gpio_priv;
struct regmap *registers;
+ const struct regmap_config *regmap_config;
unsigned int reg_offset;
struct cs35l56_base *cs35l56_base;
u8 applied_pad_pull_state[CS35L56_MAX_GPIO];
@@ -614,6 +615,55 @@ static void cs35l56_shared_test_get_speaker_id_from_host_gpio(struct kunit *test
KUNIT_EXPECT_EQ(test, cs35l56_get_speaker_id(priv->cs35l56_base), param->spkid);
}
+static void cs35l56_shared_test_sorted_regmap_defaults(struct kunit *test)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ const struct reg_default *defaults;
+ int i;
+
+ KUNIT_ASSERT_NOT_NULL(test, priv->regmap_config);
+ defaults = priv->regmap_config->reg_defaults;
+ KUNIT_ASSERT_NOT_NULL(test, defaults);
+ KUNIT_ASSERT_NE(test, priv->regmap_config->num_reg_defaults, 0);
+
+ for (i = 1; i < priv->regmap_config->num_reg_defaults; i++)
+ KUNIT_EXPECT_LT(test, defaults[i - 1].reg, defaults[i].reg);
+}
+
+static void cs35l56_shared_test_regmap_defaults_not_volatile(struct kunit *test)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ const struct regmap_config *config = priv->regmap_config;
+ int i;
+
+ KUNIT_ASSERT_NOT_NULL(test, config);
+ KUNIT_ASSERT_NOT_NULL(test, config->volatile_reg);
+
+ for (i = 0; i < config->num_reg_defaults; i++) {
+ KUNIT_EXPECT_FALSE(test,
+ config->volatile_reg(cs35l56_base->dev,
+ config->reg_defaults[i].reg));
+ }
+}
+
+static void cs35l56_shared_test_regmap_defaults_readable(struct kunit *test)
+{
+ struct cs35l56_shared_test_priv *priv = test->priv;
+ struct cs35l56_base *cs35l56_base = priv->cs35l56_base;
+ const struct regmap_config *config = priv->regmap_config;
+ int i;
+
+ KUNIT_ASSERT_NOT_NULL(test, config);
+ KUNIT_ASSERT_NOT_NULL(test, config->readable_reg);
+
+ for (i = 0; i < config->num_reg_defaults; i++) {
+ KUNIT_EXPECT_TRUE(test,
+ config->readable_reg(cs35l56_base->dev,
+ config->reg_defaults[i].reg));
+ }
+}
+
static int cs35l56_shared_test_case_regmap_init(struct kunit *test,
const struct regmap_config *regmap_config)
{
@@ -645,6 +695,8 @@ static int cs35l56_shared_test_case_regmap_init(struct kunit *test,
kunit_add_action_or_reset(test, regmap_exit_wrapper,
cs35l56_base->regmap));
+ priv->regmap_config = regmap_config;
+
return 0;
}
@@ -862,6 +914,10 @@ static struct kunit_case cs35l56_shared_test_cases[] = {
cs35l56_shared_test_host_gpio_spkid_gen_params,
{ KUNIT_SPEED_SLOW }),
+ KUNIT_CASE(cs35l56_shared_test_sorted_regmap_defaults),
+ KUNIT_CASE(cs35l56_shared_test_regmap_defaults_not_volatile),
+ KUNIT_CASE(cs35l56_shared_test_regmap_defaults_readable),
+
{ }
};
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 35d210626627..74129fcc79dc 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -613,12 +613,22 @@ static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
+static const u64 cs35l56_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs35l56_ops = {
.probe = cs35l56_asp_dai_probe,
.set_fmt = cs35l56_asp_dai_set_fmt,
.set_tdm_slot = cs35l56_asp_dai_set_tdm_slot,
.hw_params = cs35l56_asp_dai_hw_params,
.set_sysclk = cs35l56_asp_dai_set_sysclk,
+ .auto_selectable_formats = &cs35l56_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream,
diff --git a/sound/soc/codecs/cs40l50-codec.c b/sound/soc/codecs/cs40l50-codec.c
index 676367163067..698aa59194f4 100644
--- a/sound/soc/codecs/cs40l50-codec.c
+++ b/sound/soc/codecs/cs40l50-codec.c
@@ -233,10 +233,19 @@ static int cs40l50_set_dai_bclk_ratio(struct snd_soc_dai *dai, unsigned int rati
return 0;
}
+static const u64 cs40l50_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs40l50_dai_ops = {
.set_fmt = cs40l50_set_dai_fmt,
.set_bclk_ratio = cs40l50_set_dai_bclk_ratio,
.hw_params = cs40l50_hw_params,
+ .auto_selectable_formats = &cs40l50_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs40l50_dai[] = {
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
index a889fbd519a8..a6ead1ee3708 100644
--- a/sound/soc/codecs/cs4234.c
+++ b/sound/soc/codecs/cs4234.c
@@ -560,11 +560,20 @@ static int cs4234_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask
return 0;
}
+static const u64 cs4234_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops cs4234_dai_ops = {
.set_fmt = cs4234_dai_set_fmt,
.hw_params = cs4234_dai_hw_params,
.startup = cs4234_dai_startup,
.set_tdm_slot = cs4234_dai_set_tdm_slot,
+ .auto_selectable_formats = &cs4234_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs4234_dai[] = {
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 0b7d6826f4e9..97e32e6ccb84 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -495,11 +495,23 @@ static int cs4265_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+/*
+ * NOTE
+ *
+ * auto_select doesn't include _RIGHT_J, because capture can't use it
+ * (= cs4265_pcm_hw_params()). It can be selected via Card directly if needed.
+ */
+static const u64 cs4265_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops cs4265_ops = {
.hw_params = cs4265_pcm_hw_params,
.mute_stream = cs4265_mute,
.set_fmt = cs4265_set_fmt,
.set_sysclk = cs4265_set_sysclk,
+ .auto_selectable_formats = &cs4265_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 47b2f903a32c..aa3bbb951945 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -456,11 +456,17 @@ static const struct snd_kcontrol_new cs4270_snd_controls[] = {
snd_soc_get_volsw, cs4270_soc_put_mute),
};
+static const u64 cs4270_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops cs4270_dai_ops = {
.hw_params = cs4270_hw_params,
.set_sysclk = cs4270_set_dai_sysclk,
.set_fmt = cs4270_set_dai_fmt,
.mute_stream = cs4270_dai_mute,
+ .auto_selectable_formats = &cs4270_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index d8cdd37e9112..398d8c24a9ce 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -455,11 +455,17 @@ static const struct snd_kcontrol_new cs4271_snd_controls[] = {
7, 1, 1),
};
+static const u64 cs4271_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops cs4271_dai_ops = {
.hw_params = cs4271_hw_params,
.set_sysclk = cs4271_set_dai_sysclk,
.set_fmt = cs4271_set_dai_fmt,
.mute_stream = cs4271_mute_stream,
+ .auto_selectable_formats = &cs4271_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs4271_dai = {
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
index fadf68860601..b5d69d5516a6 100644
--- a/sound/soc/codecs/cs42l42.c
+++ b/sound/soc/codecs/cs42l42.c
@@ -1122,6 +1122,13 @@ EXPORT_SYMBOL_NS_GPL(cs42l42_mute_stream, "SND_SOC_CS42L42_CORE");
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 cs42l42_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs42l42_ops = {
.startup = cs42l42_dai_startup,
.hw_params = cs42l42_pcm_hw_params,
@@ -1129,6 +1136,8 @@ static const struct snd_soc_dai_ops cs42l42_ops = {
.set_sysclk = cs42l42_set_sysclk,
.set_bclk_ratio = cs42l42_set_bclk_ratio,
.mute_stream = cs42l42_mute_stream,
+ .auto_selectable_formats = &cs42l42_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
struct snd_soc_dai_driver cs42l42_dai = {
@@ -1673,7 +1682,7 @@ irqreturn_t cs42l42_irq_thread(int irq, void *data)
if (cs42l42->suspended || !cs42l42->init_done)
return IRQ_NONE;
- /* Read sticky registers to clear interurpt */
+ /* Read sticky registers to clear interrupt */
for (i = 0; i < ARRAY_SIZE(stickies); i++) {
regmap_read(cs42l42->regmap, irq_params_table[i].status_addr,
&(stickies[i]));
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
index 1b817e1a6ea5..b6238f89c789 100644
--- a/sound/soc/codecs/cs42l43.c
+++ b/sound/soc/codecs/cs42l43.c
@@ -671,6 +671,16 @@ static int cs42l43_dai_remove(struct snd_soc_dai *dai)
return 0;
}
+static const u64 cs42l43_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs42l43_asp_ops = {
.probe = cs42l43_dai_probe,
.remove = cs42l43_dai_remove,
@@ -678,6 +688,8 @@ static const struct snd_soc_dai_ops cs42l43_asp_ops = {
.hw_params = cs42l43_asp_hw_params,
.set_fmt = cs42l43_asp_set_fmt,
.set_tdm_slot = cs42l43_asp_set_tdm_slot,
+ .auto_selectable_formats = &cs42l43_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int cs42l43_sdw_hw_params(struct snd_pcm_substream *substream,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 0519affe0ed8..4b964c713398 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -533,11 +533,18 @@ static int cs42l51_of_xlate_dai_id(struct snd_soc_component *component,
return 0;
}
+static const u64 cs42l51_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops cs42l51_dai_ops = {
.hw_params = cs42l51_hw_params,
.set_sysclk = cs42l51_set_dai_sysclk,
.set_fmt = cs42l51_set_dai_fmt,
.mute_stream = cs42l51_dai_mute,
+ .auto_selectable_formats = &cs42l51_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 9d6bcfbbf0b2..5f033556099a 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -883,11 +883,24 @@ static int cs42l52_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_U20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE)
+static const u64 cs42l52_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs42l52_ops = {
.hw_params = cs42l52_pcm_hw_params,
.mute_stream = cs42l52_mute,
.set_fmt = cs42l52_set_fmt,
.set_sysclk = cs42l52_set_sysclk,
+ .auto_selectable_formats = &cs42l52_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1000,7 +1013,7 @@ static void cs42l52_init_beep(struct snd_soc_component *component)
struct cs42l52_private *cs42l52 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l52->beep = devm_input_allocate_device(component->dev);
+ cs42l52->beep = input_allocate_device();
if (!cs42l52->beep) {
dev_err(component->dev, "Failed to allocate beep device\n");
return;
@@ -1021,8 +1034,10 @@ static void cs42l52_init_beep(struct snd_soc_component *component)
ret = input_register_device(cs42l52->beep);
if (ret != 0) {
+ input_free_device(cs42l52->beep);
cs42l52->beep = NULL;
dev_err(component->dev, "Failed to register beep device\n");
+ return;
}
ret = device_create_file(component->dev, &dev_attr_beep);
@@ -1038,7 +1053,10 @@ static void cs42l52_free_beep(struct snd_soc_component *component)
device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l52->beep_work);
- cs42l52->beep = NULL;
+ if (cs42l52->beep) {
+ input_unregister_device(cs42l52->beep);
+ cs42l52->beep = NULL;
+ }
snd_soc_component_update_bits(component, CS42L52_BEEP_TONE_CTL,
CS42L52_BEEP_EN_MASK, 0);
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index 4d9a22a1029c..ad45e8fdb393 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -960,11 +960,19 @@ static int cs42l56_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 cs42l56_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops cs42l56_ops = {
.hw_params = cs42l56_pcm_hw_params,
.mute_stream = cs42l56_mute,
.set_fmt = cs42l56_set_dai_fmt,
.set_sysclk = cs42l56_set_sysclk,
+ .auto_selectable_formats = &cs42l56_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1077,7 +1085,7 @@ static void cs42l56_init_beep(struct snd_soc_component *component)
struct cs42l56_private *cs42l56 = snd_soc_component_get_drvdata(component);
int ret;
- cs42l56->beep = devm_input_allocate_device(component->dev);
+ cs42l56->beep = input_allocate_device();
if (!cs42l56->beep) {
dev_err(component->dev, "Failed to allocate beep device\n");
return;
@@ -1098,8 +1106,10 @@ static void cs42l56_init_beep(struct snd_soc_component *component)
ret = input_register_device(cs42l56->beep);
if (ret != 0) {
+ input_free_device(cs42l56->beep);
cs42l56->beep = NULL;
dev_err(component->dev, "Failed to register beep device\n");
+ return;
}
ret = device_create_file(component->dev, &dev_attr_beep);
@@ -1115,7 +1125,10 @@ static void cs42l56_free_beep(struct snd_soc_component *component)
device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&cs42l56->beep_work);
- cs42l56->beep = NULL;
+ if (cs42l56->beep) {
+ input_unregister_device(cs42l56->beep);
+ cs42l56->beep = NULL;
+ }
snd_soc_component_update_bits(component, CS42L56_BEEP_TONE_CFG,
CS42L56_BEEP_EN_MASK, 0);
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 6ddc6549492a..72c4213ed74b 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -761,7 +761,7 @@ static const struct snd_soc_dapm_route cs42l73_audio_map[] = {
{"ASPOUTL", "ASP-IP Volume", "ASPL Output Mixer"},
{"ASPOUTR", "ASP-IP Volume", "ASPR Output Mixer"},
- /* Auxillary Capture */
+ /* Auxiliary Capture */
{"XSPL Output Mixer", NULL, "Input Left Capture"},
{"XSPR Output Mixer", NULL, "Input Right Capture"},
@@ -1126,7 +1126,7 @@ static int cs42l73_set_bias_level(struct snd_soc_component *component,
mdelay(cs42l73->shutdwn_delay);
cs42l73->shutdwn_delay = 0;
} else {
- mdelay(15); /* Min amount of time requred to power
+ mdelay(15); /* Min amount of time required to power
* down.
*/
}
@@ -1163,12 +1163,19 @@ static int cs42l73_pcm_startup(struct snd_pcm_substream *substream,
#define CS42L73_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 cs42l73_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops cs42l73_ops = {
.startup = cs42l73_pcm_startup,
.hw_params = cs42l73_pcm_hw_params,
.set_fmt = cs42l73_set_dai_fmt,
.set_sysclk = cs42l73_set_sysclk,
.set_tristate = cs42l73_set_tristate,
+ .auto_selectable_formats = &cs42l73_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs42l73_dai[] = {
diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c
index a545730b7ed9..77015590368b 100644
--- a/sound/soc/codecs/cs42l84.c
+++ b/sound/soc/codecs/cs42l84.c
@@ -662,11 +662,17 @@ static int cs42l84_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
return 0;
}
+static const u64 cs42l84_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs42l84_ops = {
.hw_params = cs42l84_pcm_hw_params,
.set_fmt = cs42l84_set_dai_fmt,
.set_sysclk = cs42l84_set_sysclk,
.mute_stream = cs42l84_mute_stream,
+ .auto_selectable_formats = &cs42l84_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define CS42L84_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index 5b689549c74e..fb9f4aff6bd8 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -393,12 +393,20 @@ static int cs42xx8_mute(struct snd_soc_dai *dai, int mute, int direction)
return 0;
}
+static const u64 cs42xx8_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A;
+
static const struct snd_soc_dai_ops cs42xx8_dai_ops = {
.set_fmt = cs42xx8_set_dai_fmt,
.set_sysclk = cs42xx8_set_dai_sysclk,
.hw_params = cs42xx8_hw_params,
.hw_free = cs42xx8_hw_free,
.mute_stream = cs42xx8_mute,
+ .auto_selectable_formats = &cs42xx8_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index 48d88dad4ceb..7bbb2e83a1ff 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -1609,12 +1609,24 @@ static int cs43130_set_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
+static const u64 cs43130_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs43130_pcm_ops = {
.startup = cs43130_pcm_startup,
.hw_params = cs43130_hw_params,
.hw_free = cs43130_hw_free,
.set_sysclk = cs43130_set_sysclk,
.set_fmt = cs43130_pcm_set_fmt,
+ .auto_selectable_formats = &cs43130_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops cs43130_dop_ops = {
@@ -1623,6 +1635,8 @@ static const struct snd_soc_dai_ops cs43130_dop_ops = {
.hw_free = cs43130_hw_free,
.set_sysclk = cs43130_set_sysclk,
.set_fmt = cs43130_pcm_set_fmt,
+ .auto_selectable_formats = &cs43130_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops cs43130_dsd_ops = {
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
index a44e6b3c298e..74544c58a415 100644
--- a/sound/soc/codecs/cs4341.c
+++ b/sound/soc/codecs/cs4341.c
@@ -171,10 +171,18 @@ static const struct snd_kcontrol_new cs4341_controls[] = {
SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0),
};
+static const u64 cs4341_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops cs4341_dai_ops = {
.set_fmt = cs4341_set_fmt,
.hw_params = cs4341_hw_params,
.mute_stream = cs4341_mute,
+ .auto_selectable_formats = &cs4341_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index 9a7f70175717..28320951e309 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -228,10 +228,22 @@ static const struct snd_soc_dapm_route cs4349_routes[] = {
#define CS4349_PCM_RATES SNDRV_PCM_RATE_8000_192000
+/*
+ * NOTE
+ *
+ * auto_select doesn't include _RIGHT_J, because 32bit width can't use it
+ * (= cs4349_pcm_hw_params()). It can be selected via Card directly if needed.
+ */
+static const u64 cs4349_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops cs4349_dai_ops = {
.hw_params = cs4349_pcm_hw_params,
.set_fmt = cs4349_set_dai_fmt,
.mute_stream = cs4349_mute,
+ .auto_selectable_formats = &cs4349_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/cs48l32.c b/sound/soc/codecs/cs48l32.c
index a9967771124e..c11fc1a588c4 100644
--- a/sound/soc/codecs/cs48l32.c
+++ b/sound/soc/codecs/cs48l32.c
@@ -2361,6 +2361,27 @@ static int cs48l32_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
+static const u64 cs48l32_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /*
+ * 2nd priority
+ *
+ * _DSP_B / _LEFT_J can't be clock consumer (= cs48l32_set_fmt()).
+ */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+};
+
static const struct snd_soc_dai_ops cs48l32_dai_ops = {
.probe = &cs48l32_asp_dai_probe,
.startup = &cs48l32_startup,
@@ -2368,6 +2389,8 @@ static const struct snd_soc_dai_ops cs48l32_dai_ops = {
.set_tdm_slot = &cs48l32_set_tdm_slot,
.hw_params = &cs48l32_hw_params,
.set_sysclk = &cs48l32_dai_set_sysclk,
+ .auto_selectable_formats = cs48l32_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(cs48l32_selectable_formats),
};
static int cs48l32_sysclk_ev(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c
index 2c7e33135911..e0ea246edbd8 100644
--- a/sound/soc/codecs/cs530x.c
+++ b/sound/soc/codecs/cs530x.c
@@ -945,10 +945,19 @@ static int cs530x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
val << CS530X_ASP_TDM_SLOT_SHIFT);
}
+static const u64 cs530x_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs530x_dai_ops = {
.set_fmt = cs530x_set_fmt,
.hw_params = cs530x_hw_params,
.set_tdm_slot = cs530x_set_tdm_slot,
+ .auto_selectable_formats = &cs530x_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_driver cs530x_dai = {
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index 511453b4c375..c9c682a312c3 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -833,6 +833,12 @@ static int cs53l30_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 cs53l30_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops cs53l30_ops = {
.hw_params = cs53l30_pcm_hw_params,
.set_fmt = cs53l30_set_dai_fmt,
@@ -840,6 +846,8 @@ static const struct snd_soc_dai_ops cs53l30_ops = {
.set_tristate = cs53l30_set_tristate,
.set_tdm_slot = cs53l30_set_dai_tdm_slot,
.mute_stream = cs53l30_mute_stream,
+ .auto_selectable_formats = &cs53l30_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver cs53l30_dai = {
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index 0dea84cd8ef2..d3c7a9370220 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -200,7 +200,7 @@ static int cx20442_write(struct snd_soc_component *component, unsigned int reg,
* Line discpline related code
*
* Any of the callback functions below can be used in two ways:
- * 1) registerd by a machine driver as one of line discipline operations,
+ * 1) registered by a machine driver as one of line discipline operations,
* 2) called from a machine's provided line discipline callback function
* in case when extra machine specific code must be run as well.
*/
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
index d02ab74e549b..5b9e9520ae01 100644
--- a/sound/soc/codecs/cx2072x.c
+++ b/sound/soc/codecs/cx2072x.c
@@ -1521,6 +1521,16 @@ static const struct snd_soc_component_driver soc_codec_driver_cx2072x = {
.endianness = 1,
};
+
+static const u64 cx2072x_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
/*
* DAI ops
*/
@@ -1529,6 +1539,8 @@ static const struct snd_soc_dai_ops cx2072x_dai_ops = {
.set_fmt = cx2072x_set_dai_fmt,
.hw_params = cx2072x_hw_params,
.set_bclk_ratio = cx2072x_set_dai_bclk_ratio,
+ .auto_selectable_formats = &cx2072x_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai)
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index 4df1a25e318b..40d759938edc 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1030,6 +1030,11 @@ err:
return -EINVAL;
}
+static const u64 da7210_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
/* DAI operations */
static const struct snd_soc_dai_ops da7210_dai_ops = {
.hw_params = da7210_hw_params,
@@ -1037,6 +1042,8 @@ static const struct snd_soc_dai_ops da7210_dai_ops = {
.set_sysclk = da7210_set_dai_sysclk,
.set_pll = da7210_set_dai_pll,
.mute_stream = da7210_mute,
+ .auto_selectable_formats = &da7210_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1151,7 +1158,7 @@ static int da7210_probe(struct snd_soc_component *component)
snd_soc_component_write(component, DA7210_PLL_DIV3, DA7210_MCLK_RANGE_10_20_MHZ |
DA7210_PLL_BYP);
- /* Diable PLL and bypass it */
+ /* Disable PLL and bypass it */
snd_soc_component_write(component, DA7210_PLL, DA7210_PLL_FS_48000);
/* Activate all enabled subsystem */
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index 93bd045ed870..a538b3dd1635 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -2166,12 +2166,24 @@ static int da7218_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 da7218_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops da7218_dai_ops = {
.hw_params = da7218_hw_params,
.set_sysclk = da7218_set_dai_sysclk,
.set_pll = da7218_set_dai_pll,
.set_fmt = da7218_set_dai_fmt,
.set_tdm_slot = da7218_set_dai_tdm_slot,
+ .auto_selectable_formats = &da7218_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define DA7218_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
@@ -2286,7 +2298,7 @@ static const struct of_device_id da7218_of_match[] = {
MODULE_DEVICE_TABLE(of, da7218_of_match);
static enum da7218_micbias_voltage
- da7218_of_micbias_lvl(struct snd_soc_component *component, u32 val)
+ da7218_of_micbias_lvl(struct device *dev, u32 val)
{
switch (val) {
case 1200:
@@ -2308,13 +2320,13 @@ static enum da7218_micbias_voltage
case 3000:
return DA7218_MICBIAS_3_0V;
default:
- dev_warn(component->dev, "Invalid micbias level");
+ dev_warn(dev, "Invalid micbias level");
return DA7218_MICBIAS_1_6V;
}
}
static enum da7218_mic_amp_in_sel
- da7218_of_mic_amp_in_sel(struct snd_soc_component *component, const char *str)
+ da7218_of_mic_amp_in_sel(struct device *dev, const char *str)
{
if (!strcmp(str, "diff")) {
return DA7218_MIC_AMP_IN_SEL_DIFF;
@@ -2323,39 +2335,39 @@ static enum da7218_mic_amp_in_sel
} else if (!strcmp(str, "se_n")) {
return DA7218_MIC_AMP_IN_SEL_SE_N;
} else {
- dev_warn(component->dev, "Invalid mic input type selection");
+ dev_warn(dev, "Invalid mic input type selection");
return DA7218_MIC_AMP_IN_SEL_DIFF;
}
}
static enum da7218_dmic_data_sel
- da7218_of_dmic_data_sel(struct snd_soc_component *component, const char *str)
+ da7218_of_dmic_data_sel(struct device *dev, const char *str)
{
if (!strcmp(str, "lrise_rfall")) {
return DA7218_DMIC_DATA_LRISE_RFALL;
} else if (!strcmp(str, "lfall_rrise")) {
return DA7218_DMIC_DATA_LFALL_RRISE;
} else {
- dev_warn(component->dev, "Invalid DMIC data type selection");
+ dev_warn(dev, "Invalid DMIC data type selection");
return DA7218_DMIC_DATA_LRISE_RFALL;
}
}
static enum da7218_dmic_samplephase
- da7218_of_dmic_samplephase(struct snd_soc_component *component, const char *str)
+ da7218_of_dmic_samplephase(struct device *dev, const char *str)
{
if (!strcmp(str, "on_clkedge")) {
return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
} else if (!strcmp(str, "between_clkedge")) {
return DA7218_DMIC_SAMPLE_BETWEEN_CLKEDGE;
} else {
- dev_warn(component->dev, "Invalid DMIC sample phase");
+ dev_warn(dev, "Invalid DMIC sample phase");
return DA7218_DMIC_SAMPLE_ON_CLKEDGE;
}
}
static enum da7218_dmic_clk_rate
- da7218_of_dmic_clkrate(struct snd_soc_component *component, u32 val)
+ da7218_of_dmic_clkrate(struct device *dev, u32 val)
{
switch (val) {
case 1500000:
@@ -2363,13 +2375,13 @@ static enum da7218_dmic_clk_rate
case 3000000:
return DA7218_DMIC_CLK_3_0MHZ;
default:
- dev_warn(component->dev, "Invalid DMIC clock rate");
+ dev_warn(dev, "Invalid DMIC clock rate");
return DA7218_DMIC_CLK_3_0MHZ;
}
}
static enum da7218_hpldet_jack_rate
- da7218_of_jack_rate(struct snd_soc_component *component, u32 val)
+ da7218_of_jack_rate(struct device *dev, u32 val)
{
switch (val) {
case 5:
@@ -2389,13 +2401,13 @@ static enum da7218_hpldet_jack_rate
case 640:
return DA7218_HPLDET_JACK_RATE_640US;
default:
- dev_warn(component->dev, "Invalid jack detect rate");
+ dev_warn(dev, "Invalid jack detect rate");
return DA7218_HPLDET_JACK_RATE_40US;
}
}
static enum da7218_hpldet_jack_debounce
- da7218_of_jack_debounce(struct snd_soc_component *component, u32 val)
+ da7218_of_jack_debounce(struct device *dev, u32 val)
{
switch (val) {
case 0:
@@ -2407,13 +2419,13 @@ static enum da7218_hpldet_jack_debounce
case 4:
return DA7218_HPLDET_JACK_DEBOUNCE_4;
default:
- dev_warn(component->dev, "Invalid jack debounce");
+ dev_warn(dev, "Invalid jack debounce");
return DA7218_HPLDET_JACK_DEBOUNCE_2;
}
}
static enum da7218_hpldet_jack_thr
- da7218_of_jack_thr(struct snd_soc_component *component, u32 val)
+ da7218_of_jack_thr(struct device *dev, u32 val)
{
switch (val) {
case 84:
@@ -2425,76 +2437,76 @@ static enum da7218_hpldet_jack_thr
case 96:
return DA7218_HPLDET_JACK_THR_96PCT;
default:
- dev_warn(component->dev, "Invalid jack threshold level");
+ dev_warn(dev, "Invalid jack threshold level");
return DA7218_HPLDET_JACK_THR_84PCT;
}
}
-static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *component)
+static struct da7218_pdata *da7218_of_to_pdata(struct device *dev,
+ struct da7218_priv *da7218)
{
- struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
- struct device_node *np = component->dev->of_node;
+ struct device_node *np = dev->of_node;
struct device_node *hpldet_np;
struct da7218_pdata *pdata;
struct da7218_hpldet_pdata *hpldet_pdata;
const char *of_str;
u32 of_val32;
- pdata = devm_kzalloc(component->dev, sizeof(*pdata), GFP_KERNEL);
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return NULL;
if (of_property_read_u32(np, "dlg,micbias1-lvl-millivolt", &of_val32) >= 0)
- pdata->micbias1_lvl = da7218_of_micbias_lvl(component, of_val32);
+ pdata->micbias1_lvl = da7218_of_micbias_lvl(dev, of_val32);
else
pdata->micbias1_lvl = DA7218_MICBIAS_1_6V;
if (of_property_read_u32(np, "dlg,micbias2-lvl-millivolt", &of_val32) >= 0)
- pdata->micbias2_lvl = da7218_of_micbias_lvl(component, of_val32);
+ pdata->micbias2_lvl = da7218_of_micbias_lvl(dev, of_val32);
else
pdata->micbias2_lvl = DA7218_MICBIAS_1_6V;
if (!of_property_read_string(np, "dlg,mic1-amp-in-sel", &of_str))
pdata->mic1_amp_in_sel =
- da7218_of_mic_amp_in_sel(component, of_str);
+ da7218_of_mic_amp_in_sel(dev, of_str);
else
pdata->mic1_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
if (!of_property_read_string(np, "dlg,mic2-amp-in-sel", &of_str))
pdata->mic2_amp_in_sel =
- da7218_of_mic_amp_in_sel(component, of_str);
+ da7218_of_mic_amp_in_sel(dev, of_str);
else
pdata->mic2_amp_in_sel = DA7218_MIC_AMP_IN_SEL_DIFF;
if (!of_property_read_string(np, "dlg,dmic1-data-sel", &of_str))
- pdata->dmic1_data_sel = da7218_of_dmic_data_sel(component, of_str);
+ pdata->dmic1_data_sel = da7218_of_dmic_data_sel(dev, of_str);
else
pdata->dmic1_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
if (!of_property_read_string(np, "dlg,dmic1-samplephase", &of_str))
pdata->dmic1_samplephase =
- da7218_of_dmic_samplephase(component, of_str);
+ da7218_of_dmic_samplephase(dev, of_str);
else
pdata->dmic1_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
if (of_property_read_u32(np, "dlg,dmic1-clkrate-hz", &of_val32) >= 0)
- pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(component, of_val32);
+ pdata->dmic1_clk_rate = da7218_of_dmic_clkrate(dev, of_val32);
else
pdata->dmic1_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
if (!of_property_read_string(np, "dlg,dmic2-data-sel", &of_str))
- pdata->dmic2_data_sel = da7218_of_dmic_data_sel(component, of_str);
+ pdata->dmic2_data_sel = da7218_of_dmic_data_sel(dev, of_str);
else
pdata->dmic2_data_sel = DA7218_DMIC_DATA_LRISE_RFALL;
if (!of_property_read_string(np, "dlg,dmic2-samplephase", &of_str))
pdata->dmic2_samplephase =
- da7218_of_dmic_samplephase(component, of_str);
+ da7218_of_dmic_samplephase(dev, of_str);
else
pdata->dmic2_samplephase = DA7218_DMIC_SAMPLE_ON_CLKEDGE;
if (of_property_read_u32(np, "dlg,dmic2-clkrate-hz", &of_val32) >= 0)
- pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(component, of_val32);
+ pdata->dmic2_clk_rate = da7218_of_dmic_clkrate(dev, of_val32);
else
pdata->dmic2_clk_rate = DA7218_DMIC_CLK_3_0MHZ;
@@ -2508,7 +2520,7 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *compone
if (!hpldet_np)
return pdata;
- hpldet_pdata = devm_kzalloc(component->dev, sizeof(*hpldet_pdata),
+ hpldet_pdata = devm_kzalloc(dev, sizeof(*hpldet_pdata),
GFP_KERNEL);
if (!hpldet_pdata) {
of_node_put(hpldet_np);
@@ -2519,14 +2531,14 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *compone
if (of_property_read_u32(hpldet_np, "dlg,jack-rate-us",
&of_val32) >= 0)
hpldet_pdata->jack_rate =
- da7218_of_jack_rate(component, of_val32);
+ da7218_of_jack_rate(dev, of_val32);
else
hpldet_pdata->jack_rate = DA7218_HPLDET_JACK_RATE_40US;
if (of_property_read_u32(hpldet_np, "dlg,jack-debounce",
&of_val32) >= 0)
hpldet_pdata->jack_debounce =
- da7218_of_jack_debounce(component, of_val32);
+ da7218_of_jack_debounce(dev, of_val32);
else
hpldet_pdata->jack_debounce =
DA7218_HPLDET_JACK_DEBOUNCE_2;
@@ -2534,7 +2546,7 @@ static struct da7218_pdata *da7218_of_to_pdata(struct snd_soc_component *compone
if (of_property_read_u32(hpldet_np, "dlg,jack-threshold-pct",
&of_val32) >= 0)
hpldet_pdata->jack_thr =
- da7218_of_jack_thr(component, of_val32);
+ da7218_of_jack_thr(dev, of_val32);
else
hpldet_pdata->jack_thr = DA7218_HPLDET_JACK_THR_84PCT;
@@ -2626,18 +2638,7 @@ static int da7218_handle_supplies(struct snd_soc_component *component)
struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
struct regulator *vddio;
u8 io_voltage_lvl = DA7218_IO_VOLTAGE_LEVEL_2_5V_3_6V;
- int i, ret;
-
- /* Get required supplies */
- for (i = 0; i < DA7218_NUM_SUPPLIES; ++i)
- da7218->supplies[i].supply = da7218_supply_names[i];
-
- ret = devm_regulator_bulk_get(component->dev, DA7218_NUM_SUPPLIES,
- da7218->supplies);
- if (ret) {
- dev_err(component->dev, "Failed to get supplies\n");
- return ret;
- }
+ int ret;
/* Determine VDDIO voltage provided */
vddio = da7218->supplies[DA7218_SUPPLY_VDDIO].consumer;
@@ -2875,21 +2876,8 @@ static int da7218_probe(struct snd_soc_component *component)
if (ret)
return ret;
- /* Handle DT/Platform data */
- if (component->dev->of_node)
- da7218->pdata = da7218_of_to_pdata(component);
- else
- da7218->pdata = dev_get_platdata(component->dev);
-
da7218_handle_pdata(component);
- /* Check if MCLK provided, if not the clock is NULL */
- da7218->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(da7218->mclk)) {
- ret = PTR_ERR(da7218->mclk);
- goto err_disable_reg;
- }
-
/* Default PC to free-running */
snd_soc_component_write(component, DA7218_PC_COUNT, DA7218_PC_FREERUN_MASK);
@@ -2953,10 +2941,10 @@ static int da7218_probe(struct snd_soc_component *component)
}
if (da7218->irq) {
- ret = devm_request_threaded_irq(component->dev, da7218->irq, NULL,
- da7218_irq_thread,
- IRQF_TRIGGER_LOW | IRQF_ONESHOT,
- "da7218", component);
+ ret = request_threaded_irq(da7218->irq, NULL,
+ da7218_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "da7218", component);
if (ret != 0) {
dev_err(component->dev, "Failed to request IRQ %d: %d\n",
da7218->irq, ret);
@@ -2977,6 +2965,9 @@ static void da7218_remove(struct snd_soc_component *component)
{
struct da7218_priv *da7218 = snd_soc_component_get_drvdata(component);
+ if (da7218->irq)
+ free_irq(da7218->irq, component);
+
regulator_bulk_disable(DA7218_NUM_SUPPLIES, da7218->supplies);
}
@@ -3247,7 +3238,7 @@ static const struct regmap_config da7218_regmap_config = {
static int da7218_i2c_probe(struct i2c_client *i2c)
{
struct da7218_priv *da7218;
- int ret;
+ int i, ret;
da7218 = devm_kzalloc(&i2c->dev, sizeof(*da7218), GFP_KERNEL);
if (!da7218)
@@ -3272,6 +3263,28 @@ static int da7218_i2c_probe(struct i2c_client *i2c)
return ret;
}
+ /* Get required supplies */
+ for (i = 0; i < DA7218_NUM_SUPPLIES; ++i)
+ da7218->supplies[i].supply = da7218_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, DA7218_NUM_SUPPLIES,
+ da7218->supplies);
+ if (ret) {
+ dev_err(&i2c->dev, "Failed to get supplies\n");
+ return ret;
+ }
+
+ /* Check if MCLK provided, if not the clock is NULL */
+ da7218->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(da7218->mclk))
+ return PTR_ERR(da7218->mclk);
+
+ /* Handle DT/Platform data */
+ if (i2c->dev.of_node)
+ da7218->pdata = da7218_of_to_pdata(&i2c->dev, da7218);
+ else
+ da7218->pdata = dev_get_platdata(&i2c->dev);
+
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_da7218, &da7218_dai, 1);
if (ret < 0) {
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index f6d371e93549..673285c3213b 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -1643,12 +1643,24 @@ static int da7219_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 da7219_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops da7219_dai_ops = {
.hw_params = da7219_hw_params,
.set_sysclk = da7219_set_dai_sysclk,
.set_pll = da7219_set_dai_pll,
.set_fmt = da7219_set_dai_fmt,
.set_tdm_slot = da7219_set_dai_tdm_slot,
+ .auto_selectable_formats = &da7219_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 12d1ac98461e..c9b6172ac0a1 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1188,10 +1188,27 @@ static int da732x_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
#define DA732X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 da732x_selectable_formats[] = {
+ /* Hi Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* Low Priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops da732x_dai_ops = {
.hw_params = da732x_hw_params,
.set_fmt = da732x_set_dai_fmt,
.set_sysclk = da732x_set_dai_sysclk,
+ .auto_selectable_formats = da732x_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(da732x_selectable_formats),
};
static struct snd_soc_dai_driver da732x_dai[] = {
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index a88c13533145..faff1535ab12 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1317,6 +1317,12 @@ pll_err:
return -EINVAL;
}
+static const u64 da9055_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A;
+
/* DAI operations */
static const struct snd_soc_dai_ops da9055_dai_ops = {
.hw_params = da9055_hw_params,
@@ -1324,6 +1330,8 @@ static const struct snd_soc_dai_ops da9055_dai_ops = {
.set_sysclk = da9055_set_dai_sysclk,
.set_pll = da9055_set_dai_pll,
.mute_stream = da9055_mute,
+ .auto_selectable_formats = &da9055_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c
index 441df1523f1c..e3e6142ca028 100644
--- a/sound/soc/codecs/es7134.c
+++ b/sound/soc/codecs/es7134.c
@@ -130,10 +130,16 @@ static int es7134_component_probe(struct snd_soc_component *c)
return 0;
}
+static const u64 es7134_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops es7134_dai_ops = {
.set_fmt = es7134_set_fmt,
.hw_params = es7134_hw_params,
.set_sysclk = es7134_set_sysclk,
+ .auto_selectable_formats = &es7134_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver es7134_dai = {
diff --git a/sound/soc/codecs/es7241.c b/sound/soc/codecs/es7241.c
index 339553cfbb48..a0bd5386387d 100644
--- a/sound/soc/codecs/es7241.c
+++ b/sound/soc/codecs/es7241.c
@@ -152,10 +152,17 @@ static int es7241_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
+static const u64 es7241_selectable_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 es7241_dai_ops = {
.set_fmt = es7241_set_fmt,
.hw_params = es7241_hw_params,
.set_sysclk = es7241_set_sysclk,
+ .auto_selectable_formats = &es7241_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver es7241_dai = {
diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c
index 6fe3ae41afbd..ddbbfc2443b3 100644
--- a/sound/soc/codecs/es8311.c
+++ b/sound/soc/codecs/es8311.c
@@ -800,12 +800,29 @@ static int es8311_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static const u64 es8311_selectable_formats[] = {
+ /* Hi Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* Low Priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops es8311_dai_ops = {
.startup = es8311_startup,
.hw_params = es8311_hw_params,
.mute_stream = es8311_mute,
.set_sysclk = es8311_set_sysclk,
.set_fmt = es8311_set_dai_fmt,
+ .auto_selectable_formats = es8311_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(es8311_selectable_formats),
.no_capture_mute = 1,
};
@@ -889,12 +906,6 @@ static int es8311_component_probe(struct snd_soc_component *component)
es8311 = snd_soc_component_get_drvdata(component);
- es8311->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(es8311->mclk)) {
- dev_err(component->dev, "invalid mclk\n");
- return PTR_ERR(es8311->mclk);
- }
-
es8311->mclk_freq = clk_get_rate(es8311->mclk);
if (es8311->mclk_freq > 0 && es8311->mclk_freq < ES8311_MCLK_MAX_FREQ)
es8311_set_sysclk_constraints(es8311->mclk_freq, es8311);
@@ -943,6 +954,10 @@ static int es8311_i2c_probe(struct i2c_client *i2c_client)
if (es8311 == NULL)
return -ENOMEM;
+ es8311->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(es8311->mclk))
+ return dev_err_probe(dev, PTR_ERR(es8311->mclk), "invalid mclk\n");
+
es8311->regmap =
devm_regmap_init_i2c(i2c_client, &es8311_regmap_config);
if (IS_ERR(es8311->regmap))
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 24ec8b211cdb..ded36a43065a 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -566,12 +566,21 @@ static int es8316_mute(struct snd_soc_dai *dai, int mute, int direction)
#define ES8316_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 es8316_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops es8316_ops = {
.startup = es8316_pcm_startup,
.hw_params = es8316_pcm_hw_params,
.set_fmt = es8316_set_dai_fmt,
.set_sysclk = es8316_set_dai_sysclk,
.mute_stream = es8316_mute,
+ .auto_selectable_formats = &es8316_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -762,14 +771,6 @@ static int es8316_probe(struct snd_soc_component *component)
es8316->component = component;
- es8316->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(es8316->mclk)) {
- dev_err(component->dev, "unable to get mclk\n");
- return PTR_ERR(es8316->mclk);
- }
- if (!es8316->mclk)
- dev_warn(component->dev, "assuming static mclk\n");
-
ret = clk_prepare_enable(es8316->mclk);
if (ret) {
dev_err(component->dev, "unable to enable mclk\n");
@@ -810,9 +811,7 @@ static int es8316_resume(struct snd_soc_component *component)
struct es8316_priv *es8316 = snd_soc_component_get_drvdata(component);
regcache_cache_only(es8316->regmap, false);
- regcache_sync(es8316->regmap);
-
- return 0;
+ return regcache_sync(es8316->regmap);
}
static int es8316_suspend(struct snd_soc_component *component)
@@ -874,6 +873,12 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client)
i2c_set_clientdata(i2c_client, es8316);
+ es8316->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(es8316->mclk))
+ return dev_err_probe(dev, PTR_ERR(es8316->mclk), "unable to get mclk\n");
+ if (!es8316->mclk)
+ dev_warn(dev, "assuming static mclk\n");
+
ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(es8316_supply_names),
es8316_supply_names);
if (ret)
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
index b926340256be..6a8d3902af23 100644
--- a/sound/soc/codecs/es8323.c
+++ b/sound/soc/codecs/es8323.c
@@ -622,12 +622,25 @@ static int es8323_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
mute ? ES8323_DACCONTROL3_DACMUTE : 0);
}
+static const u64 es8323_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops es8323_ops = {
.startup = es8323_pcm_startup,
.hw_params = es8323_pcm_hw_params,
.set_fmt = es8323_set_dai_fmt,
.set_sysclk = es8323_set_dai_sysclk,
.mute_stream = es8323_mute_stream,
+ .auto_selectable_formats = &es8323_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define ES8323_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
@@ -658,15 +671,6 @@ static int es8323_probe(struct snd_soc_component *component)
struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
int ret;
- es8323->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(es8323->mclk)) {
- dev_err(component->dev, "unable to get mclk\n");
- return PTR_ERR(es8323->mclk);
- }
-
- if (!es8323->mclk)
- dev_warn(component->dev, "assuming static mclk\n");
-
ret = clk_prepare_enable(es8323->mclk);
if (ret) {
dev_err(component->dev, "unable to enable mclk\n");
@@ -733,9 +737,7 @@ static int es8323_resume(struct snd_soc_component *component)
struct es8323_priv *es8323 = snd_soc_component_get_drvdata(component);
regcache_cache_only(es8323->regmap, false);
- regcache_sync(es8323->regmap);
-
- return 0;
+ return regcache_sync(es8323->regmap);
}
static const struct snd_soc_component_driver soc_component_dev_es8323 = {
@@ -776,6 +778,12 @@ static int es8323_i2c_probe(struct i2c_client *i2c_client)
i2c_set_clientdata(i2c_client, es8323);
+ es8323->mclk = devm_clk_get_optional(dev, "mclk");
+ if (IS_ERR(es8323->mclk))
+ return dev_err_probe(dev, PTR_ERR(es8323->mclk), "unable to get mclk\n");
+ if (!es8323->mclk)
+ dev_warn(dev, "assuming static mclk\n");
+
es8323->regmap = devm_regmap_init_i2c(i2c_client, &es8323_regmap);
if (IS_ERR(es8323->regmap))
return PTR_ERR(es8323->regmap);
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index b6eadc2e9659..a95b3b8c75dd 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -33,11 +33,16 @@ struct es8326_priv {
* while enabling or disabling or during an irq.
*/
struct mutex lock;
+ struct mutex hpf_lock;
u8 jack_pol;
u8 interrupt_src;
u8 interrupt_clk;
u8 hpl_vol;
u8 hpr_vol;
+ u8 hpfl;
+ u8 hpfr;
+ u32 hpf_freq;
+ u32 capture_rate;
bool jd_inverted;
unsigned int sysclk;
@@ -47,6 +52,48 @@ struct es8326_priv {
int jack_remove_retry;
};
+static const u32 hpf_table[10][10] = {
+ {1020, 754, 624, 559, 527, 511, 502, 498, 497, 496},
+ {754, 495, 368, 306, 274, 259, 251, 247, 246, 244},
+ {624, 368, 243, 182, 151, 136, 128, 124, 123, 121},
+ {559, 306, 182, 120, 90, 75, 68, 63, 62, 60},
+ {527, 274, 151, 90, 60, 45, 38, 33, 32, 31},
+ {511, 259, 136, 75, 45, 30, 23, 19, 18, 17},
+ {502, 251, 128, 68, 38, 23, 16, 13, 11, 11},
+ {498, 247, 124, 63, 33, 19, 13, 10, 8, 8},
+ {497, 246, 123, 62, 32, 18, 11, 8, 8, 0},
+ {496, 244, 121, 60, 31, 17, 11, 8, 0, 0}
+};
+
+static bool find_best_hpf_freq(u32 target_hz, u8 *hpf1, u8 *hpf2)
+{
+ int best_row = -1, best_col = -1;
+ u32 min_diff = U32_MAX;
+ u32 f, diff;
+ int i, j;
+
+ if (target_hz > 1020)
+ return false;
+
+ for (i = 0; i < 10; i++) {
+ for (j = i; j < 10; j++) {
+ f = hpf_table[i][j];
+
+ diff = (target_hz > f) ? (target_hz - f) : (f - target_hz);
+ if (diff < min_diff) {
+ min_diff = diff;
+ best_row = i;
+ best_col = j;
+ }
+ }
+ }
+
+ *hpf1 = best_col + ES8326_HPF_OFFSET;
+ *hpf2 = best_row + ES8326_HPF_OFFSET;
+
+ return true;
+}
+
static int es8326_crosstalk1_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -189,6 +236,48 @@ static int es8326_hprvol_set(struct snd_kcontrol *kcontrol,
return 0;
}
+static int es8326_hpf_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = es8326->hpf_freq;
+ return 0;
+}
+
+static int es8326_hpf_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ u32 freq;
+ bool hpf;
+
+ if ((ucontrol->value.integer.value[0] > 1020) || (ucontrol->value.integer.value[0] < 0))
+ return -EBUSY;
+
+ if (es8326->hpf_freq == ucontrol->value.integer.value[0])
+ return 0;
+
+ if (es8326->capture_rate) {
+ guard(mutex)(&es8326->hpf_lock);
+ freq = (ucontrol->value.integer.value[0] * 48000) / es8326->capture_rate;
+
+ hpf = find_best_hpf_freq(freq, &es8326->hpfl, &es8326->hpfr);
+ if (!hpf)
+ return -EBUSY;
+
+ regmap_update_bits(es8326->regmap, ES8326_ADC_HPFS1, 0x0f, es8326->hpfl);
+ regmap_update_bits(es8326->regmap, ES8326_ADC_HPFS2, 0x0f, es8326->hpfr);
+ } else {
+ dev_dbg_ratelimited(component->dev, "PCM_STREAM_CAPTURE is not active.retain the input frequency\n");
+ }
+ es8326->hpf_freq = ucontrol->value.integer.value[0];
+
+ return 1;
+}
+
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9550, 50, 0);
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_analog_pga_tlv, 0, 300, 0);
@@ -269,6 +358,8 @@ static const struct snd_kcontrol_new es8326_snd_controls[] = {
SOC_ENUM("ALC Capture Winsize", alc_winsize),
SOC_SINGLE_TLV("ALC Capture Target Level", ES8326_ALC_LEVEL,
0, 0x0f, 0, drc_target_tlv),
+ SOC_SINGLE_EXT("ADC HPF Freq Select", SND_SOC_NOPM, 0, 1020, 0,
+ es8326_hpf_get, es8326_hpf_set),
SOC_SINGLE_EXT("CROSSTALK1", SND_SOC_NOPM, 0, 31, 0,
es8326_crosstalk1_get, es8326_crosstalk1_set),
@@ -353,8 +444,6 @@ static bool es8326_writeable_register(struct device *dev, unsigned int reg)
case ES8326_BIAS_SW2:
case ES8326_BIAS_SW3:
case ES8326_BIAS_SW4:
- case ES8326_ADC_HPFS1:
- case ES8326_ADC_HPFS2:
return false;
default:
return true;
@@ -558,6 +647,8 @@ static int es8326_pcm_hw_params(struct snd_pcm_substream *substream,
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
u8 srate = 0;
int coeff, array;
+ u32 freq;
+ bool hpf;
if (es8326->version == 0) {
coeff_div = coeff_div_v0;
@@ -612,6 +703,28 @@ static int es8326_pcm_hw_params(struct snd_pcm_substream *substream,
dev_warn(component->dev, "Clock coefficients do not match");
}
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ es8326->capture_rate = params_rate(params);
+ freq = (es8326->hpf_freq * 48000) / params_rate(params);
+ hpf = find_best_hpf_freq(freq, &es8326->hpfl, &es8326->hpfr);
+ if (!hpf) {
+ dev_err(component->dev, "The HPF frequency is invalid\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int es8326_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ es8326->capture_rate = 0;
+
return 0;
}
@@ -636,6 +749,8 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
regmap_update_bits(es8326->regmap, ES8326_VMIDSEL, 0x40, 0x40);
regmap_update_bits(es8326->regmap, ES8326_ANA_MICBIAS, 0x70, 0x30);
}
+ regmap_update_bits(es8326->regmap, ES8326_ADC_HPFS1, 0x0f, 0x04);
+ regmap_update_bits(es8326->regmap, ES8326_ADC_HPFS2, 0x0f, 0x04);
}
} else {
if (!es8326->calibrated) {
@@ -705,7 +820,8 @@ static int es8326_set_bias_level(struct snd_soc_component *codec,
case SND_SOC_BIAS_PREPARE:
break;
case SND_SOC_BIAS_STANDBY:
- regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
+ regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x58);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x13);
regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00);
regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
if (es8326->version > ES8326_VERSION_B) {
@@ -726,11 +842,21 @@ static int es8326_set_bias_level(struct snd_soc_component *codec,
#define es8326_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 es8326_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops es8326_ops = {
.hw_params = es8326_pcm_hw_params,
+ .hw_free = es8326_pcm_hw_free,
.set_fmt = es8326_set_dai_fmt,
.set_sysclk = es8326_set_dai_sysclk,
.mute_stream = es8326_mute,
+ .auto_selectable_formats = &es8326_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 0,
};
@@ -781,6 +907,8 @@ static void es8326_capture_pop_handler(struct work_struct *work)
struct es8326_priv *es8326 =
container_of(work, struct es8326_priv, capture_pop_work.work);
+ regmap_update_bits(es8326->regmap, ES8326_ADC_HPFS1, 0x0f, es8326->hpfl);
+ regmap_update_bits(es8326->regmap, ES8326_ADC_HPFS2, 0x0f, es8326->hpfr);
regmap_update_bits(es8326->regmap, ES8326_ADC_MUTE,
0x0F, 0x00);
}
@@ -865,7 +993,7 @@ static void es8326_jack_detect_handler(struct work_struct *work)
struct es8326_priv *es8326 =
container_of(work, struct es8326_priv, jack_detect_work.work);
struct snd_soc_component *comp = es8326->component;
- unsigned int iface;
+ unsigned int iface, pga_status;
guard(mutex)(&es8326->lock);
iface = snd_soc_component_read(comp, ES8326_HPDET_STA);
@@ -961,12 +1089,19 @@ static void es8326_jack_detect_handler(struct work_struct *work)
dev_dbg(comp->dev, "Headset detected\n");
snd_soc_jack_report(es8326->jack,
SND_JACK_HEADSET, SND_JACK_HEADSET);
- regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
- 0x08, 0x08);
- regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
- regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
- regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
- 0x08, 0x00);
+ pga_status = snd_soc_component_read(comp, ES8326_PGA_PDN);
+
+ if ((pga_status & 0x08) >> 3) {
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
+ } else {
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x08);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x00);
+ }
usleep_range(10000, 15000);
}
}
@@ -1130,18 +1265,28 @@ static int es8326_resume(struct snd_soc_component *component)
{
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
unsigned int reg;
+ int ret;
regcache_cache_only(es8326->regmap, false);
regcache_cache_bypass(es8326->regmap, true);
- regmap_read(es8326->regmap, ES8326_CLK_RESAMPLE, &reg);
+ ret = regmap_read(es8326->regmap, ES8326_CLK_RESAMPLE, &reg);
regcache_cache_bypass(es8326->regmap, false);
+ if (ret)
+ return ret;
+
/* reset internal clock state */
- if (reg == 0x05)
- regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
- else
+ if (reg == 0x05) {
+ ret = regmap_write(es8326->regmap, ES8326_CLK_CTL,
+ ES8326_CLK_ON);
+ if (ret)
+ return ret;
+ } else {
es8326_init(component);
+ }
- regcache_sync(es8326->regmap);
+ ret = regcache_sync(es8326->regmap);
+ if (ret)
+ return ret;
es8326_irq(es8326->irq, es8326);
return 0;
@@ -1202,6 +1347,7 @@ static int es8326_probe(struct snd_soc_component *component)
}
dev_dbg(component->dev, "interrupt-clk %x", es8326->interrupt_clk);
+ es8326->hpf_freq = ES8326_HPF_DEFAULT;
es8326_init(component);
return 0;
}
@@ -1288,6 +1434,7 @@ static int es8326_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, es8326);
es8326->i2c = i2c;
mutex_init(&es8326->lock);
+ mutex_init(&es8326->hpf_lock);
es8326->regmap = devm_regmap_init_i2c(i2c, &es8326_regmap_config);
if (IS_ERR(es8326->regmap)) {
ret = PTR_ERR(es8326->regmap);
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index 1c5b3ec70a1e..c78d528e7dfe 100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
@@ -149,6 +149,10 @@
#define ES8326_ADC_SRC_DMIC_SDIN3 6
#define ES8326_ADC_SRC_DMIC_SDIN3_INV 7
+/* HPF Settings */
+#define ES8326_HPF_DEFAULT 8
+#define ES8326_HPF_OFFSET 4
+
#define ES8326_ADC_AMIC ((ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC2_SHIFT) \
| (ES8326_ADC_SRC_ANA_INV_SW1 << ES8326_ADC1_SHIFT))
#define ES8326_ADC_DMIC ((ES8326_ADC_SRC_DMIC_SDIN2 << ES8326_ADC2_SHIFT) \
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index aaa6646ad4c5..37fda2f42b0c 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -727,12 +727,19 @@ static int es8328_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static const u64 es8328_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops es8328_dai_ops = {
.startup = es8328_startup,
.hw_params = es8328_hw_params,
.mute_stream = es8328_mute,
.set_sysclk = es8328_set_sysclk,
.set_fmt = es8328_set_dai_fmt,
+ .auto_selectable_formats = &es8328_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -822,14 +829,6 @@ static int es8328_component_probe(struct snd_soc_component *component)
return ret;
}
- /* Setup clocks */
- es8328->clk = devm_clk_get(component->dev, NULL);
- if (IS_ERR(es8328->clk)) {
- dev_err(component->dev, "codec clock missing or invalid\n");
- ret = PTR_ERR(es8328->clk);
- goto clk_fail;
- }
-
ret = clk_prepare_enable(es8328->clk);
if (ret) {
dev_err(component->dev, "unable to prepare codec clk\n");
@@ -899,6 +898,11 @@ int es8328_probe(struct device *dev, struct regmap *regmap)
es8328->regmap = regmap;
+ es8328->clk = devm_clk_get(dev, NULL);
+ if (IS_ERR(es8328->clk))
+ return dev_err_probe(dev, PTR_ERR(es8328->clk),
+ "codec clock missing or invalid\n");
+
for (i = 0; i < ARRAY_SIZE(es8328->supplies); i++)
es8328->supplies[i].supply = supply_names[i];
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
index e8747bc24433..80707d2768c6 100644
--- a/sound/soc/codecs/es8375.c
+++ b/sound/soc/codecs/es8375.c
@@ -494,11 +494,23 @@ static int es8375_mute(struct snd_soc_dai *dai, int mute, int stream)
#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 u64 es8375_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops es8375_ops = {
.hw_params = es8375_hw_params,
.mute_stream = es8375_mute,
.set_sysclk = es8375_set_sysclk,
.set_fmt = es8375_set_dai_fmt,
+ .auto_selectable_formats = &es8375_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver es8375_dai = {
@@ -577,20 +589,24 @@ static int es8375_resume(struct snd_soc_component *component)
{
struct es8375_priv *es8375 = snd_soc_component_get_drvdata(component);
unsigned int reg;
+ int ret;
regcache_cache_only(es8375->regmap, false);
regcache_cache_bypass(es8375->regmap, true);
- regmap_read(es8375->regmap, ES8375_CLK_MGR2, &reg);
+ ret = regmap_read(es8375->regmap, ES8375_CLK_MGR2, &reg);
regcache_cache_bypass(es8375->regmap, false);
+ if (ret)
+ return ret;
- if (reg == 0x00)
+ if (reg == 0x00) {
es8375_init(component);
- else
- es8375_set_bias_level(component, SND_SOC_BIAS_ON);
-
- regcache_sync(es8375->regmap);
+ } else {
+ ret = es8375_set_bias_level(component, SND_SOC_BIAS_ON);
+ if (ret)
+ return ret;
+ }
- return 0;
+ return regcache_sync(es8375->regmap);
}
static int es8375_codec_probe(struct snd_soc_component *component)
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
index 2ea0c4052ce1..267806b72f87 100644
--- a/sound/soc/codecs/es8389.c
+++ b/sound/soc/codecs/es8389.c
@@ -910,6 +910,13 @@ static int es8389_mute(struct snd_soc_dai *dai, int mute, int direction)
#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 u64 es8389_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops es8389_ops = {
.hw_params = es8389_pcm_hw_params,
.hw_free = es8389_pcm_hw_free,
@@ -917,6 +924,8 @@ static const struct snd_soc_dai_ops es8389_ops = {
.set_sysclk = es8389_set_dai_sysclk,
.set_tdm_slot = es8389_set_tdm_slot,
.mute_stream = es8389_mute,
+ .auto_selectable_formats = &es8389_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver es8389_dai = {
@@ -1032,25 +1041,33 @@ static int es8389_resume(struct snd_soc_component *component)
{
struct es8389_private *es8389 = snd_soc_component_get_drvdata(component);
unsigned int regv;
+ int ret;
regcache_cache_only(es8389->regmap, false);
regcache_cache_bypass(es8389->regmap, true);
- regmap_read(es8389->regmap, ES8389_RESET, &regv);
+ ret = regmap_read(es8389->regmap, ES8389_RESET, &regv);
+ if (ret)
+ goto disable_bypass;
- if (regv == 0xff)
+ if (regv == 0xff) {
es8389_init(component);
- else
- es8389_set_bias_level(component, SND_SOC_BIAS_ON);
+ } else {
+ ret = es8389_set_bias_level(component, SND_SOC_BIAS_ON);
+ if (ret)
+ goto disable_bypass;
+ }
+disable_bypass:
regcache_cache_bypass(es8389->regmap, false);
- regcache_sync(es8389->regmap);
+ if (ret)
+ return ret;
- return 0;
+ return regcache_sync(es8389->regmap);
}
static int es8389_probe(struct snd_soc_component *component)
{
- int ret, i;
+ 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);
@@ -1059,22 +1076,6 @@ static int es8389_probe(struct snd_soc_component *component)
es8389->mclk_src = ES8389_MCLK_SOURCE;
}
- for (i = 0; i < ARRAY_SIZE(es8389_core_supplies); i++)
- es8389->core_supply[i].supply = es8389_core_supplies[i];
- ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(es8389_core_supplies), es8389->core_supply);
- if (ret) {
- dev_err(component->dev, "Failed to request core supplies %d\n", ret);
- return ret;
- }
-
- es8389->mclk = devm_clk_get_optional(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__);
@@ -1162,7 +1163,7 @@ static void es8389_i2c_shutdown(struct i2c_client *i2c)
static int es8389_i2c_probe(struct i2c_client *i2c_client)
{
struct es8389_private *es8389;
- int ret;
+ int ret, i;
es8389 = devm_kzalloc(&i2c_client->dev, sizeof(*es8389), GFP_KERNEL);
if (es8389 == NULL)
@@ -1174,6 +1175,22 @@ static int es8389_i2c_probe(struct i2c_client *i2c_client)
return dev_err_probe(&i2c_client->dev, PTR_ERR(es8389->regmap),
"regmap_init() failed\n");
+ for (i = 0; i < ARRAY_SIZE(es8389_core_supplies); i++)
+ es8389->core_supply[i].supply = es8389_core_supplies[i];
+ ret = devm_regulator_bulk_get(&i2c_client->dev, ARRAY_SIZE(es8389_core_supplies),
+ es8389->core_supply);
+ if (ret)
+ return dev_err_probe(&i2c_client->dev, ret,
+ "Failed to request core supplies\n");
+
+ es8389->mclk = devm_clk_get_optional(&i2c_client->dev, "mclk");
+ if (IS_ERR(es8389->mclk))
+ return dev_err_probe(&i2c_client->dev, PTR_ERR(es8389->mclk),
+ "ES8389 is unable to get mclk\n");
+
+ if (!es8389->mclk)
+ dev_err(&i2c_client->dev, "%s, assuming static mclk\n", __func__);
+
ret = devm_snd_soc_register_component(&i2c_client->dev,
&soc_codec_dev_es8389,
&es8389_dai,
diff --git a/sound/soc/codecs/es9039q2m.c b/sound/soc/codecs/es9039q2m.c
new file mode 100644
index 000000000000..4e9f86cb7860
--- /dev/null
+++ b/sound/soc/codecs/es9039q2m.c
@@ -0,0 +1,2008 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// ESS Technology ES9039Q2M 32-bit 2-channel audio DAC
+//
+// Copyright (C) 2026 Karl Asseily <karl@asseily.com>
+//
+// Every register number, bit field and default in this file was taken from
+// ES9039Q2M datasheet v0.2.3 and then verified by reading the defaults back off
+// a live part over I2C.
+//
+// The part has two control personalities selected by the MODE pin: hardware
+// mode (strapped by HW0/HW1/HW2, no bus at all) and software mode (I2C or SPI).
+// This driver implements software mode over I2C, which MODE = GND selects.
+//
+// Three properties shape the driver:
+//
+// - There is an ASRC in front of the DAC, so MCLK need not be synchronous with
+// BCLK or LRCK. A board may feed it a fixed oscillator, or may run it
+// synchronously and let the part generate BCLK and WS; both are supported.
+//
+// - INPUT_SEL chooses PCM / DSD / DoP / S/PDIF. It does NOT choose I2S vs
+// left-justified - that is TDM_LJ_MODE in register 60, and there is no
+// right-justified mode to map onto at all.
+//
+// - Several registers have non-zero reserved defaults (register 88 reads
+// 0xb8 at reset), so every write here is read-modify-write.
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+/* ------------------------------------------------- read/write registers ---- */
+
+#define ES9039_SYSTEM_CONFIG 0x00 /* reg 0 */
+#define ES9039_DAC_MODE BIT(1) /* CLEAR at reset - datapath off */
+#define ES9039_64FS_MODE BIT(6)
+
+/*
+ * Register 1 selects which DECODERS are running, and it is separate from
+ * INPUT_SEL in register 57, which only says which port to listen to. Both are
+ * needed: on reset only ENABLE_TDM_DECODE is set, so selecting DoP as the input
+ * while leaving bit 2 clear leaves the part hunting for a marker with the
+ * marker decoder switched off. It then finds no valid DoP and mutes - silence,
+ * DOP_VALID reading 0, and nothing anywhere saying why. Measured on hardware.
+ */
+#define ES9039_SYS_MODE 0x01 /* reg 1, reset 0xb1 */
+#define ES9039_ENABLE_TDM_DECODE BIT(0) /* set at reset */
+#define ES9039_ENABLE_DSD_DECODE BIT(1)
+#define ES9039_ENABLE_DOP_DECODE BIT(2)
+#define ES9039_ENABLE_SPDIF_DECODE BIT(3)
+#define ES9039_SYNC_MODE BIT(6) /* 0 = ASYNC */
+#define ES9039_ENABLE_DAC_CLK BIT(7) /* set at reset */
+/*
+ * Every decoder the part has. hw_params() enables one of these and clears the
+ * rest, so leaving S/PDIF out of the mask would have left its decoder running
+ * alongside the selected one.
+ */
+#define ES9039_DECODE_MASK (ES9039_ENABLE_TDM_DECODE | \
+ ES9039_ENABLE_DSD_DECODE | \
+ ES9039_ENABLE_DOP_DECODE | \
+ ES9039_ENABLE_SPDIF_DECODE)
+
+#define ES9039_AUTO_FS_DETECT 0x03 /* reg 3 */
+#define ES9039_AUTO_FS_DETECT_EN BIT(7)
+
+#define ES9039_CLOCK_CONFIG 0x04 /* reg 4, MASTER_BCK_DIV */
+
+#define ES9039_INPUT_SEL 0x39 /* reg 57 */
+#define ES9039_AUTO_INPUT_SEL BIT(0)
+#define ES9039_INPUT_SEL_MASK GENMASK(2, 1)
+#define ES9039_INPUT_PCM 0x0
+#define ES9039_INPUT_DSD 0x1
+#define ES9039_INPUT_DOP 0x2
+#define ES9039_INPUT_SPDIF 0x3
+#define ES9039_PCM_MASTER_MODE BIT(4)
+#define ES9039_DSD_MASTER_MODE BIT(5)
+#define ES9039_DSD_FAULT_DETECT BIT(6) /* set at reset */
+
+#define ES9039_MASTER_ENC 0x3a /* reg 58 */
+#define ES9039_BCK_INV BIT(6)
+
+#define ES9039_TDM_CH_NUM 0x3b /* reg 59, slots = value + 1 */
+#define ES9039_TDM_CH_NUM_MASK GENMASK(4, 0)
+
+#define ES9039_TDM_CONFIG1 0x3c /* reg 60 */
+#define ES9039_TDM_VALID_EDGE BIT(6)
+#define ES9039_TDM_LJ_MODE BIT(7) /* 0 = standard I2S */
+
+#define ES9039_TDM_CONFIG2 0x3d /* reg 61 */
+#define ES9039_TDM_BIT_WIDTH_MASK GENMASK(6, 5)
+#define ES9039_WIDTH_32 0x0
+#define ES9039_WIDTH_24 0x1
+#define ES9039_WIDTH_16 0x2
+
+#define ES9039_MONITOR_CFG 0x3e /* reg 62 */
+#define ES9039_DISABLE_PCM_DC BIT(3)
+#define ES9039_ENABLE_BCK_MONITOR BIT(4) /* set at reset */
+#define ES9039_ENABLE_WS_MONITOR BIT(5) /* set at reset */
+#define ES9039_DISABLE_DSD_MUTE BIT(6)
+#define ES9039_DISABLE_DSD_DC BIT(7)
+
+#define ES9039_VOLUME_CH1 0x4a /* reg 74, 0x00 = 0 dB */
+#define ES9039_VOLUME_CH2 0x4b /* reg 75, 0xff = -127.5 dB */
+#define ES9039_VOL_MAX 0xff
+
+#define ES9039_VOL_RATE_UP 0x52 /* reg 82 */
+#define ES9039_VOL_RATE_DOWN 0x53 /* reg 83 */
+
+#define ES9039_DAC_MUTE 0x56 /* reg 86, 1 = muted */
+#define ES9039_MUTE_CH1 BIT(0)
+#define ES9039_MUTE_CH2 BIT(1)
+#define ES9039_MUTE_BOTH (ES9039_MUTE_CH1 | ES9039_MUTE_CH2)
+
+#define ES9039_DAC_INVERT 0x57 /* reg 87 */
+
+#define ES9039_FILTER_SHAPE 0x58 /* reg 88, [7:3] reset to 10111 */
+#define ES9039_FILTER_SHAPE_MASK GENMASK(2, 0)
+#define ES9039_FILTER_APODIZING 1 /* linear phase apodizing fast */
+
+#define ES9039_IIR_SPDIF 0x59 /* reg 89 */
+#define ES9039_IIR_BW_MASK GENMASK(2, 0)
+#define ES9039_VOLUME_HOLD BIT(3)
+#define ES9039_SPDIF_SEL_MASK GENMASK(7, 4)
+
+#define ES9039_DAC_PATH 0x5a /* reg 90 */
+#define ES9039_BYPASS_FIR2X BIT(0)
+#define ES9039_BYPASS_FIR4X BIT(1)
+#define ES9039_BYPASS_IIR BIT(2)
+
+#define ES9039_THD_C2 0x5b /* regs 91-94: CH1 lo, CH2 hi */
+#define ES9039_THD_C3 0x6b /* regs 107-110 */
+
+#define ES9039_AUTOMUTE_EN 0x7b /* reg 123, both set at reset */
+#define ES9039_AUTOMUTE_TIME 0x7c /* regs 124-125 */
+#define ES9039_AUTOMUTE_TIME_MASK GENMASK(10, 0)
+#define ES9039_MUTE_RAMP_TO_GND BIT(11) /* set at reset */
+#define ES9039_AUTOMUTE_LEVEL 0x7e /* regs 126-127 */
+#define ES9039_AUTOMUTE_OFF_LEVEL 0x80 /* regs 128-129 */
+
+#define ES9039_SOFT_RAMP 0x82 /* reg 130, valid 0..12 */
+#define ES9039_SOFT_RAMP_MASK GENMASK(4, 0)
+#define ES9039_SOFT_RAMP_MAX 12
+
+#define ES9039_NSMOD 0x83 /* reg 131 */
+#define ES9039_NSMOD_WIDE_BW_MASK GENMASK(4, 1)
+#define ES9039_NSMOD_DEFAULT 0x4
+#define ES9039_NSMOD_WIDE 0xc
+
+#define ES9039_PROG_RAM_CTRL 0x87 /* reg 135 */
+#define ES9039_PROG_COEFF_EN BIT(0)
+#define ES9039_PROG_COEFF_WE BIT(1)
+
+#define ES9039_PROG_RAM_ADDR 0x89 /* reg 137 */
+#define ES9039_PROG_ADDR_MASK GENMASK(6, 0)
+#define ES9039_PROG_STAGE_4X BIT(7)
+
+#define ES9039_PROG_RAM_DATA 0x8a /* regs 138-140, 24-bit signed */
+
+#define ES9039_LAST_RW 0x8e /* reg 145 */
+
+/* ----------------------------------------------------- readback registers -- */
+
+#define ES9039_READBACK_BASE 0xe0 /* reg 224 */
+
+#define ES9039_CHIP_ID 0xe1 /* reg 225 */
+#define ES9039_CHIP_ID_ES9039Q2M 0x63
+
+#define ES9039_IRQ_SOURCES 0xea /* regs 234-235, 16-bit */
+#define ES9039_SRC_VOL_MIN_MASK GENMASK(1, 0)
+#define ES9039_SRC_AUTOMUTE_MASK GENMASK(3, 2)
+#define ES9039_SRC_SS_RAMP_MASK GENMASK(5, 4)
+#define ES9039_SRC_DOP_VALID BIT(6)
+#define ES9039_SRC_BCK_WS_FAIL BIT(7)
+#define ES9039_SRC_TDM_VALID BIT(11)
+
+#define ES9039_AUTO_FS_READ 0xef /* reg 239 */
+#define ES9039_FS_DIV_MASK GENMASK(5, 0)
+#define ES9039_FS_HALF_DIV BIT(6)
+#define ES9039_FS_DIV_VALID BIT(7)
+
+#define ES9039_AUTOMUTE_READ 0xf2 /* reg 242 */
+
+#define ES9039_INPUT_STREAM_READ 0xf5 /* reg 245 */
+#define ES9039_RD_INPUT_SEL_MASK GENMASK(1, 0)
+#define ES9039_RD_DOP_VALID BIT(2)
+#define ES9039_RD_TDM_VALID BIT(3)
+#define ES9039_RD_SPDIF_VALID BIT(4)
+
+#define ES9039_MAX_REGISTER 0xfb /* reg 251 */
+
+/* Programmable oversampling FIR: 128 taps in the 2x stage, 32 in the 4x. */
+#define ES9039_FIR2X_TAPS 128
+#define ES9039_FIR4X_TAPS 32
+#define ES9039_COEFF_BYTES 3
+#define ES9039_FIR_STAGES 2 /* [0] = 2x stage, [1] = 4x */
+
+/* In power-up order. Reversed on the way down. Datasheet figure 22. */
+#define ES9039_NUM_SUPPLIES 4
+#define ES9039_FIR_MAX_BYTES (ES9039_FIR2X_TAPS * ES9039_COEFF_BYTES)
+
+/* ------------------------------------------------------------------ private */
+
+struct es9039q2m_priv {
+ struct regmap *regmap;
+ struct clk *mclk;
+ unsigned int mclk_rate;
+ unsigned int fmt;
+ unsigned int stream_rate; /* last rate from hw_params */
+ unsigned int bclk_ratio; /* bit clocks per frame, 0 = unknown */
+
+ /*
+ * Serialises dop_auto against hw_params(), which reads it while
+ * deciding what to program. Without it a control write racing a
+ * stream start can leave the two DoP registers disagreeing.
+ */
+ struct mutex lock;
+ bool dop_auto; /* let the part detect DoP itself */
+
+ /*
+ * ES9039_DAC_MUTE has two owners and neither can hold it alone: the
+ * "Master Playback Switch" control, and mute_stream() around stream
+ * start and stop. Both record what they want here and the register is
+ * written from the union, so a user unmute cannot un-mute a stopped
+ * stream and a stream start cannot override a user mute.
+ *
+ * mute_stream starts FALSE and only a real mute_stream(1) sets it.
+ * Starting it true seemed the safe choice - come up muted - but it is
+ * not: if a card never reaches the DAI mute callbacks, the flag never
+ * clears, and because the control now records intent rather than
+ * writing the register there is nothing the user can do about it. The
+ * DAC is then muted for ever and the mixer looks fine. Measured on
+ * hardware: silent with the switch on, off, stream or no stream. Pop
+ * suppression at probe comes from writing the register
+ * directly instead, which is what the driver did before this rework
+ * and is a state either owner may legitimately lift.
+ */
+ unsigned int mute_user; /* ES9039_MUTE_CH* the user asked for */
+ bool mute_stream; /* ASoC has the stream muted */
+
+ bool provider; /* part drives BCK and WS */
+ bool mclk_fixed; /* MCLK rate cannot be changed, so ASRC it is */
+
+ /*
+ * The four supply inputs, held IN POWER-UP ORDER - the array order is
+ * the datasheet's sequence and the enable loop walks it forwards, the
+ * disable loop backwards. DVDD is not among them: the part's 1.2 V
+ * digital rail is generated on chip and that pin wants a decoupling
+ * capacitor, not a regulator.
+ */
+ struct regulator_bulk_data supplies[ES9039_NUM_SUPPLIES];
+
+ /*
+ * Shadow of whatever was last uploaded to the programmable FIR RAM.
+ *
+ * That RAM cannot be read back - see the upload code below - so regmap
+ * cannot cache it and nothing else in the system knows its contents.
+ * A board that removes the part's supplies in system suspend therefore
+ * comes back with the RAM undefined while regcache_sync() restores the
+ * filter SELECTION from the cache, which would point the interpolator
+ * at whatever the RAM powered up holding. Keep a copy and re-upload.
+ */
+ u8 fir_shadow[ES9039_FIR_STAGES][ES9039_FIR_MAX_BYTES];
+ unsigned int fir_taps[ES9039_FIR_STAGES]; /* 0 = never uploaded */
+};
+
+/*
+ * Multi-byte fields are little-endian across ascending register addresses:
+ * register N holds bits [7:0], N+1 holds [15:8], and so on.
+ */
+static int es9039_read_le(struct regmap *map, unsigned int reg, int n, u32 *out)
+{
+ u8 buf[4];
+ int ret, i;
+
+ ret = regmap_bulk_read(map, reg, buf, n);
+ if (ret)
+ return ret;
+
+ *out = 0;
+ for (i = 0; i < n; i++)
+ *out |= (u32)buf[i] << (8 * i);
+
+ return 0;
+}
+
+static int es9039_write_le(struct regmap *map, unsigned int reg, int n, u32 val)
+{
+ u8 buf[4];
+ int i;
+
+ for (i = 0; i < n; i++)
+ buf[i] = (val >> (8 * i)) & 0xff;
+
+ return regmap_bulk_write(map, reg, buf, n);
+}
+
+/* ------------------------------------------------------------------ regmap */
+
+static bool es9039q2m_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return reg <= ES9039_LAST_RW;
+}
+
+static bool es9039q2m_readable_reg(struct device *dev, unsigned int reg)
+{
+ return reg <= ES9039_LAST_RW || reg >= ES9039_READBACK_BASE;
+}
+
+static bool es9039q2m_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return reg >= ES9039_READBACK_BASE;
+}
+
+static const struct regmap_config es9039q2m_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = ES9039_MAX_REGISTER,
+ .writeable_reg = es9039q2m_writeable_reg,
+ .readable_reg = es9039q2m_readable_reg,
+ .volatile_reg = es9039q2m_volatile_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+/* --------------------------------------------------- signed 16-bit controls */
+
+struct es9039_s16_ctl {
+ unsigned int reg;
+ unsigned int shift; /* 0 for CH1, 16 for CH2 within the 32-bit pair */
+};
+
+static int es9039_s16_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 = S16_MIN;
+ uinfo->value.integer.max = S16_MAX;
+ return 0;
+}
+
+static int es9039_s16_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ struct es9039_s16_ctl *p = (void *)kcontrol->private_value;
+ u32 v;
+ int ret;
+
+ ret = es9039_read_le(priv->regmap, p->reg + (p->shift / 8), 2, &v);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = (s16)v;
+ return 0;
+}
+
+static int es9039_s16_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ struct es9039_s16_ctl *p = (void *)kcontrol->private_value;
+ long v = ucontrol->value.integer.value[0];
+ u32 old;
+ int ret;
+
+ if (v < S16_MIN || v > S16_MAX)
+ return -EINVAL;
+
+ ret = es9039_read_le(priv->regmap, p->reg + (p->shift / 8), 2, &old);
+ if (ret)
+ return ret;
+
+ if ((s16)old == (s16)v)
+ return 0;
+
+ ret = es9039_write_le(priv->regmap, p->reg + (p->shift / 8), 2,
+ (u16)v);
+ if (ret)
+ return ret;
+
+ return 1;
+}
+
+#define ES9039_S16(xname, xreg, xshift) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = es9039_s16_info, \
+ .get = es9039_s16_get, \
+ .put = es9039_s16_put, \
+ .private_value = (unsigned long)&(struct es9039_s16_ctl) \
+ { .reg = xreg, .shift = xshift }, \
+}
+
+/* ------------------------------------------------- multi-register integers */
+
+struct es9039_wide_ctl {
+ unsigned int reg;
+ unsigned int bytes;
+ unsigned int mask;
+ unsigned int max;
+};
+
+static int es9039_wide_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct es9039_wide_ctl *p = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int es9039_wide_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ struct es9039_wide_ctl *p = (void *)kcontrol->private_value;
+ u32 v;
+ int ret;
+
+ ret = es9039_read_le(priv->regmap, p->reg, p->bytes, &v);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = v & p->mask;
+ return 0;
+}
+
+static int es9039_wide_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ struct es9039_wide_ctl *p = (void *)kcontrol->private_value;
+ long v = ucontrol->value.integer.value[0];
+ u32 old;
+ int ret, i;
+
+ if (v < 0 || v > p->max)
+ return -EINVAL;
+
+ ret = es9039_read_le(priv->regmap, p->reg, p->bytes, &old);
+ if (ret)
+ return ret;
+
+ if ((old & p->mask) == (u32)v)
+ return 0;
+
+ /*
+ * Write one register at a time through regmap_update_bits() rather
+ * than reading the whole field and writing it back.
+ *
+ * The bits outside the field are not ours: ES9039_AUTOMUTE_TIME spans
+ * registers 124-125 while MUTE_RAMP_TO_GND is bit 3 of register 125
+ * and has a control of its own. A read-modify-write over the pair
+ * leaves a window in which that other control can write between our
+ * read and our write, and its change is then lost - the ALSA put path
+ * holds card->controls_rwsem for READ, so two controls really can run
+ * at once. Per-register update_bits closes the window inside regmap's
+ * own lock and never touches a bit outside p->mask.
+ */
+ for (i = 0; i < p->bytes; i++) {
+ u8 mask = (p->mask >> (8 * i)) & 0xff;
+
+ if (!mask)
+ continue;
+
+ ret = regmap_update_bits(priv->regmap, p->reg + i, mask,
+ ((u32)v >> (8 * i)) & mask);
+ if (ret)
+ return ret;
+ }
+
+ return 1;
+}
+
+#define ES9039_WIDE(xname, xreg, xbytes, xmask, xmax) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = es9039_wide_info, \
+ .get = es9039_wide_get, \
+ .put = es9039_wide_put, \
+ .private_value = (unsigned long)&(struct es9039_wide_ctl) \
+ { .reg = xreg, .bytes = xbytes, \
+ .mask = xmask, .max = xmax }, \
+}
+
+/* ------------------------------------------------ programmable FIR upload --
+ *
+ * Write-only, deliberately. The chip has a PROG_COEFF_OUT register (248-246)
+ * described only as "Programmable FIR coefficient readback", but it is not a
+ * RAM read port: it returns the LAST COEFFICIENT WRITTEN, whatever address is
+ * selected in PROG_COEFF_ADDR. Measured over raw I2C with this driver out of
+ * the path - two different coefficients written to
+ * addresses 0 and 1, then read back with five different sequences (plain, with
+ * a settle delay, with PROG_COEFF_EN set, with the address written twice, and
+ * with a WE pulse after the address). All ten reads returned the value written
+ * to address 1.
+ *
+ * A get() built on that register would return something with the shape of data
+ * and none of its meaning, so there is no get(). If ESS documents a real
+ * readback sequence, add one.
+ */
+
+/*
+ * Per-control data rides in our own struct with the soc_bytes_ext EMBEDDED,
+ * recovered by container_of. Not in soc_bytes_ext.dobj: that field belongs to
+ * the topology subsystem and only exists under CONFIG_SND_SOC_TOPOLOGY, so a
+ * driver stashing its own data there fails to build on any config without it.
+ */
+struct es9039_fir_ctl {
+ struct soc_bytes_ext be;
+ unsigned int taps;
+ bool stage_4x;
+};
+
+/*
+ * Push one stage's coefficients into the RAM. Callers hold priv->lock: both
+ * FIR controls drive the same address, data and strobe registers, so two
+ * uploads at once would interleave into each other's RAM, and resume re-uploads
+ * through this same path.
+ *
+ * PROG_COEFF_WE is a per-coefficient strobe, not a gate held open across the
+ * upload. The datasheet's sequence is address, data, raise WE, lower WE, once
+ * per coefficient. Holding it high for the whole loop also appears to work on
+ * ES9039Q2M silicon, but "appears to work" is not a specification.
+ */
+static int es9039_fir_upload(struct es9039q2m_priv *priv, const u8 *data,
+ unsigned int taps, bool stage_4x)
+{
+ unsigned int i;
+ int ret = 0;
+
+ for (i = 0; i < taps; i++) {
+ ret = regmap_write(priv->regmap, ES9039_PROG_RAM_ADDR,
+ (stage_4x ? ES9039_PROG_STAGE_4X : 0) |
+ FIELD_PREP(ES9039_PROG_ADDR_MASK, i));
+ if (ret)
+ goto out;
+
+ ret = regmap_bulk_write(priv->regmap, ES9039_PROG_RAM_DATA,
+ &data[i * ES9039_COEFF_BYTES],
+ ES9039_COEFF_BYTES);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_PROG_RAM_CTRL,
+ ES9039_PROG_COEFF_WE,
+ ES9039_PROG_COEFF_WE);
+ if (ret)
+ goto out;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_PROG_RAM_CTRL,
+ ES9039_PROG_COEFF_WE, 0);
+ if (ret)
+ goto out;
+ }
+
+out:
+ regmap_update_bits(priv->regmap, ES9039_PROG_RAM_CTRL,
+ ES9039_PROG_COEFF_WE, 0);
+ return ret;
+}
+
+static int es9039_fir_put(struct snd_kcontrol *kcontrol,
+ const unsigned int __user *bytes, unsigned int size)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ struct soc_bytes_ext *be = (void *)kcontrol->private_value;
+ struct es9039_fir_ctl *p = container_of(be, struct es9039_fir_ctl, be);
+ unsigned int idx = p->stage_4x ? 1 : 0;
+ u8 *buf;
+ int ret;
+
+ if (size != p->taps * ES9039_COEFF_BYTES)
+ return -EINVAL;
+
+ buf = memdup_user(bytes, size);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ scoped_guard(mutex, &priv->lock) {
+ ret = es9039_fir_upload(priv, buf, p->taps, p->stage_4x);
+ if (!ret) {
+ /*
+ * Keep a copy. The RAM cannot be read back, so this is
+ * the only record of what is in it, and resume has
+ * nothing else to restore from.
+ */
+ memcpy(priv->fir_shadow[idx], buf, size);
+ priv->fir_taps[idx] = p->taps;
+ }
+ }
+
+ kfree(buf);
+ if (ret)
+ return ret;
+
+ return 1;
+}
+
+static struct es9039_fir_ctl es9039_fir2x = {
+ .be = { .max = ES9039_FIR2X_TAPS * ES9039_COEFF_BYTES,
+ .put = es9039_fir_put },
+ .taps = ES9039_FIR2X_TAPS,
+};
+
+static struct es9039_fir_ctl es9039_fir4x = {
+ .be = { .max = ES9039_FIR4X_TAPS * ES9039_COEFF_BYTES,
+ .put = es9039_fir_put },
+ .taps = ES9039_FIR4X_TAPS,
+ .stage_4x = true,
+};
+
+#define ES9039_FIR(xname, xctl) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = snd_soc_bytes_info_ext, \
+ .tlv.c = snd_soc_bytes_tlv_callback, \
+ .access = SNDRV_CTL_ELEM_ACCESS_TLV_WRITE | \
+ SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK, \
+ .private_value = (unsigned long)&(xctl).be, \
+}
+
+/* ------------------------------------------------------- status (read-only) */
+
+struct es9039_stat_ctl {
+ unsigned int reg;
+ unsigned int mask;
+ unsigned int max;
+};
+
+static int es9039_stat_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct es9039_stat_ctl *p = (void *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = p->max;
+ return 0;
+}
+
+static int es9039_stat_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ struct es9039_stat_ctl *p = (void *)kcontrol->private_value;
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(priv->regmap, p->reg, &v);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] =
+ (v & p->mask) >> (ffs(p->mask) - 1);
+ return 0;
+}
+
+#define ES9039_STAT(xname, xreg, xmask, xmax) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | \
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = es9039_stat_info, \
+ .get = es9039_stat_get, \
+ .private_value = (unsigned long)&(struct es9039_stat_ctl) \
+ { .reg = xreg, .mask = xmask, .max = xmax }, \
+}
+
+/*
+ * Detected sample rate.
+ *
+ * When the part's own rate detector has a valid ratio, use it - it is measured
+ * from the incoming frame clock and is the ground truth:
+ *
+ * FS = Y * SYS_CLK / ((X + 1) * (128 >> 64FS_MODE))
+ *
+ * with X = IDAC_DIV_AUTO and Y = 2 when IDAC_HALF_DIV_AUTO reports a
+ * half-integer multiple.
+ *
+ * That detector is UNAVAILABLE on any board running the DAC asynchronously -
+ * register 3[7] AUTO_FS_DETECT carries the note "Cannot be used in ASYNC mode".
+ * A board feeding a free-running oscillator and letting the ASRC absorb the
+ * difference is precisely that case, and it is the preferable design, so the
+ * detector reading 0 there is expected rather than a fault. Fall back to the
+ * rate the stream was opened at, which is what a front panel wants to show.
+ * Reports 0 only when nothing is playing and the chip has no lock either.
+ */
+static int es9039_rate_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 = 1536000;
+ return 0;
+}
+
+static int es9039_rate_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ unsigned int fsreg, sysreg, div, y, den;
+ int ret;
+
+ ucontrol->value.integer.value[0] = priv->stream_rate;
+
+ if (!priv->mclk_rate)
+ return 0;
+
+ ret = regmap_read(priv->regmap, ES9039_AUTO_FS_READ, &fsreg);
+ if (ret)
+ return ret;
+
+ if (!(fsreg & ES9039_FS_DIV_VALID))
+ return 0; /* async mode: keep the stream rate set above */
+
+ ret = regmap_read(priv->regmap, ES9039_SYSTEM_CONFIG, &sysreg);
+ if (ret)
+ return ret;
+
+ div = FIELD_GET(ES9039_FS_DIV_MASK, fsreg) + 1;
+ y = (fsreg & ES9039_FS_HALF_DIV) ? 2 : 1;
+ den = div * ((sysreg & ES9039_64FS_MODE) ? 64 : 128);
+
+ ucontrol->value.integer.value[0] =
+ DIV_ROUND_CLOSEST(priv->mclk_rate * y, den);
+ return 0;
+}
+
+static const char * const es9039_stream_texts[] = {
+ "PCM", "DSD", "DoP", "S/PDIF",
+};
+
+static int es9039_stream_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ return snd_ctl_enum_info(uinfo, 1, ARRAY_SIZE(es9039_stream_texts),
+ es9039_stream_texts);
+}
+
+static int es9039_stream_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ unsigned int v;
+ int ret;
+
+ ret = regmap_read(priv->regmap, ES9039_INPUT_STREAM_READ, &v);
+ if (ret)
+ return ret;
+
+ ucontrol->value.enumerated.item[0] =
+ FIELD_GET(ES9039_RD_INPUT_SEL_MASK, v);
+ return 0;
+}
+
+/* ----------------------------------------------------------------- controls */
+
+static const DECLARE_TLV_DB_SCALE(es9039_vol_tlv, -12750, 50, 1);
+
+static const char * const es9039_filter_texts[] = {
+ "Minimum Phase",
+ "Linear Phase Apodizing Fast Roll-Off",
+ "Linear Phase Fast Roll-Off",
+ "Linear Phase Fast Roll-Off Low Ripple",
+ "Linear Phase Slow Roll-Off",
+ "Minimum Phase Fast Roll-Off",
+ "Minimum Phase Slow Roll-Off",
+ "Minimum Phase Slow Roll-Off Low Dispersion",
+};
+
+static SOC_ENUM_SINGLE_DECL(es9039_filter_enum, ES9039_FILTER_SHAPE, 0,
+ es9039_filter_texts);
+
+/* IIR_BW is a multiple of the datapath bandwidth, not a frequency. */
+static const char * const es9039_iir_texts[] = {
+ "Reserved", "BW x8", "BW x4", "BW x2", "BW", "BW /2", "BW /4", "BW /8",
+};
+
+static SOC_ENUM_SINGLE_DECL(es9039_iir_enum, ES9039_IIR_SPDIF, 0,
+ es9039_iir_texts);
+
+static const char * const es9039_nsmod_texts[] = {
+ "Default", "Wide Bandwidth",
+};
+
+static const unsigned int es9039_nsmod_values[] = {
+ ES9039_NSMOD_DEFAULT, ES9039_NSMOD_WIDE,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(es9039_nsmod_enum, ES9039_NSMOD, 1,
+ GENMASK(3, 0), es9039_nsmod_texts,
+ es9039_nsmod_values);
+
+static int es9039_dop_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+
+ ucontrol->value.integer.value[0] = priv->dop_auto;
+ return 0;
+}
+
+/*
+ * Automatic DoP detection is two bits in two registers, and they are one
+ * setting: AUTO_INPUT_SEL lets the part choose a decoder from the data, and
+ * ENABLE_DOP_DECODE is what gives it a DoP decoder to choose. Enabling the
+ * first without the second leaves the part hunting for a marker it has no
+ * decoder for, whereupon it finds no valid DoP and mutes.
+ *
+ * Everything that changes the setting goes through here, so the pair can
+ * never be written apart. Callers hold priv->lock.
+ */
+static int es9039_apply_dop(struct es9039q2m_priv *priv, bool on)
+{
+ int ret;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_SYS_MODE,
+ ES9039_ENABLE_DOP_DECODE,
+ on ? ES9039_ENABLE_DOP_DECODE : 0);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(priv->regmap, ES9039_INPUT_SEL,
+ ES9039_AUTO_INPUT_SEL,
+ on ? ES9039_AUTO_INPUT_SEL : 0);
+}
+
+/* Caller holds priv->lock. */
+static int es9039_apply_mute(struct es9039q2m_priv *priv)
+{
+ unsigned int val = priv->mute_user;
+
+ if (priv->mute_stream)
+ val = ES9039_MUTE_BOTH;
+
+ return regmap_update_bits(priv->regmap, ES9039_DAC_MUTE,
+ ES9039_MUTE_BOTH, val);
+}
+
+/*
+ * The control is inverted - 1 means playing - so a zero here is a mute
+ * request. It reports priv->mute_user rather than the register, because the
+ * register also carries the stream mute and the user did not ask for that.
+ */
+static int es9039_mute_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+
+ guard(mutex)(&priv->lock);
+
+ ucontrol->value.integer.value[0] = !(priv->mute_user & ES9039_MUTE_CH1);
+ ucontrol->value.integer.value[1] = !(priv->mute_user & ES9039_MUTE_CH2);
+
+ return 0;
+}
+
+static int es9039_mute_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ unsigned int val = 0, old;
+ int ret;
+
+ if (!ucontrol->value.integer.value[0])
+ val |= ES9039_MUTE_CH1;
+ if (!ucontrol->value.integer.value[1])
+ val |= ES9039_MUTE_CH2;
+
+ guard(mutex)(&priv->lock);
+
+ if (val == priv->mute_user)
+ return 0;
+
+ /*
+ * Commit to the register first. If the write fails and the shadow has
+ * already moved, get() reports a mute the part is not in and the
+ * val == mute_user test above turns a retry with the same value into a
+ * no-op, leaving the control permanently wrong.
+ */
+ old = priv->mute_user;
+ priv->mute_user = val;
+
+ ret = es9039_apply_mute(priv);
+ if (ret) {
+ priv->mute_user = old;
+ return ret;
+ }
+
+ return 1;
+}
+
+static int es9039_dop_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ bool on = !!ucontrol->value.integer.value[0], old;
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ if (on == priv->dop_auto)
+ return 0;
+
+ /*
+ * hw_params() programs the decoder and INPUT_SEL from dop_auto, so
+ * changing it under a running stream would apply half the setting now
+ * and the rest at the next open. Refuse instead of half-applying.
+ */
+ if (priv->stream_rate)
+ return -EBUSY;
+
+ /* Same reasoning as es9039_mute_put(): the shadow follows the write. */
+ old = priv->dop_auto;
+ priv->dop_auto = on;
+
+ ret = es9039_apply_dop(priv, on);
+ if (ret) {
+ priv->dop_auto = old;
+ return ret;
+ }
+
+ return 1;
+}
+
+/*
+ * The two channel volumes live in separate registers, so a stereo change is
+ * two I2C writes and the channels are briefly at different levels in between.
+ * VOLUME_HOLD exists for exactly this: while it is set the part accepts writes
+ * to registers 74-75 without applying them, and clearing it applies both at
+ * once. Raise it, let the generic handler do the writes, drop it again.
+ *
+ * It is dropped unconditionally, including on the error path, because a stuck
+ * VOLUME_HOLD would silently freeze the volume control.
+ */
+static int es9039_vol_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(c);
+ int ret;
+
+ /*
+ * The hold has to cover the pair of channel writes, so it has to be
+ * serialised. Two callers interleaving would let the second write its
+ * volumes after the first had already dropped VOLUME_HOLD, which is
+ * exactly the torn stereo update the bit exists to prevent.
+ */
+ guard(mutex)(&priv->lock);
+
+ ret = regmap_update_bits(priv->regmap, ES9039_IIR_SPDIF,
+ ES9039_VOLUME_HOLD, ES9039_VOLUME_HOLD);
+ if (ret)
+ return ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+ regmap_update_bits(priv->regmap, ES9039_IIR_SPDIF,
+ ES9039_VOLUME_HOLD, 0);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new es9039q2m_controls[] = {
+ /* --- level --- */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Volume",
+ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
+ SNDRV_CTL_ELEM_ACCESS_TLV_READ,
+ .info = snd_soc_info_volsw,
+ .get = snd_soc_get_volsw,
+ .put = es9039_vol_put,
+ .tlv.p = es9039_vol_tlv,
+ .private_value = SOC_DOUBLE_R_VALUE(ES9039_VOLUME_CH1,
+ ES9039_VOLUME_CH2, 0, 0,
+ ES9039_VOL_MAX, 1),
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Master Playback Switch",
+ .info = snd_soc_info_volsw,
+ .get = es9039_mute_get,
+ .put = es9039_mute_put,
+ .private_value = SOC_DOUBLE_VALUE(ES9039_DAC_MUTE, 0, 1, 0, 1,
+ 1, 0),
+ },
+ SOC_DOUBLE("DAC Invert Switch", ES9039_DAC_INVERT, 0, 1, 1, 0),
+ SOC_SINGLE("Volume Ramp Up Rate", ES9039_VOL_RATE_UP, 0, 255, 0),
+ SOC_SINGLE("Volume Ramp Down Rate", ES9039_VOL_RATE_DOWN, 0, 255, 0),
+ SOC_SINGLE("Soft Ramp Time", ES9039_SOFT_RAMP, 0,
+ ES9039_SOFT_RAMP_MAX, 0),
+
+ /* --- reconstruction filter --- */
+ SOC_ENUM("Filter Shape", es9039_filter_enum),
+ SOC_ENUM("IIR Bandwidth", es9039_iir_enum),
+ SOC_ENUM("Modulator Bandwidth", es9039_nsmod_enum),
+ SOC_SINGLE("IIR Filter Bypass Switch", ES9039_DAC_PATH, 2, 1, 0),
+ SOC_SINGLE("FIR 2x Bypass Switch", ES9039_DAC_PATH, 0, 1, 0),
+ SOC_SINGLE("FIR 4x Bypass Switch", ES9039_DAC_PATH, 1, 1, 0),
+ SOC_SINGLE("Custom FIR Switch", ES9039_PROG_RAM_CTRL, 0, 1, 0),
+ ES9039_FIR("FIR 2x Coefficients", es9039_fir2x),
+ ES9039_FIR("FIR 4x Coefficients", es9039_fir4x),
+
+ /*
+ * Not "... Volume", because these do not set a level. They are signed
+ * correction coefficients for the second and third harmonic: they
+ * cancel distortion the analogue stage adds, and turning one up does
+ * not make anything louder. Useful values come from measuring a given
+ * board's distortion on an analyser and solving for them; zero, the
+ * reset value, is the only honest default until someone has.
+ */
+ /* --- distortion compensation --- */
+ ES9039_S16("THD Compensation C2 CH1", ES9039_THD_C2, 0),
+ ES9039_S16("THD Compensation C2 CH2", ES9039_THD_C2, 16),
+ ES9039_S16("THD Compensation C3 CH1", ES9039_THD_C3, 0),
+ ES9039_S16("THD Compensation C3 CH2", ES9039_THD_C3, 16),
+
+ /* --- automute --- */
+ SOC_DOUBLE("Automute Switch", ES9039_AUTOMUTE_EN, 0, 1, 1, 0),
+ ES9039_WIDE("Automute Time", ES9039_AUTOMUTE_TIME, 2,
+ ES9039_AUTOMUTE_TIME_MASK, 2047),
+ ES9039_WIDE("Automute Level", ES9039_AUTOMUTE_LEVEL, 2, 0xffff, 65535),
+ ES9039_WIDE("Automute Off Level", ES9039_AUTOMUTE_OFF_LEVEL, 2,
+ 0xffff, 65535),
+ SOC_SINGLE("Mute Ramp To Ground Switch", ES9039_AUTOMUTE_TIME + 1,
+ 3, 1, 0),
+ SOC_SINGLE("DSD DC Automute Switch", ES9039_MONITOR_CFG, 7, 1, 1),
+ SOC_SINGLE("DSD Mute Pattern Switch", ES9039_MONITOR_CFG, 6, 1, 1),
+ SOC_SINGLE("PCM DC Automute Switch", ES9039_MONITOR_CFG, 3, 1, 1),
+
+ /* --- stream --- */
+ /*
+ * On by default. DoP is designed to be detected, not announced: the
+ * player just sends it and a DoP-aware DAC notices the marker, which
+ * is why a DAC that does not notice plays it as near-silence rather
+ * than noise. Players rely on that, and none of them can reach into
+ * ALSA to flip a mode first.
+ *
+ * Left switchable because automatic detection is a pattern match, and
+ * anyone worried about PCM material that happens to look like DoP can
+ * turn it off and get strictly PCM.
+ */
+ SOC_SINGLE_BOOL_EXT("DoP Auto Detect Switch", 0,
+ es9039_dop_get, es9039_dop_put),
+
+ /* --- status, read-only --- */
+ ES9039_STAT("Automute Active CH1", ES9039_AUTOMUTE_READ, BIT(0), 1),
+ ES9039_STAT("Automute Active CH2", ES9039_AUTOMUTE_READ, BIT(1), 1),
+ ES9039_STAT("DoP Valid", ES9039_INPUT_STREAM_READ,
+ ES9039_RD_DOP_VALID, 1),
+ ES9039_STAT("TDM Data Valid", ES9039_INPUT_STREAM_READ,
+ ES9039_RD_TDM_VALID, 1),
+ ES9039_STAT("SPDIF Valid", ES9039_INPUT_STREAM_READ,
+ ES9039_RD_SPDIF_VALID, 1),
+ /*
+ * Register 235-234 is one 16-bit word and BCK_WS_FAIL_SOURCE is bit
+ * 7 of it, so it sits in the low byte at ES9039_IRQ_SOURCES. This
+ * read the byte above with the same mask, which is bit 15 - reserved
+ * - so the control could never have reported a fault. The named
+ * constant now carries the bit rather than a bare BIT(7).
+ *
+ * The flag is meaningful only with the BCK and WS monitors enabled,
+ * which they are at reset and the driver leaves alone.
+ */
+ ES9039_STAT("Clock Fault", ES9039_IRQ_SOURCES,
+ ES9039_SRC_BCK_WS_FAIL, 1),
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Detected Sample Rate",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = es9039_rate_info,
+ .get = es9039_rate_get,
+ },
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "Detected Input Format",
+ .access = SNDRV_CTL_ELEM_ACCESS_READ |
+ SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+ .info = es9039_stream_info,
+ .get = es9039_stream_get,
+ },
+};
+
+/* --------------------------------------------------------------------- DAPM */
+
+static const struct snd_soc_dapm_widget es9039q2m_widgets[] = {
+ SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("AOUTL"),
+ SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route es9039q2m_routes[] = {
+ { "DAC", NULL, "Playback" },
+ { "AOUTL", NULL, "DAC" },
+ { "AOUTR", NULL, "DAC" },
+};
+
+/* ---------------------------------------------------------------------- DAI */
+
+/*
+ * MCLK limits, all from the datasheet:
+ *
+ * - 50 MHz absolute maximum ("Max MCLK Frequency", electrical specification).
+ *
+ * - Table 7 note 1: MCLK >= 128 * FS synchronous, MCLK > 130 * FS
+ * asynchronous. The gap is small but not academic - with a 24.576 MHz
+ * clock, 128 * FS puts 192 kHz exactly at the limit while 130 * FS puts the
+ * ceiling at 189 kHz. Note that the asynchronous bound is strict.
+ *
+ * - Register 0[6] ENABLE_64FS_MODE runs the interpolation path at 64FS and is
+ * "used only for PCM high sample rates such as 768kHz with a 49.152MHz or
+ * 384kHz with 24.576MHz clock" - both of which are MCLK = 64 * FS. Since
+ * 128 * 768000 is 98.304 MHz, well over the ceiling, 64FS mode is the only
+ * way 705.6 and 768 kHz are reachable at all.
+ */
+#define ES9039_MAX_MCLK 50000000
+#define ES9039_64FS_MCLK_FS 64
+#define ES9039_SYNC_MIN_MCLK_FS 128
+#define ES9039_ASYNC_MIN_MCLK_FS 130
+
+/*
+ * MCLK/FS ratios to try on a settable clock, highest first. 256 leaves the
+ * part inside the window its automatic clock gearing aims for - register 5[2]:
+ * "MCLK will be geared down until 128FS <= SYS_CLK < 256FS" - and 64 is 64FS
+ * mode, which only becomes the choice when nothing larger fits under 50 MHz.
+ */
+static const unsigned int es9039_mclk_ratios[] = { 512, 256, 128, 64 };
+
+/*
+ * Synchronous means the frame clock and MCLK come from the same place. That is
+ * true when the part generates BCK and WS itself, and it is true when the MCLK
+ * we were given can be set to a multiple of the sample rate. Only a fixed
+ * oscillator feeding a consumer-mode part is genuinely asynchronous, and only
+ * then does the ASRC have anything to do.
+ */
+static bool es9039_is_sync(struct es9039q2m_priv *priv)
+{
+ return priv->provider || !priv->mclk_fixed;
+}
+
+/*
+ * 64FS mode is an exact MCLK/FS ratio, not a rate threshold, and it only makes
+ * sense synchronously: at 64 * FS an asynchronous part would sit far below its
+ * own 130 * FS floor, so a fixed oscillator that happens to land on 64 * FS is
+ * a coincidence rather than a mode.
+ */
+static bool es9039_is_64fs(struct es9039q2m_priv *priv, unsigned int mclk,
+ unsigned int rate)
+{
+ return es9039_is_sync(priv) && rate &&
+ mclk == ES9039_64FS_MCLK_FS * rate;
+}
+
+/* The lowest MCLK this rate may run at outside 64FS mode. */
+static unsigned int es9039_min_mclk(struct es9039q2m_priv *priv,
+ unsigned int rate)
+{
+ if (es9039_is_sync(priv))
+ return ES9039_SYNC_MIN_MCLK_FS * rate;
+
+ /* Asynchronous is strictly greater than 130 * FS, not equal to it. */
+ return ES9039_ASYNC_MIN_MCLK_FS * rate + 1;
+}
+
+static int es9039q2m_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(dai->component);
+ unsigned int max_rate;
+
+ /*
+ * A clock whose rate can be set imposes no ceiling here: hw_params()
+ * raises MCLK to suit the rate, and fails cleanly if it cannot.
+ * Constraining from the rate MCLK merely happens to be idling at would
+ * reject rates the board can carry perfectly well.
+ */
+ if (!priv->mclk_rate || !priv->mclk_fixed)
+ return 0;
+
+ if (!es9039_is_sync(priv)) {
+ max_rate = priv->mclk_rate / ES9039_ASYNC_MIN_MCLK_FS;
+ } else if (priv->mclk_rate % ES9039_64FS_MCLK_FS == 0) {
+ /*
+ * 64FS mode reaches exactly one rate above the ordinary
+ * ceiling - MCLK / 64 - so admit it here and leave
+ * hw_params() to reject anything in between, where neither
+ * 64 * FS nor 128 * FS is satisfied.
+ */
+ max_rate = priv->mclk_rate / ES9039_64FS_MCLK_FS;
+ } else {
+ max_rate = priv->mclk_rate / ES9039_SYNC_MIN_MCLK_FS;
+ }
+
+ return snd_pcm_hw_constraint_minmax(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE,
+ 8000, max_rate);
+}
+
+/*
+ * Put the clocking where it needs to be for this rate, and tell the part which
+ * of its two timing worlds it is living in.
+ *
+ * With a settable MCLK the useful thing is to pull it to an exact multiple of
+ * the sample rate: the ASRC then has nothing to correct, which is the better
+ * arrangement whenever the board can manage it. With a fixed oscillator there
+ * is nothing to pull, and the ASRC earns its keep.
+ */
+static int es9039_setup_clocking(struct es9039q2m_priv *priv,
+ struct snd_soc_dai *dai, unsigned int rate)
+{
+ unsigned int ratio, min_mclk, div, i, target = 0;
+ bool sixtyfour;
+ int ret;
+
+ if (!priv->mclk_rate)
+ return 0;
+
+ ratio = priv->bclk_ratio ? priv->bclk_ratio : 64;
+
+ if (!priv->mclk_fixed) {
+ /*
+ * Take the highest ratio that still fits under the 50 MHz
+ * ceiling rather than always asking for 256 * FS, which would
+ * put 384 kHz at 98.304 MHz and 768 kHz at 196.608 MHz. A
+ * settable clock is synchronous by definition here - see
+ * es9039_is_sync() - so only the synchronous floors apply.
+ */
+ for (i = 0; i < ARRAY_SIZE(es9039_mclk_ratios); i++) {
+ unsigned int mult = es9039_mclk_ratios[i];
+
+ if (mult * rate > ES9039_MAX_MCLK)
+ continue;
+ if (mult < ES9039_SYNC_MIN_MCLK_FS &&
+ mult != ES9039_64FS_MCLK_FS)
+ continue;
+ /*
+ * Master mode divides MCLK down to BCK by a whole
+ * number, so a ratio that is not a multiple of the
+ * frame size cannot produce the bit clock.
+ */
+ if (priv->provider && mult % ratio)
+ continue;
+
+ target = mult * rate;
+ break;
+ }
+
+ if (!target) {
+ dev_err(dai->dev,
+ "no mclk ratio for %u Hz within %u Hz\n",
+ rate, ES9039_MAX_MCLK);
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(priv->mclk, target);
+ if (ret)
+ return ret;
+
+ /* Take what the clock settled on, not what was asked for. */
+ priv->mclk_rate = clk_get_rate(priv->mclk);
+ }
+
+ if (priv->mclk_rate > ES9039_MAX_MCLK) {
+ dev_err(dai->dev, "mclk %u Hz is above the %u Hz maximum\n",
+ priv->mclk_rate, ES9039_MAX_MCLK);
+ return -EINVAL;
+ }
+
+ sixtyfour = es9039_is_64fs(priv, priv->mclk_rate, rate);
+ min_mclk = es9039_min_mclk(priv, rate);
+
+ if (!sixtyfour && priv->mclk_rate < min_mclk) {
+ dev_err(dai->dev,
+ "mclk %u Hz is below the %u Hz %u Hz needs in this mode\n",
+ priv->mclk_rate, min_mclk, rate);
+ return -EINVAL;
+ }
+
+ /*
+ * Register 0[6]. Decided from the MCLK actually in use rather than from
+ * the sample rate alone, because the part cares about the ratio: 384 kHz
+ * from 24.576 MHz needs 64FS mode exactly as 768 kHz from 49.152 MHz
+ * does. The mode also forces a minimum phase filter regardless of
+ * FILTER_SHAPE.
+ */
+ ret = regmap_update_bits(priv->regmap, ES9039_SYSTEM_CONFIG,
+ ES9039_64FS_MODE,
+ sixtyfour ? ES9039_64FS_MODE : 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_SYS_MODE,
+ ES9039_SYNC_MODE,
+ es9039_is_sync(priv) ? ES9039_SYNC_MODE : 0);
+ if (ret)
+ return ret;
+
+ if (!priv->provider)
+ return 0;
+
+ /*
+ * Register 4: BCK = MCLK / (MASTER_BCK_DIV + 1), and WS follows from
+ * the frame length. An MCLK that is not a whole multiple of the bit
+ * clock cannot produce the requested rate at all, so say so rather
+ * than emitting something close.
+ */
+ if (priv->mclk_rate % (ratio * rate)) {
+ dev_err(dai->dev,
+ "mclk %u Hz cannot produce %u * %u Hz bit clock\n",
+ priv->mclk_rate, ratio, rate);
+ return -EINVAL;
+ }
+
+ div = priv->mclk_rate / (ratio * rate);
+ if (div < 1 || div > 256) {
+ dev_err(dai->dev, "master bck divider %u out of range\n", div);
+ return -EINVAL;
+ }
+
+ return regmap_write(priv->regmap, ES9039_CLOCK_CONFIG, div - 1);
+}
+
+static int es9039q2m_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(dai->component);
+ unsigned int cfg1 = 0, enc = 0;
+ int ret;
+
+ /*
+ * Both directions are useful and which is right is a board decision,
+ * not a driver one. Driving BCK and WS from a good MCLK is the
+ * conventional arrangement and keeps everything synchronous; taking
+ * them from a consumer-mode host and letting the ASRC absorb the
+ * mismatch is what a board with a fixed oscillator wants. The part can
+ * do either, so the driver offers either.
+ */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ priv->provider = false;
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ if (!priv->mclk)
+ return -EINVAL;
+ priv->provider = true;
+ break;
+ default:
+ /* The part cannot split them: it drives both or neither. */
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, ES9039_INPUT_SEL,
+ ES9039_PCM_MASTER_MODE,
+ priv->provider ? ES9039_PCM_MASTER_MODE : 0);
+ if (ret)
+ return ret;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ cfg1 |= ES9039_TDM_LJ_MODE;
+ break;
+ default:
+ /* Register 60 offers I2S or LJ. There is no RJ mode. */
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ enc |= ES9039_BCK_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(priv->regmap, ES9039_TDM_CONFIG1,
+ ES9039_TDM_LJ_MODE, cfg1);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_MASTER_ENC,
+ ES9039_BCK_INV, enc);
+ if (ret)
+ return ret;
+
+ priv->fmt = fmt;
+ return 0;
+}
+
+/*
+ * TDM_BIT_WIDTH describes the SLOT width on the wire, not the sample size. Those
+ * are routinely different: 16-bit samples are usually carried left-justified in
+ * 32-bit slots. Getting this wrong misaligns the channel boundaries and the
+ * result is one channel, or noise.
+ *
+ * ASoC does not hand the codec the bit clock ratio unless a machine driver sets
+ * it, so take it when offered and otherwise assume 32-bit slots - by far the
+ * most common arrangement, and what the RK3588 I2S does unconditionally
+ * (rockchip_i2s.c sets bclk_ratio = 64 at probe and never varies it with
+ * format).
+ */
+static int es9039q2m_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(dai->component);
+
+ priv->bclk_ratio = ratio;
+ return 0;
+}
+
+static int es9039q2m_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(dai->component);
+ unsigned int input_sel, decode, width, slot_bits, isel;
+ bool auto_sel;
+ int ret;
+
+ /*
+ * Held for the whole call: dop_auto decides both INPUT_SEL and
+ * SYS_MODE below, and a kcontrol write landing between the two would
+ * leave automatic detection enabled with no DoP decoder behind it.
+ */
+ guard(mutex)(&priv->lock);
+
+ priv->stream_rate = params_rate(params);
+
+ ret = es9039_setup_clocking(priv, dai, priv->stream_rate);
+ if (ret)
+ return ret;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_DSD_U8:
+ case SNDRV_PCM_FORMAT_DSD_U16_LE:
+ case SNDRV_PCM_FORMAT_DSD_U32_LE:
+ /*
+ * Forced, not auto-detected: the datasheet requires DSD data on
+ * DATA1 and DATA2 for AUTO_INPUT_SEL to identify it, which a
+ * two-channel I2S link does not provide.
+ */
+ input_sel = ES9039_INPUT_DSD;
+ decode = ES9039_ENABLE_DSD_DECODE;
+ auto_sel = false;
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ case SNDRV_PCM_FORMAT_S24_LE:
+ case SNDRV_PCM_FORMAT_S24_3LE:
+ case SNDRV_PCM_FORMAT_S32_LE:
+ /*
+ * DoP arrives inside ordinary PCM frames and is indistinguishable
+ * from PCM until the part finds the marker, so both decoders run
+ * and AUTO_INPUT_SEL picks between them. INPUT_SEL is programmed
+ * anyway as the fallback the part uses when auto-detection is
+ * switched off.
+ */
+ input_sel = ES9039_INPUT_PCM;
+ decode = ES9039_ENABLE_TDM_DECODE |
+ (priv->dop_auto ? ES9039_ENABLE_DOP_DECODE : 0);
+ auto_sel = priv->dop_auto;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ slot_bits = priv->bclk_ratio ?
+ priv->bclk_ratio / params_channels(params) : 32;
+
+ switch (slot_bits) {
+ case 16:
+ width = ES9039_WIDTH_16;
+ break;
+ case 24:
+ width = ES9039_WIDTH_24;
+ break;
+ case 32:
+ width = ES9039_WIDTH_32;
+ break;
+ default:
+ dev_err(dai->dev, "unsupported slot width %u\n", slot_bits);
+ return -EINVAL;
+ }
+
+ /*
+ * AUTO_INPUT_SEL belongs in the value as well as the mask. It was in
+ * the mask alone, so every hw_params quietly cleared it and undid what
+ * the component probe had set - which is why enabling auto-detection
+ * by hand mid-stream worked while enabling it in probe() did not.
+ * INPUT_SEL is still programmed underneath: it is what the part falls
+ * back to when auto-detection is switched off.
+ */
+ isel = FIELD_PREP(ES9039_INPUT_SEL_MASK, input_sel);
+ if (auto_sel)
+ isel |= ES9039_AUTO_INPUT_SEL;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_INPUT_SEL,
+ ES9039_AUTO_INPUT_SEL | ES9039_INPUT_SEL_MASK,
+ isel);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_TDM_CONFIG2,
+ ES9039_TDM_BIT_WIDTH_MASK,
+ FIELD_PREP(ES9039_TDM_BIT_WIDTH_MASK, width));
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_TDM_CH_NUM,
+ ES9039_TDM_CH_NUM_MASK,
+ params_channels(params) - 1);
+ if (ret)
+ return ret;
+
+ /* Switch the right decoder on for this stream, and the others off. */
+ return regmap_update_bits(priv->regmap, ES9039_SYS_MODE,
+ ES9039_DECODE_MASK, decode);
+}
+
+static int es9039q2m_mute_stream(struct snd_soc_dai *dai, int mute, int dir)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(dai->component);
+ bool old;
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ old = priv->mute_stream;
+ priv->mute_stream = mute;
+
+ ret = es9039_apply_mute(priv);
+ if (ret)
+ priv->mute_stream = old;
+
+ return ret;
+}
+
+/*
+ * stream_rate doubles as "a stream is open", which es9039_dop_put() needs in
+ * order to refuse a change it could only half-apply.
+ */
+static void es9039q2m_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(dai->component);
+
+ guard(mutex)(&priv->lock);
+
+ priv->stream_rate = 0;
+}
+
+static const struct snd_soc_dai_ops es9039q2m_dai_ops = {
+ .startup = es9039q2m_startup,
+ .shutdown = es9039q2m_shutdown,
+ .set_fmt = es9039q2m_set_fmt,
+ .set_bclk_ratio = es9039q2m_set_bclk_ratio,
+ .hw_params = es9039q2m_hw_params,
+ .mute_stream = es9039q2m_mute_stream,
+ .no_capture_mute = 1,
+};
+
+#define ES9039_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | \
+ SNDRV_PCM_FMTBIT_S32_LE | \
+ SNDRV_PCM_FMTBIT_DSD_U8 | \
+ SNDRV_PCM_FMTBIT_DSD_U16_LE | \
+ SNDRV_PCM_FMTBIT_DSD_U32_LE)
+
+static struct snd_soc_dai_driver es9039q2m_dai = {
+ .name = "es9039q2m-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_768000,
+ .formats = ES9039_FORMATS,
+ },
+ .ops = &es9039q2m_dai_ops,
+};
+
+/* ---------------------------------------------------------------- component */
+
+static int es9039q2m_component_probe(struct snd_soc_component *component)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /*
+ * Come up muted. The volume registers default to 0 dB, and an unmuted
+ * DAC arriving into an already-powered analogue stage is how you get a
+ * thump. ASoC unmutes via mute_stream() when a stream starts.
+ */
+ priv->mute_user = 0;
+ priv->mute_stream = false;
+ ret = regmap_update_bits(priv->regmap, ES9039_DAC_MUTE,
+ ES9039_MUTE_BOTH, ES9039_MUTE_BOTH);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable the datapath. SYSTEM_CONFIG[1] is clear at reset, so in
+ * software mode nothing turns the DAC on at power-up: the analogue
+ * output stays dead until this bit is set.
+ *
+ * After the mute above and never before it. Bringing the datapath up
+ * into an unmuted part whose volume registers still sit at their 0 dB
+ * reset value is exactly the thump the mute is there to prevent.
+ *
+ * Note when testing that a warm reboot does not reset this part -
+ * registers and coefficient RAM both survive one, and only removing
+ * power clears them - so the previous boot's value can hide whether
+ * this write happened at all.
+ */
+ ret = regmap_update_bits(priv->regmap, ES9039_SYSTEM_CONFIG,
+ ES9039_DAC_MODE, ES9039_DAC_MODE);
+ if (ret)
+ return ret;
+
+ /*
+ * VOLUME_HOLD set means "do not apply volume register writes", so its
+ * resting state has to be clear or the volume control would appear
+ * dead. es9039_vol_put() raises it around the pair of writes and drops
+ * it again, which is how both channels are made to move together.
+ */
+ ret = regmap_update_bits(priv->regmap, ES9039_IIR_SPDIF,
+ ES9039_VOLUME_HOLD, 0);
+ if (ret)
+ return ret;
+
+ /*
+ * Let the part identify DoP for itself. Register 57[0] AUTO_INPUT_SEL
+ * makes it choose between PCM and DoP from the data, which is the only
+ * way DoP can work in practice: players send DoP-encoded PCM and expect
+ * the DAC to notice, and none of them can flip an ALSA control first.
+ * Without this the part is told "PCM", never looks for the marker, and
+ * renders a DoP stream as the near-silent hiss the DoP marker design
+ * deliberately degrades to. Verified on hardware by enabling this
+ * mid-stream and watching reg 245 flip from PCM to DoP with DOP_VALID
+ * set.
+ *
+ * The datasheet's "data must be provided on the DATA2 pin" applies to
+ * identifying DSD, whose two channels arrive on separate data lines.
+ * DoP is ordinary stereo I2S on one line and detects correctly without
+ * it, which the same measurement establishes.
+ */
+ priv->dop_auto = true;
+ scoped_guard(mutex, &priv->lock)
+ ret = es9039_apply_dop(priv, true);
+ if (ret)
+ return ret;
+
+ /*
+ * Board defaults, applied once at probe so the part is deterministic
+ * from cold instead of inheriting whatever its reset value happens to
+ * be. Both stay user-settable through their kcontrols; these are
+ * defaults, not policy.
+ *
+ * Reconstruction filter: linear phase apodizing fast roll-off. It keeps
+ * the sharp cut and flat passband of the plain fast linear-phase filter
+ * while suppressing pre-ringing, and an apodizing response also
+ * suppresses pre-ringing already baked into the source material by the
+ * recording chain - which none of the other seven addresses. The cost
+ * is a little stopband rejection right at the band edge, well above
+ * where it can matter. Chosen this way because a blind A/B listening
+ * test found no audible difference between any of the eight,
+ * so the tie is broken on theory rather than on preference.
+ *
+ * Modulator: wide bandwidth, which is ESS's own recommendation. It
+ * moves the modulator's noise further out of band and improves
+ * linearity at high frequencies.
+ */
+ ret = regmap_update_bits(priv->regmap, ES9039_FILTER_SHAPE,
+ ES9039_FILTER_SHAPE_MASK,
+ ES9039_FILTER_APODIZING);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, ES9039_NSMOD,
+ ES9039_NSMOD_WIDE_BW_MASK,
+ FIELD_PREP(ES9039_NSMOD_WIDE_BW_MASK,
+ ES9039_NSMOD_WIDE));
+ if (ret)
+ return ret;
+
+ /* Let the part work out the incoming rate; the ASRC does the rest. */
+ return regmap_update_bits(priv->regmap, ES9039_AUTO_FS_DETECT,
+ ES9039_AUTO_FS_DETECT_EN,
+ ES9039_AUTO_FS_DETECT_EN);
+}
+
+/*
+ * System suspend.
+ *
+ * Where the part is clocked from the SoC, system suspend stops its clock as a
+ * side effect and there is nothing for a driver to do. Where the board feeds
+ * it a free-running oscillator instead - which the ASRC in front of the DAC
+ * makes an attractive design, since MCLK then need not track BCLK or LRCK -
+ * nothing in the system can gate that clock, and the part stays fully clocked
+ * for the whole of suspend.
+ *
+ * Clearing ENABLE_DAC_CLK gates the part's internal clock tree, which is the
+ * only part of its consumption software can reach. The analogue supplies are
+ * the board's business, and not every board is able to switch them.
+ *
+ * The write deliberately bypasses the cache. The cached value must keep
+ * ENABLE_DAC_CLK set, so that regcache_sync() on resume restores whatever
+ * state userspace last left the part in without this code having to remember
+ * anything itself.
+ */
+static int es9039q2m_suspend(struct snd_soc_component *component)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ regcache_cache_bypass(priv->regmap, true);
+ ret = regmap_update_bits(priv->regmap, ES9039_SYS_MODE,
+ ES9039_ENABLE_DAC_CLK, 0);
+ regcache_cache_bypass(priv->regmap, false);
+
+ if (ret)
+ dev_warn(component->dev,
+ "could not gate the DAC clock for suspend: %d\n", ret);
+
+ regcache_mark_dirty(priv->regmap);
+ regcache_cache_only(priv->regmap, true);
+
+ return 0;
+}
+
+static int es9039q2m_resume(struct snd_soc_component *component)
+{
+ struct es9039q2m_priv *priv = snd_soc_component_get_drvdata(component);
+ unsigned int i;
+ int ret;
+
+ guard(mutex)(&priv->lock);
+
+ regcache_cache_only(priv->regmap, false);
+
+ /*
+ * Mute the part before anything else, and write it straight to the
+ * hardware rather than through the cache.
+ *
+ * Where the board kept the supplies up this is a no-op - the part still
+ * holds the mute state it went to sleep with. Where the board removed
+ * them it is not: the part comes back at reset defaults, which are
+ * DAC_MODE clear, both channels UNMUTED, and both volume registers at
+ * 0x00, their 0 dB setting. regcache_sync() walks registers in ascending
+ * address order, so it would set DAC_MODE in register 0 - lighting the
+ * datapath up unmuted at full scale - and only restore the real mute in
+ * register 86, long after. That is the thump the probe path mutes to
+ * avoid, arriving by the other door.
+ *
+ * The cache is bypassed so the cached mute state is untouched; the sync
+ * below puts it back, after the volume registers at 74 and 75.
+ */
+ regcache_cache_bypass(priv->regmap, true);
+ ret = regmap_update_bits(priv->regmap, ES9039_DAC_MUTE,
+ ES9039_MUTE_BOTH, ES9039_MUTE_BOTH);
+ regcache_cache_bypass(priv->regmap, false);
+
+ if (ret)
+ dev_warn(component->dev,
+ "could not mute before resync: %d\n", ret);
+
+ ret = regcache_sync(priv->regmap);
+ if (ret)
+ dev_err(component->dev,
+ "failed to restore registers on resume: %d\n", ret);
+
+ /*
+ * Re-upload the programmable FIR RAM. regcache_sync() cannot restore
+ * it - it is write-only, so there is no cache of it to sync - and a
+ * board that removed the part's supplies comes back with that RAM
+ * undefined while the sync has just restored the filter SELECTION from
+ * the cache. Selecting the programmable filter would then point the
+ * interpolator at whatever the RAM powered up holding. After the sync,
+ * so the selection is already in place. Stages never uploaded have
+ * fir_taps == 0 and keep the part's own defaults.
+ */
+ for (i = 0; i < ES9039_FIR_STAGES; i++) {
+ int err;
+
+ if (!priv->fir_taps[i])
+ continue;
+
+ err = es9039_fir_upload(priv, priv->fir_shadow[i],
+ priv->fir_taps[i], i == 1);
+ if (err)
+ dev_err(component->dev,
+ "failed to restore FIR stage %u on resume: %d\n",
+ i, err);
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_component_driver es9039q2m_component = {
+ .probe = es9039q2m_component_probe,
+ .suspend = es9039q2m_suspend,
+ .resume = es9039q2m_resume,
+ .controls = es9039q2m_controls,
+ .num_controls = ARRAY_SIZE(es9039q2m_controls),
+ .dapm_widgets = es9039q2m_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es9039q2m_widgets),
+ .dapm_routes = es9039q2m_routes,
+ .num_dapm_routes = ARRAY_SIZE(es9039q2m_routes),
+ .idle_bias_on = 1,
+ .endianness = 1,
+};
+
+/* ------------------------------------------------------------------- power */
+
+/*
+ * The part has four supply inputs and the datasheet is specific about the
+ * order: figure 22 brings AVDD up first, VCCA about 200 us later, then the two
+ * output-stage references, and only then is the part enabled. Power-down is
+ * the exact reverse.
+ *
+ * That ordering is the whole reason these are enabled one at a time rather
+ * than with regulator_bulk_enable(), which makes no ordering guarantee at all.
+ * The bulk API is still used to GET them, where order is irrelevant. On a
+ * board that ties all four to one always-on rail the sequence costs nothing;
+ * on one that switches them separately, bringing an output stage up before its
+ * reference is precisely what the sequence exists to prevent.
+ *
+ * The datasheet gives no settling time between the last supply and the first
+ * register access, so none is invented here.
+ */
+static const char * const es9039_supply_names[ES9039_NUM_SUPPLIES] = {
+ "avdd", "vcca", "avcc-dac1", "avcc-dac2",
+};
+
+static int es9039_power_on(struct es9039q2m_priv *priv)
+{
+ int i, ret;
+
+ for (i = 0; i < ES9039_NUM_SUPPLIES; i++) {
+ ret = regulator_enable(priv->supplies[i].consumer);
+ if (ret)
+ goto err;
+
+ /* AVDD leads VCCA. It is the only interval the part specifies. */
+ if (i == 0)
+ fsleep(200);
+ }
+
+ return 0;
+
+err:
+ while (--i >= 0)
+ regulator_disable(priv->supplies[i].consumer);
+
+ return ret;
+}
+
+static void es9039_power_off(void *data)
+{
+ struct es9039q2m_priv *priv = data;
+ int i;
+
+ for (i = ES9039_NUM_SUPPLIES - 1; i >= 0; i--)
+ regulator_disable(priv->supplies[i].consumer);
+}
+
+/* --------------------------------------------------------------------- I2C */
+
+static int es9039q2m_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct es9039q2m_priv *priv;
+ unsigned int id;
+ int i, ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->regmap = devm_regmap_init_i2c(i2c, &es9039q2m_regmap);
+ if (IS_ERR(priv->regmap))
+ return dev_err_probe(dev, PTR_ERR(priv->regmap),
+ "failed to init regmap\n");
+
+ for (i = 0; i < ES9039_NUM_SUPPLIES; i++)
+ priv->supplies[i].supply = es9039_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, ES9039_NUM_SUPPLIES, priv->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to get the supplies\n");
+
+ ret = es9039_power_on(priv);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable the supplies\n");
+
+ ret = devm_add_action_or_reset(dev, es9039_power_off, priv);
+ if (ret)
+ return ret;
+
+ priv->mclk = devm_clk_get_optional_enabled(dev, "mclk");
+ if (IS_ERR(priv->mclk))
+ return dev_err_probe(dev, PTR_ERR(priv->mclk),
+ "failed to get mclk\n");
+
+ if (priv->mclk) {
+ priv->mclk_rate = clk_get_rate(priv->mclk);
+ if (priv->mclk_rate > 50000000)
+ return dev_err_probe(dev, -EINVAL,
+ "mclk %u Hz exceeds the 50 MHz maximum\n",
+ priv->mclk_rate);
+
+ /*
+ * A fixed-rate clock answers every rounding question with the
+ * one rate it has. That is the board saying "this is an
+ * oscillator, use the ASRC"; anything else can be pulled to
+ * suit the sample rate and run synchronously.
+ */
+ priv->mclk_fixed =
+ clk_round_rate(priv->mclk, priv->mclk_rate / 2) ==
+ priv->mclk_rate;
+ }
+
+ ret = devm_mutex_init(dev, &priv->lock);
+ if (ret)
+ return ret;
+
+ i2c_set_clientdata(i2c, priv);
+
+ ret = regmap_read(priv->regmap, ES9039_CHIP_ID, &id);
+ if (ret)
+ return dev_err_probe(dev, ret, "no response at 0x%02x\n",
+ i2c->addr);
+
+ if (id != ES9039_CHIP_ID_ES9039Q2M)
+ return dev_err_probe(dev, -ENODEV,
+ "unexpected chip id 0x%02x, want 0x%02x\n",
+ id, ES9039_CHIP_ID_ES9039Q2M);
+
+ dev_info(dev, "ES9039Q2M at 0x%02x, mclk %u Hz\n",
+ i2c->addr, priv->mclk_rate);
+
+ return devm_snd_soc_register_component(dev, &es9039q2m_component,
+ &es9039q2m_dai, 1);
+}
+
+static const struct of_device_id es9039q2m_of_match[] = {
+ { .compatible = "esstech,es9039q2m" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es9039q2m_of_match);
+
+static const struct i2c_device_id es9039q2m_i2c_id[] = {
+ { "es9039q2m" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, es9039q2m_i2c_id);
+
+static struct i2c_driver es9039q2m_i2c_driver = {
+ .driver = {
+ .name = "es9039q2m",
+ .of_match_table = es9039q2m_of_match,
+ },
+ .probe = es9039q2m_i2c_probe,
+ .id_table = es9039q2m_i2c_id,
+};
+module_i2c_driver(es9039q2m_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC ES9039Q2M driver");
+MODULE_AUTHOR("Karl Asseily <karl@asseily.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c
index 510cbeea5b62..316f3fe80d93 100644
--- a/sound/soc/codecs/fs210x.c
+++ b/sound/soc/codecs/fs210x.c
@@ -610,8 +610,9 @@ tag_power_down:
if (!ret) {
regcache_cache_only(fs210x->regmap, false);
regcache_mark_dirty(fs210x->regmap);
- regcache_sync(fs210x->regmap);
- fs210x->is_inited = true;
+ ret = regcache_sync(fs210x->regmap);
+ if (!ret)
+ fs210x->is_inited = true;
}
return ret;
diff --git a/sound/soc/codecs/hda.c b/sound/soc/codecs/hda.c
index 237b0b060457..084659106d5d 100644
--- a/sound/soc/codecs/hda.c
+++ b/sound/soc/codecs/hda.c
@@ -85,12 +85,11 @@ static int hda_codec_register_dais(struct hda_codec *codec, struct snd_soc_compo
struct snd_soc_dai_driver *drvs = NULL;
struct snd_soc_dapm_context *dapm;
struct hda_pcm *pcm;
- int ret, pcm_count = 0;
+ int ret, pcm_count;
- if (list_empty(&codec->pcm_list_head))
+ pcm_count = list_count_nodes(&codec->pcm_list_head);
+ if (!pcm_count)
return -EINVAL;
- list_for_each_entry(pcm, &codec->pcm_list_head, list)
- pcm_count++;
ret = hda_codec_create_dais(codec, pcm_count, &drvs);
if (ret < 0)
diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c
index 1c06fdbf0e71..7414be08d3d5 100644
--- a/sound/soc/codecs/hdac_hda.c
+++ b/sound/soc/codecs/hdac_hda.c
@@ -520,7 +520,7 @@ static int hdac_hda_codec_probe(struct snd_soc_component *component)
/*
* hdac_device core already sets the state to active and calls
* get_noresume. So enable runtime and set the device to suspend.
- * pm_runtime_enable is also called during codec registeration
+ * pm_runtime_enable is also called during codec registration
*/
pm_runtime_put(&hdev->dev);
pm_runtime_suspend(&hdev->dev);
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index d9e6fa6d3e99..b3f677e7ddc9 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -1753,7 +1753,7 @@ static int hdmi_codec_resume(struct device *dev)
/*
* As the ELD notify callback request is not entertained while the
* device is in suspend state. Need to manually check detection of
- * all pins here. pin capablity change is not support, so use the
+ * all pins here. pin capability change is not support, so use the
* already set pin caps.
*
* NOTE: this is safe to call even if the codec doesn't actually resume.
diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c
index fdd19f8e8864..aaf03f228150 100644
--- a/sound/soc/codecs/inno_rk3036.c
+++ b/sound/soc/codecs/inno_rk3036.c
@@ -309,9 +309,21 @@ static int rk3036_codec_dai_hw_params(struct snd_pcm_substream *substream,
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 rk3036_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops rk3036_codec_dai_ops = {
.set_fmt = rk3036_codec_dai_set_fmt,
.hw_params = rk3036_codec_dai_hw_params,
+ .auto_selectable_formats = &rk3036_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rk3036_codec_dai_driver[] = {
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index fa1c23dda4eb..7e8dde79b11a 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1011,10 +1011,17 @@ static int isabelle_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
#define ISABELLE_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 isabelle_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_PDM;
+
static const struct snd_soc_dai_ops isabelle_hs_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
.mute_stream = isabelle_hs_mute,
+ .auto_selectable_formats = &isabelle_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1022,6 +1029,8 @@ static const struct snd_soc_dai_ops isabelle_hf_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
.mute_stream = isabelle_hf_mute,
+ .auto_selectable_formats = &isabelle_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1029,12 +1038,16 @@ static const struct snd_soc_dai_ops isabelle_line_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
.mute_stream = isabelle_line_mute,
+ .auto_selectable_formats = &isabelle_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
static const struct snd_soc_dai_ops isabelle_ul_dai_ops = {
.hw_params = isabelle_hw_params,
.set_fmt = isabelle_set_dai_fmt,
+ .auto_selectable_formats = &isabelle_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
/* ISABELLE dai structure */
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index 6e2e292d143b..991555bec712 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1283,11 +1283,18 @@ static int lm49453_set_bias_level(struct snd_soc_component *component,
#define LM49453_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 lm49453_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops lm49453_headset_dai_ops = {
.hw_params = lm49453_hw_params,
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.mute_stream = lm49453_hp_mute,
+ .auto_selectable_formats = &lm49453_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1296,6 +1303,8 @@ static const struct snd_soc_dai_ops lm49453_speaker_dai_ops = {
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.mute_stream = lm49453_ls_mute,
+ .auto_selectable_formats = &lm49453_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1304,6 +1313,8 @@ static const struct snd_soc_dai_ops lm49453_haptic_dai_ops = {
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.mute_stream = lm49453_ha_mute,
+ .auto_selectable_formats = &lm49453_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1312,6 +1323,8 @@ static const struct snd_soc_dai_ops lm49453_ep_dai_ops = {
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.mute_stream = lm49453_ep_mute,
+ .auto_selectable_formats = &lm49453_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1320,6 +1333,8 @@ static const struct snd_soc_dai_ops lm49453_lineout_dai_ops = {
.set_sysclk = lm49453_set_dai_sysclk,
.set_fmt = lm49453_set_dai_fmt,
.mute_stream = lm49453_lo_mute,
+ .auto_selectable_formats = &lm49453_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index 282a6aaa9986..c41eb8de6160 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -1673,7 +1673,7 @@ static const struct regmap_config rx_regmap_config = {
.reg_bits = 16,
.val_bits = 32, /* 8 but with 32 bit read/write */
.reg_stride = 4,
- .cache_type = REGCACHE_FLAT,
+ .cache_type = REGCACHE_MAPLE,
.max_register = RX_MAX_OFFSET,
.writeable_reg = rx_is_writeable_register,
.volatile_reg = rx_is_volatile_register,
@@ -3732,8 +3732,8 @@ static int rx_macro_register_mclk_output(struct rx_macro *rx)
struct device *dev = rx->dev;
const char *parent_clk_name = NULL;
const char *clk_name = "lpass-rx-mclk";
+ struct clk_init_data init = {};
struct clk_hw *hw;
- struct clk_init_data init;
int ret;
if (rx->npl)
@@ -3852,6 +3852,8 @@ static int rx_macro_probe(struct platform_device *pdev)
return -EINVAL;
}
+ regcache_sort_defaults(reg_defaults, def_count);
+
struct regmap_config *reg_config __free(kfree) = kmemdup(&rx_regmap_config,
sizeof(*reg_config),
GFP_KERNEL);
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index a8e83eb60ba2..6deaaabe43af 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -602,7 +602,7 @@ static const struct regmap_config tx_regmap_config = {
.reg_bits = 16,
.val_bits = 32,
.reg_stride = 4,
- .cache_type = REGCACHE_FLAT,
+ .cache_type = REGCACHE_MAPLE,
.max_register = TX_MAX_OFFSET,
.reg_defaults = tx_defaults,
.num_reg_defaults = ARRAY_SIZE(tx_defaults),
@@ -2210,8 +2210,8 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx)
struct device *dev = tx->dev;
const char *parent_clk_name = NULL;
const char *clk_name = "lpass-tx-mclk";
+ struct clk_init_data init = {};
struct clk_hw *hw;
- struct clk_init_data init;
int ret;
if (tx->npl)
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index dbc5795b9273..03d6434a4a12 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -454,7 +454,7 @@ static const struct regmap_config va_regmap_config = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
- .cache_type = REGCACHE_FLAT,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = va_defaults,
.num_reg_defaults = ARRAY_SIZE(va_defaults),
.max_register = VA_MAX_OFFSET,
@@ -1414,7 +1414,7 @@ static int va_macro_register_fsgen_output(struct va_macro *va)
struct device_node *np = dev->of_node;
const char *parent_clk_name;
const char *clk_name = "fsgen";
- struct clk_init_data init;
+ struct clk_init_data init = {};
int ret;
if (va->has_npl_clk)
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 38e37908baf9..899c8bee4645 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -1109,7 +1109,7 @@ static const struct regmap_config wsa_regmap_config = {
.reg_bits = 16,
.val_bits = 32, /* 8 but with 32 bit read/write */
.reg_stride = 4,
- .cache_type = REGCACHE_FLAT,
+ .cache_type = REGCACHE_MAPLE,
/* .reg_defaults and .num_reg_defaults set in probe() */
.max_register = WSA_MAX_OFFSET,
.writeable_reg = wsa_is_writeable_register,
@@ -1129,27 +1129,35 @@ static const struct regmap_config wsa_regmap_config = {
int wsa_macro_set_spkr_mode(struct snd_soc_component *component, int mode)
{
struct wsa_macro *wsa = snd_soc_component_get_drvdata(component);
+ unsigned int comp1_ctl3, comp1_ctl7;
+ u8 comp_val, boost_val;
wsa->spkr_mode = mode;
- switch (mode) {
- case WSA_MACRO_SPKR_MODE_1:
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x00);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x44);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x44);
- break;
- default:
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, 0x80);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL3, 0x80, 0x80);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, 0x01);
- snd_soc_component_update_bits(component, CDC_WSA_COMPANDER1_CTL7, 0x01, 0x01);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, 0x58);
- snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, 0x58);
- break;
+ /* Compander1 CTL3/CTL7 moved on v2.5+ silicon. */
+ if (wsa->codec_version >= LPASS_CODEC_VERSION_2_5) {
+ comp1_ctl3 = CDC_2_5_WSA_COMPANDER1_CTL3;
+ comp1_ctl7 = CDC_2_5_WSA_COMPANDER1_CTL7;
+ } else {
+ comp1_ctl3 = CDC_WSA_COMPANDER1_CTL3;
+ comp1_ctl7 = CDC_WSA_COMPANDER1_CTL7;
}
+
+ if (mode == WSA_MACRO_SPKR_MODE_1) {
+ comp_val = 0x00;
+ boost_val = 0x44;
+ } else {
+ comp_val = 0x80;
+ boost_val = 0x58;
+ }
+
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL3, 0x80, comp_val);
+ snd_soc_component_update_bits(component, comp1_ctl3, 0x80, comp_val);
+ snd_soc_component_update_bits(component, CDC_WSA_COMPANDER0_CTL7, 0x01, comp_val ? 0x01 : 0x00);
+ snd_soc_component_update_bits(component, comp1_ctl7, 0x01, comp_val ? 0x01 : 0x00);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST0_BOOST_CTL, 0x7C, boost_val);
+ snd_soc_component_update_bits(component, CDC_WSA_BOOST1_BOOST_CTL, 0x7C, boost_val);
+
return 0;
}
EXPORT_SYMBOL(wsa_macro_set_spkr_mode);
@@ -2641,10 +2649,10 @@ static const struct clk_ops swclk_gate_ops = {
static int wsa_macro_register_mclk_output(struct wsa_macro *wsa)
{
+ struct clk_init_data init = {};
struct device *dev = wsa->dev;
const char *parent_clk_name;
struct clk_hw *hw;
- struct clk_init_data init;
int ret;
if (wsa->npl)
@@ -2758,6 +2766,8 @@ static int wsa_macro_probe(struct platform_device *pdev)
return -EINVAL;
}
+ regcache_sort_defaults(reg_defaults, def_count);
+
struct regmap_config *reg_config __free(kfree) = kmemdup(&wsa_regmap_config,
sizeof(*reg_config),
GFP_KERNEL);
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index df438baf05dc..1dd7881c84aa 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1342,11 +1342,21 @@ static int max98088_set_bias_level(struct snd_soc_component *component,
#define MAX98088_RATES SNDRV_PCM_RATE_8000_96000
#define MAX98088_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 max98088_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops max98088_dai1_ops = {
.set_sysclk = max98088_dai_set_sysclk,
.set_fmt = max98088_dai1_set_fmt,
.hw_params = max98088_dai1_hw_params,
.mute_stream = max98088_dai1_mute,
+ .auto_selectable_formats = &max98088_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1355,6 +1365,8 @@ static const struct snd_soc_dai_ops max98088_dai2_ops = {
.set_fmt = max98088_dai2_set_fmt,
.hw_params = max98088_dai2_hw_params,
.mute_stream = max98088_dai2_mute,
+ .auto_selectable_formats = &max98088_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 64dc442e9066..4bb5478f4642 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2382,6 +2382,29 @@ static int max98090_set_jack(struct snd_soc_component *component,
#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000
#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 max98090_selectable_formats[] = {
+ /*
+ * Non RIGHT_J formats doesn't have 24bit support.
+ * priority is RIGHT_J > I2S/LEFT_J/DSP_A
+ * see
+ * max98090_dai_startup()
+ */
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+};
+
static const struct snd_soc_dai_ops max98090_dai_ops = {
.startup = max98090_dai_startup,
.set_sysclk = max98090_dai_set_sysclk,
@@ -2390,6 +2413,8 @@ static const struct snd_soc_dai_ops max98090_dai_ops = {
.hw_params = max98090_dai_hw_params,
.mute_stream = max98090_dai_mute,
.trigger = max98090_dai_trigger,
+ .auto_selectable_formats = max98090_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(max98090_selectable_formats),
.no_capture_mute = 1,
};
@@ -2423,11 +2448,6 @@ static int max98090_probe(struct snd_soc_component *component)
dev_dbg(component->dev, "max98090_probe\n");
- max98090->mclk = devm_clk_get(component->dev, "mclk");
- if (IS_ERR(max98090->mclk))
- if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
max98090->component = component;
/* Reset the codec, the DSP core, and disable all interrupts */
@@ -2608,6 +2628,11 @@ static int max98090_i2c_probe(struct i2c_client *i2c)
goto err_enable;
}
+ max98090->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(max98090->mclk))
+ if (PTR_ERR(max98090->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
ret = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
max98090_interrupt, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"max98090_interrupt", max98090);
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index d93436046849..c827fd47a5b0 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -1413,22 +1413,36 @@ static int max98095_set_bias_level(struct snd_soc_component *component,
#define MAX98095_RATES SNDRV_PCM_RATE_8000_96000
#define MAX98095_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 max98095_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops max98095_dai1_ops = {
.set_sysclk = max98095_dai_set_sysclk,
.set_fmt = max98095_dai1_set_fmt,
.hw_params = max98095_dai1_hw_params,
+ .auto_selectable_formats = &max98095_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops max98095_dai2_ops = {
.set_sysclk = max98095_dai_set_sysclk,
.set_fmt = max98095_dai2_set_fmt,
.hw_params = max98095_dai2_hw_params,
+ .auto_selectable_formats = &max98095_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops max98095_dai3_ops = {
.set_sysclk = max98095_dai_set_sysclk,
.set_fmt = max98095_dai3_set_fmt,
.hw_params = max98095_dai3_hw_params,
+ .auto_selectable_formats = &max98095_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver max98095_dai[] = {
@@ -1991,11 +2005,6 @@ static int max98095_probe(struct snd_soc_component *component)
struct i2c_client *client;
int ret = 0;
- max98095->mclk = devm_clk_get(component->dev, "mclk");
- if (IS_ERR(max98095->mclk))
- if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
/* reset the codec, the DSP core, and disable all interrupts */
max98095_reset(component);
@@ -2139,6 +2148,11 @@ static int max98095_i2c_probe(struct i2c_client *i2c)
return ret;
}
+ max98095->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (IS_ERR(max98095->mclk))
+ if (PTR_ERR(max98095->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
max98095->devtype = (uintptr_t)i2c_get_match_data(i2c);
i2c_set_clientdata(i2c, max98095);
max98095->pdata = i2c->dev.platform_data;
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
index c8d4c68af562..b62bd98f2252 100644
--- a/sound/soc/codecs/max98371.c
+++ b/sound/soc/codecs/max98371.c
@@ -322,9 +322,16 @@ static const struct snd_soc_dapm_route max98371_audio_map[] = {
#define MAX98371_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE | \
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+static const u64 max98371_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops max98371_dai_ops = {
.set_fmt = max98371_dai_set_fmt,
.hw_params = max98371_dai_hw_params,
+ .auto_selectable_formats = &max98371_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver max98371_dai[] = {
diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c
index cbd7ebffa6d6..4e52282ace7f 100644
--- a/sound/soc/codecs/max98373-i2c.c
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -398,10 +398,20 @@ static int max98373_dai_tdm_slot(struct snd_soc_dai *dai,
#define MAX98373_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 max98373_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops max98373_dai_ops = {
.set_fmt = max98373_dai_set_fmt,
.hw_params = max98373_dai_hw_params,
.set_tdm_slot = max98373_dai_tdm_slot,
+ .auto_selectable_formats = &max98373_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static bool max98373_readable_register(struct device *dev, unsigned int reg)
diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c
index d97dab41bf0f..a653e0a21899 100644
--- a/sound/soc/codecs/max98388.c
+++ b/sound/soc/codecs/max98388.c
@@ -776,10 +776,20 @@ static int max98388_dai_tdm_slot(struct snd_soc_dai *dai,
#define MAX98388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 max98388_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops max98388_dai_ops = {
.set_fmt = max98388_dai_set_fmt,
.hw_params = max98388_dai_hw_params,
.set_tdm_slot = max98388_dai_tdm_slot,
+ .auto_selectable_formats = &max98388_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static bool max98388_readable_register(struct device *dev,
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index 2295fc057c71..b87cca2a5b43 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -462,11 +462,21 @@ static int max98390_dai_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
+static const u64 max98390_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops max98390_dai_ops = {
.set_sysclk = max98390_dai_set_sysclk,
.set_fmt = max98390_dai_set_fmt,
.hw_params = max98390_dai_hw_params,
.set_tdm_slot = max98390_dai_tdm_slot,
+ .auto_selectable_formats = &max98390_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int max98390_dac_event(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
index e5a2fe824c03..935319fee41b 100644
--- a/sound/soc/codecs/max98396.c
+++ b/sound/soc/codecs/max98396.c
@@ -790,10 +790,22 @@ static int max98396_dai_tdm_slot(struct snd_soc_dai *dai,
#define MAX98396_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 max98396_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops max98396_dai_ops = {
.set_fmt = max98396_dai_set_fmt,
.hw_params = max98396_dai_hw_params,
.set_tdm_slot = max98396_dai_tdm_slot,
+ .auto_selectable_formats = &max98396_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int max98396_dac_event(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index 9bd6a61212c1..c229b732121f 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -254,10 +254,21 @@ static int max9850_set_bias_level(struct snd_soc_component *component,
#define MAX9850_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 max9850_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops max9850_dai_ops = {
.hw_params = max9850_hw_params,
.set_sysclk = max9850_set_dai_sysclk,
.set_fmt = max9850_set_dai_fmt,
+ .auto_selectable_formats = &max9850_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver max9850_dai = {
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
index 4fb98505db1a..57759d85907b 100644
--- a/sound/soc/codecs/max98520.c
+++ b/sound/soc/codecs/max98520.c
@@ -326,10 +326,20 @@ static int max98520_dai_tdm_slot(struct snd_soc_dai *dai,
#define MAX98520_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 max98520_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops max98520_dai_ops = {
.set_fmt = max98520_dai_set_fmt,
.hw_params = max98520_dai_hw_params,
.set_tdm_slot = max98520_dai_tdm_slot,
+ .auto_selectable_formats = &max98520_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int max98520_dac_event(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
index 20e288e38669..35265c2502fe 100644
--- a/sound/soc/codecs/max9860.c
+++ b/sound/soc/codecs/max9860.c
@@ -459,9 +459,32 @@ static int max9860_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
}
}
+static const u64 max9860_selectable_formats[] = {
+ /*
+ * DSP_A/B has some limitations.
+ * priority is I2S/LEFT_J > DSP_A/B
+ * see
+ * max9860_hw_params()
+ */
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops max9860_dai_ops = {
.hw_params = max9860_hw_params,
.set_fmt = max9860_set_fmt,
+ .auto_selectable_formats = max9860_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(max9860_selectable_formats),
};
static struct snd_soc_dai_driver max9860_dai = {
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 07a53ec2a18a..f2214e3a19ce 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -522,12 +522,22 @@ static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
return 0;
}
+static const u64 max9867_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops max9867_dai_ops = {
.set_sysclk = max9867_set_dai_sysclk,
.set_fmt = max9867_dai_set_fmt,
.mute_stream = max9867_mute,
.startup = max9867_startup,
.hw_params = max9867_dai_hw_params,
+ .auto_selectable_formats = &max9867_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index 4302ab16a642..fe43ef99a0cb 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -484,10 +484,18 @@ static int max98925_dai_set_sysclk(struct snd_soc_dai *dai,
#define MAX98925_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 max98925_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops max98925_dai_ops = {
.set_sysclk = max98925_dai_set_sysclk,
.set_fmt = max98925_dai_set_fmt,
.hw_params = max98925_dai_hw_params,
+ .auto_selectable_formats = &max98925_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver max98925_dai[] = {
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
index 51a30c182167..5b590a1f00de 100644
--- a/sound/soc/codecs/max98926.c
+++ b/sound/soc/codecs/max98926.c
@@ -448,9 +448,17 @@ static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 max98926_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops max98926_dai_ops = {
.set_fmt = max98926_dai_set_fmt,
.hw_params = max98926_dai_hw_params,
+ .auto_selectable_formats = &max98926_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver max98926_dai[] = {
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index 524300d8fb0b..285c9d1ea721 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -463,11 +463,21 @@ static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
return 0;
}
+static const u64 max98927_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops max98927_dai_ops = {
.set_sysclk = max98927_dai_set_sysclk,
.set_fmt = max98927_dai_set_fmt,
.hw_params = max98927_dai_hw_params,
.set_tdm_slot = max98927_dai_tdm_slot,
+ .auto_selectable_formats = &max98927_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int max98927_dac_event(struct snd_soc_dapm_widget *w,
@@ -662,7 +672,7 @@ static int max98927_probe(struct snd_soc_component *component)
0x80);
regmap_write(max98927->regmap, MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,
0x1);
- /* Set inital volume (+13dB) */
+ /* Set initial volume (+13dB) */
regmap_write(max98927->regmap, MAX98927_R0036_AMP_VOL_CTRL, 0x38);
regmap_write(max98927->regmap, MAX98927_R003C_SPK_GAIN, 0x05);
/* Enable DC blocker */
@@ -860,7 +870,7 @@ static int max98927_i2c_probe(struct i2c_client *i2c)
/* voltage/current slot configuration */
max98927_slot_config(i2c, max98927);
- /* codec registeration */
+ /* codec registration */
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_max98927,
max98927_dai, ARRAY_SIZE(max98927_dai));
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 086ac97e8386..a6231a12a9b0 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -637,11 +637,21 @@ static void mc13783_remove(struct snd_soc_component *component)
#define MC13783_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 mc13783_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops mc13783_ops_dac = {
.hw_params = mc13783_pcm_hw_params_dac,
.set_fmt = mc13783_set_fmt_async,
.set_sysclk = mc13783_set_sysclk_dac,
.set_tdm_slot = mc13783_set_tdm_slot_dac,
+ .auto_selectable_formats = &mc13783_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops mc13783_ops_codec = {
@@ -649,6 +659,8 @@ static const struct snd_soc_dai_ops mc13783_ops_codec = {
.set_fmt = mc13783_set_fmt_async,
.set_sysclk = mc13783_set_sysclk_codec,
.set_tdm_slot = mc13783_set_tdm_slot_codec,
+ .auto_selectable_formats = &mc13783_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
/*
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index 7209b47c2029..a94a3f9b8f90 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -492,11 +492,17 @@ static int ml26124_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static const u64 ml26124_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops ml26124_dai_ops = {
.hw_params = ml26124_hw_params,
.mute_stream = ml26124_mute,
.set_fmt = ml26124_set_dai_fmt,
.set_sysclk = ml26124_set_dai_sysclk,
+ .auto_selectable_formats = &ml26124_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
index b15bdb15dbb0..c51eae602ca0 100644
--- a/sound/soc/codecs/mt6359.c
+++ b/sound/soc/codecs/mt6359.c
@@ -82,7 +82,7 @@ static void mt6359_reset_capture_gpio(struct mt6359_priv *priv)
0x3 << 0, 0x0);
}
-/* use only when doing mtkaif calibraiton at the boot time */
+/* use only when doing mtkaif calibration at the boot time */
static void mt6359_set_dcxo(struct mt6359_priv *priv, bool enable)
{
regmap_update_bits(priv->regmap, MT6359_DCXO_CW12,
@@ -90,7 +90,7 @@ static void mt6359_set_dcxo(struct mt6359_priv *priv, bool enable)
(enable ? 1 : 0) << RG_XO_AUDIO_EN_M_SFT);
}
-/* use only when doing mtkaif calibraiton at the boot time */
+/* use only when doing mtkaif calibration at the boot time */
static void mt6359_set_clksq(struct mt6359_priv *priv, bool enable)
{
/* Enable/disable CLKSQ 26MHz */
@@ -99,7 +99,7 @@ static void mt6359_set_clksq(struct mt6359_priv *priv, bool enable)
(enable ? 1 : 0) << RG_CLKSQ_EN_SFT);
}
-/* use only when doing mtkaif calibraiton at the boot time */
+/* use only when doing mtkaif calibration at the boot time */
static void mt6359_set_aud_global_bias(struct mt6359_priv *priv, bool enable)
{
regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON13,
@@ -107,7 +107,7 @@ static void mt6359_set_aud_global_bias(struct mt6359_priv *priv, bool enable)
(enable ? 0 : 1) << RG_AUDGLB_PWRDN_VA32_SFT);
}
-/* use only when doing mtkaif calibraiton at the boot time */
+/* use only when doing mtkaif calibration at the boot time */
static void mt6359_set_topck(struct mt6359_priv *priv, bool enable)
{
regmap_update_bits(priv->regmap, MT6359_AUD_TOP_CKPDN_CON0,
diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c
index 236d94c29c24..2c95ed00a681 100644
--- a/sound/soc/codecs/nau8325.c
+++ b/sound/soc/codecs/nau8325.c
@@ -628,10 +628,21 @@ static const struct snd_soc_component_driver nau8325_component_driver = {
.num_dapm_routes = ARRAY_SIZE(nau8325_dapm_routes),
};
+static const u64 nau8325_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops nau8325_dai_ops = {
.startup = nau8325_dai_startup,
.hw_params = nau8325_hw_params,
.set_fmt = nau8325_set_fmt,
+ .auto_selectable_formats = &nau8325_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define NAU8325_RATES SNDRV_PCM_RATE_8000_96000
diff --git a/sound/soc/codecs/nau8360-dsp.c b/sound/soc/codecs/nau8360-dsp.c
new file mode 100644
index 000000000000..65ffec8b3a39
--- /dev/null
+++ b/sound/soc/codecs/nau8360-dsp.c
@@ -0,0 +1,634 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+//
+// Copyright (C) 2026 Nuvoton Technology Corp.
+// Author: David Lin <ctlin0@nuvoton.com>
+// Seven Lee <wtli@nuvoton.com>
+// John Hsu <kchsu0@nuvoton.com>
+// Neo Chang <ylchang2@nuvoton.com>
+
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/bitfield.h>
+#include <sound/soc.h>
+
+#include "nau8360-dsp.h"
+#include "nau8360.h"
+
+#define NAU8360_DSP_IDLE_RETRY 10
+static const unsigned short nau8360_dsp_addr[NAU8360_DSP_FW_NUM] = {
+ NAU8360_RF000_DSP_COMM, NAU8360_RF002_DSP_COMM };
+
+static int nau8360_dsp_chan_kcs_setup(struct snd_soc_component *cp,
+ const char *fw_name, int dsp_addr);
+
+#define NAU_DSP_CMD(_id, _msg, _setup, _reply) \
+ [_id] = { \
+ .cmd_id = _id, \
+ .msg_param = _msg, \
+ .setup_data = _setup, \
+ .reply_data = _reply, \
+ }
+
+#define NAU_DSP_CMD_ID(_id) \
+ [_id] = { \
+ .cmd_id = _id, \
+ }
+
+/*
+ * Preamble Fragment Masks:
+ * FIELD | BITS | SHIFT | MASK | DETAILS
+ * ----------------|--------|-------|------|---------------------------------
+ * length high | [31:24]| 24 | 0xff | Upper 8 bits of total length (LEN[9:2])
+ * cmd_id | [23:18]| 18 | 0x3f | Command identifier (6 bits)
+ * length low | [17:16]| 16 | 0x03 | Lower 2 bits of total length (LEN[1:0])
+ * preamble magic | [15:0] | 0 |0xffff| Preamble signature (0xB2A1)
+ */
+#define NAU8360_HOST_LEN_HIGH_MASK GENMASK(31, 24)
+#define NAU8360_HOST_CMD_ID_MASK GENMASK(23, 18)
+#define NAU8360_HOST_LEN_LOW_MASK GENMASK(17, 16)
+#define NAU8360_HOST_PREAMBLE_MASK GENMASK(15, 0)
+
+/*
+ * Payload Fragment Masks:
+ * FIELD | BITS | SHIFT | MASK | DETAILS
+ * ----------------|--------|-------|------|---------------------------------
+ * param_size | [31:16]| 16 |0xffff| Size of parameter data (16 bits)
+ * param_offset | [15:0] | 0 |0xffff| Starting offset (16 bits)
+ */
+#define NAU8360_HOST_PARAM_SIZE_MASK GENMASK(31, 16)
+#define NAU8360_HOST_PARAM_OFFSET_MASK GENMASK(15, 0)
+
+/*
+ * Trailing Fragment Masks:
+ * FIELD | BITS | SHIFT | MASK | DETAILS
+ * ----------------|--------|-------|------|---------------------------------
+ * length high | [15:14]| 14 | 0x03 | Upper 2 bits of total length
+ * padding | [13:12]| 12 | 0x03 | Padding bytes count
+ * length low | [7:0] | 0 | 0xff | Lower 8 bits of total length
+ * Note: Reassembled length = (length_high << 8) | length_low (Total 10 bits)
+ */
+#define NAU8360_TRAIL_LEN_LOW_MASK GENMASK(7, 0)
+#define NAU8360_TRAIL_PAD_MASK GENMASK(13, 12)
+#define NAU8360_TRAIL_LEN_HIGH_MASK GENMASK(15, 14)
+
+/*
+ * Reply Preamble Masks:
+ * FIELD | BITS | SHIFT | MASK | DETAILS
+ * ----------------|--------|-------|------|---------------------------------
+ * length high | [31:24]| 24 | 0xff | Upper 8 bits of total length
+ * reply_id | [23:18]| 18 | 0x3f | Reply identifier (6 bits)
+ * length low | [17:16]| 16 | 0x03 | Lower 2 bits of total length
+ * Note: Reassembled length = (length_high << 2) | length_low (Total 10 bits)
+ */
+#define NAU8360_REPLY_LEN_HIGH_MASK GENMASK(31, 24)
+#define NAU8360_REPLY_ID_MASK GENMASK(23, 18)
+#define NAU8360_REPLY_LEN_LOW_MASK GENMASK(17, 16)
+
+static const struct nau8360_cmd_info nau8360_dsp_cmd_table[] = {
+ /* { cmd_id, msg_param, setup_data, reply_data} */
+ NAU_DSP_CMD(NAU8360_DSP_CMD_GET_COUNTER, 0, 0, 1),
+ NAU_DSP_CMD(NAU8360_DSP_CMD_GET_FRAME_STATUS, 0, 0, 1),
+ NAU_DSP_CMD(NAU8360_DSP_CMD_GET_REVISION, 0, 0, 1),
+ NAU_DSP_CMD(NAU8360_DSP_CMD_GET_KCS_RSLTS, 1, 0, 1),
+ NAU_DSP_CMD(NAU8360_DSP_CMD_GET_KCS_SETUP, 1, 0, 1),
+ NAU_DSP_CMD(NAU8360_DSP_CMD_SET_KCS_SETUP, 1, 1, 0),
+ NAU_DSP_CMD_ID(NAU8360_DSP_CMD_CLK_STOP),
+ NAU_DSP_CMD_ID(NAU8360_DSP_CMD_CLK_RESTART),
+};
+
+static const char *const dsp_cmd_table[] = {
+ [NAU8360_DSP_CMD_GET_COUNTER] = "GET_COUNTER",
+ [NAU8360_DSP_CMD_GET_FRAME_STATUS] = "GET_FRAME_STATUS",
+ [NAU8360_DSP_CMD_GET_REVISION] = "GET_REVISION",
+ [NAU8360_DSP_CMD_GET_KCS_RSLTS] = "GET_KCS_RSLTS",
+ [NAU8360_DSP_CMD_GET_KCS_SETUP] = "GET_KCS_SETUP",
+ [NAU8360_DSP_CMD_SET_KCS_SETUP] = "SET_KCS_SETUP",
+ [NAU8360_DSP_CMD_CLK_STOP] = "CLK_STOP",
+ [NAU8360_DSP_CMD_CLK_RESTART] = "CLK_RESTART",
+};
+
+static int nau8360_dsp_idle(struct snd_soc_component *cp, unsigned short dsp_addr)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ unsigned int idle_pattern, timeout = NAU8360_DSP_IDLE_RETRY * USEC_PER_MSEC;
+ int ret = 0;
+
+ ret = regmap_read_poll_timeout(nau8360->regmap, dsp_addr, idle_pattern,
+ idle_pattern == NAU8360_DSP_COMM_IDLE_WORD, USEC_PER_MSEC, timeout);
+ if (ret)
+ dev_err(nau8360->dev, "Timeout waiting for DSP idle state: %d", ret);
+
+ return ret;
+}
+
+/**
+ * nau8360_pack_preamble - Pack DSP preamble fragment
+ * @cmd_id: Command ID for the DSP message
+ * @frag_len: Total length of the message fragments
+ *
+ * Return: 32-bit packed payload in Little Endian format.
+ */
+static inline u32 nau8360_pack_preamble(u8 cmd_id, u16 frag_len)
+{
+ return FIELD_PREP(NAU8360_HOST_PREAMBLE_MASK, NAU8360_DSP_COMM_PREAMBLE) |
+ FIELD_PREP(NAU8360_HOST_CMD_ID_MASK, cmd_id) |
+ FIELD_PREP(NAU8360_HOST_LEN_LOW_MASK, frag_len) |
+ FIELD_PREP(NAU8360_HOST_LEN_HIGH_MASK, frag_len >> 2);
+}
+
+/**
+ * nau8360_pack_param - Pack DSP parameter fragment
+ * @param_offset: Starting offset of the parameter data
+ * @param_size: Size of the parameter data in bytes
+ *
+ * Return: 32-bit packed payload in Little Endian format.
+ */
+static inline u32 nau8360_pack_param(u16 param_offset, u16 param_size)
+{
+ return FIELD_PREP(NAU8360_HOST_PARAM_OFFSET_MASK, param_offset) |
+ FIELD_PREP(NAU8360_HOST_PARAM_SIZE_MASK, param_size);
+}
+
+/**
+ * nau8360_pack_trailing - Pack DSP trailing fragment
+ * @frag_cnt: Current fragment count
+ * @padding: Number of padding bytes added to the final data fragment
+ *
+ * Return: 32-bit packed payload in Little Endian format.
+ */
+static inline u32 nau8360_pack_trailing(u16 frag_cnt, u8 padding)
+{
+ return FIELD_PREP(NAU8360_TRAIL_LEN_LOW_MASK, frag_cnt) |
+ FIELD_PREP(NAU8360_TRAIL_PAD_MASK, padding) |
+ FIELD_PREP(NAU8360_TRAIL_LEN_HIGH_MASK, frag_cnt >> 8);
+}
+
+static void nau8360_send_data_payload(struct snd_soc_component *cp,
+ unsigned short dsp_addr, const void *param_data, int param_size,
+ int *frag_cnt, int *padding)
+{
+ const u8 *data = (const u8 *)param_data;
+ u32 payload = 0;
+ int i, data_size = 0;
+
+ for (i = 0; i < param_size; i++) {
+ payload |= data[i] << (data_size * 8);
+ data_size++;
+
+ if (data_size == NAU8360_DSP_DATA_BYTE) {
+ snd_soc_component_write(cp, dsp_addr, payload);
+ data_size = 0;
+ payload = 0;
+ (*frag_cnt)++;
+ }
+ }
+
+ if (data_size > 0) {
+ *padding = NAU8360_DSP_DATA_BYTE - data_size;
+ snd_soc_component_write(cp, dsp_addr, payload);
+ (*frag_cnt)++;
+ }
+}
+
+static int nau8360_message_to_dsp(struct snd_soc_component *cp,
+ const struct nau8360_cmd_info *cmd_info, int frag_len, int param_offset,
+ int param_size, void *param_data, unsigned short dsp_addr)
+{
+ unsigned int payload;
+ int ret, padding = 0, frag_cnt = 0;
+
+ ret = nau8360_dsp_idle(cp, dsp_addr);
+ if (ret)
+ return ret;
+
+ /* sending preamble fragment */
+ payload = nau8360_pack_preamble(cmd_info->cmd_id, frag_len);
+ snd_soc_component_write(cp, dsp_addr, payload);
+
+ if (!cmd_info->msg_param)
+ return 0;
+
+ /* sending payload + padding */
+ payload = nau8360_pack_param(param_offset, param_size);
+ snd_soc_component_write(cp, dsp_addr, payload);
+ frag_cnt++;
+
+ if (cmd_info->setup_data)
+ nau8360_send_data_payload(cp, dsp_addr, param_data, param_size,
+ &frag_cnt, &padding);
+
+ /* sending trailing fragment */
+ frag_cnt++;
+ payload = nau8360_pack_trailing(frag_cnt, padding);
+ snd_soc_component_write(cp, dsp_addr, payload);
+
+ if (frag_cnt != frag_len) {
+ dev_err(cp->dev, "message error (CMD_ID 0x%x, LEN 0x%x) !!!",
+ cmd_info->cmd_id, frag_cnt);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int nau8360_dsp_replied(struct nau8360 *nau8360, int *length,
+ unsigned short dsp_addr)
+{
+ unsigned int reply_preamble;
+ int ret, reply_id;
+
+ ret = regmap_read_poll_timeout(nau8360->regmap, dsp_addr, reply_preamble,
+ (reply_preamble & 0xffff) == NAU8360_DSP_COMM_PREAMBLE,
+ USEC_PER_MSEC, NAU8360_DSP_IDLE_RETRY * USEC_PER_MSEC);
+ if (ret) {
+ dev_err(nau8360->dev, "timeout for reply preamble: %d", ret);
+ return ret;
+ }
+
+ *length = FIELD_GET(NAU8360_REPLY_LEN_LOW_MASK, reply_preamble);
+ *length |= FIELD_GET(NAU8360_REPLY_LEN_HIGH_MASK, reply_preamble) << 2;
+ reply_id = FIELD_GET(NAU8360_REPLY_ID_MASK, reply_preamble);
+
+ return (reply_id != NAU8360_DSP_REPLY_OK) ? -reply_id : 0;
+}
+
+static int nau8360_validate_trailing(struct snd_soc_component *cp,
+ unsigned short dsp_addr, int frag_len, int pad_len_exp)
+{
+ int ret, len_pos, pad_len;
+ unsigned int payload;
+ struct device *dev = cp->dev;
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+
+ /* reading trailing fragment */
+ ret = regmap_read(nau8360->regmap, dsp_addr, &payload);
+ if (ret) {
+ dev_err(dev, "failed to read trailing fragment");
+ return ret;
+ }
+
+ len_pos = FIELD_GET(NAU8360_TRAIL_LEN_LOW_MASK, payload);
+ len_pos |= FIELD_GET(NAU8360_TRAIL_LEN_HIGH_MASK, payload) << 8;
+
+ if (len_pos != frag_len) {
+ dev_err(dev, "LEN_POST %02X, expect %02X", len_pos, frag_len);
+ return -EPROTO;
+ }
+
+ pad_len = FIELD_GET(NAU8360_TRAIL_PAD_MASK, payload);
+ if (pad_len != pad_len_exp) {
+ dev_err(dev, "PAD_LEN %02X, expect %02X", pad_len, pad_len_exp);
+ return -EPROTO;
+ }
+
+ return 0;
+}
+
+static int nau8360_read_data_payload(struct snd_soc_component *cp,
+ unsigned short dsp_addr, int frag_len, bool msg_param,
+ void *data, int data_size, int *data_count)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ struct device *dev = cp->dev;
+ unsigned int payload;
+ u32 *data_buf = data;
+ int i, j, ret;
+
+ *data_count = (msg_param) ? data_size : 0;
+ for (i = 0; i < frag_len - 1; i++) {
+ ret = regmap_read(nau8360->regmap, dsp_addr, &payload);
+ if (ret) {
+ dev_err(dev, "failed to read payload of dsp");
+ return ret;
+ }
+
+ if (!msg_param) {
+ *data_buf++ = payload;
+ break;
+ }
+
+ if (*data_count >= NAU8360_DSP_DATA_BYTE) {
+ *data_buf++ = payload;
+ *data_count -= NAU8360_DSP_DATA_BYTE;
+ } else if (*data_count > 0) {
+ for (j = 0; j < *data_count; j++)
+ ((u8 *)data_buf)[j] = (payload >> (j * 8)) & 0xff;
+
+ *data_count = 0;
+ }
+
+ if (*data_count == 0)
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8360_reply_from_dsp(struct snd_soc_component *cp,
+ const struct nau8360_cmd_info *cmd_info, int data_size,
+ void *data, unsigned short dsp_addr)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ struct device *dev = cp->dev;
+ int ret, frag_len, pad_len_exp, data_count = 0;
+
+ ret = nau8360_dsp_replied(nau8360, &frag_len, dsp_addr);
+ if (ret)
+ return ret;
+ if (!frag_len || !cmd_info->reply_data)
+ return 0;
+ if (!data || frag_len == 1)
+ return -EINVAL;
+
+ ret = nau8360_read_data_payload(cp, dsp_addr, frag_len,
+ cmd_info->msg_param, data, data_size, &data_count);
+ if (ret)
+ return ret;
+
+ /* check the reply length same as request */
+ if (data_count && (cmd_info->cmd_id == NAU8360_DSP_CMD_GET_KCS_RSLTS ||
+ cmd_info->cmd_id == NAU8360_DSP_CMD_GET_KCS_SETUP))
+ dev_warn(dev, "payload_len %d, expected %d",
+ data_size - data_count, data_size);
+
+ pad_len_exp = cmd_info->msg_param ?
+ (frag_len - 1) * NAU8360_DSP_DATA_BYTE - (data_size - data_count) : 0;
+ return nau8360_validate_trailing(cp, dsp_addr, frag_len, pad_len_exp);
+}
+
+/**
+ * nau8360_send_dsp_command - Send command to DSP
+ *
+ * @cp: component to register
+ * @cmd_id: DSP supported command ID
+ * @kcs_setup: KCS setup structure
+ * @dsp_addr: DSP address
+ *
+ * The communication protocol is a Master-Slave type protocol
+ * where the host processor is the master and DSP is the slave.
+ * The Master initiates the communication and can either write or
+ * read back from the slave.
+ * Transactions from the Master are called "Messages",
+ * and read-back data from the Slave is called a "Reply".
+ *
+ * The function sends command to DSP according to the command ID.
+ * These commands include getting the information of DSP,
+ * getting or setting KCS configuration, or making DSP control.
+ */
+static int nau8360_send_dsp_command(struct snd_soc_component *cp, int cmd_id,
+ struct nau8360_kcs_setup *kcs_setup, unsigned short dsp_addr)
+{
+ const struct nau8360_cmd_info *cmd_info;
+ int ret = 0, frag_len = 0;
+
+ cmd_info = &nau8360_dsp_cmd_table[cmd_id];
+ if ((cmd_info->msg_param && !kcs_setup->set_len) ||
+ (cmd_info->setup_data && !kcs_setup->set_kcs_data) ||
+ (cmd_info->reply_data && !kcs_setup->get_data))
+ return -EFAULT;
+
+ /* Read up to 1kB data because the LEN field to request data is 10-bits
+ * long; and not beyond 3kB offset.
+ */
+ if (cmd_id == NAU8360_DSP_CMD_GET_KCS_SETUP &&
+ (kcs_setup->set_len > NAU8360_DSP_KCS_DAT_LEN_MAX ||
+ kcs_setup->set_kcs_offset > NAU8360_DSP_KCS_OFFSET_MAX))
+ return -ERANGE;
+
+ /* one fragment for offset and size parameters
+ * one fragment for a postamble fragment
+ */
+ if (cmd_info->msg_param)
+ frag_len += 2;
+
+ /* fragments for KCS setup writen */
+ if (cmd_info->setup_data)
+ frag_len += DIV_ROUND_UP(kcs_setup->set_len, NAU8360_DSP_DATA_BYTE);
+
+ ret = nau8360_message_to_dsp(cp, cmd_info, frag_len,
+ kcs_setup->set_kcs_offset, kcs_setup->set_len,
+ kcs_setup->set_kcs_data, dsp_addr);
+ if (ret)
+ return ret;
+
+ return nau8360_reply_from_dsp(cp, cmd_info, kcs_setup->get_len,
+ kcs_setup->get_data, dsp_addr);
+}
+
+static inline int nau8360_dsp_exec_command(struct snd_soc_component *cp, int cmd_id,
+ int offset, int set_len, void *set_data, int get_len, void *get_data,
+ int dsp_addr)
+{
+ struct nau8360_kcs_setup kcs_setup = {
+ .set_kcs_offset = offset,
+ .set_len = set_len,
+ .set_kcs_data = set_data,
+ .get_len = get_len,
+ .get_data = get_data,
+ };
+
+ return nau8360_send_dsp_command(cp, cmd_id, &kcs_setup, dsp_addr);
+}
+
+static inline int nau8360_send_dsp_broadcast(struct snd_soc_component *cp, int cmd_id)
+{
+ int i, ret;
+
+ for (i = 0; i < NAU8360_DSP_FW_NUM; i++) {
+ ret = nau8360_dsp_exec_command(cp, cmd_id, 0, 0, NULL, 0, NULL,
+ nau8360_dsp_addr[i]);
+ if (ret) {
+ dev_err(cp->dev, "DSP %x fail (%d)", nau8360_dsp_addr[i], ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * nau8360_dsp_kcs_setup - Send KCS setup command to DSP
+ *
+ * @cp: component to register
+ * @offset: address offset relative to KCS start
+ * @size: size of data writen to KCS
+ * @data: data writen to KCS setup
+ * @dsp_addr : DSP address
+ *
+ * The function sends KCS setup command to DSP for
+ * setting KCS configuration. The maximum size that you can transfer into
+ * the DSP is 96 bytes. Therefore, the driver has to split the data into
+ * 96 bytes chucks, if the setup configuration over the threshold.
+ */
+static int nau8360_dsp_kcs_setup(struct snd_soc_component *cp, int offset, int size,
+ const void *data, unsigned short dsp_addr)
+{
+ u8 *data_buf = (u8 *)data;
+ unsigned int kcs_rst = 0;
+ int retries = 0, ret, data_len, data_rem, addr_offset;
+
+ /* Limit full load of KCS_SETUP data and not beyond 3kB offset. */
+ if (!data || size > NAU8360_DSP_KCS_DAT_LEN_MAX ||
+ offset > NAU8360_DSP_KCS_OFFSET_MAX)
+ return -EINVAL;
+
+ /* sending fragments for KCS setup */
+ addr_offset = offset;
+ data_rem = size;
+
+ while (data_rem) {
+ data_len = min(data_rem, NAU8360_DSP_KCS_TX_MAX);
+
+ ret = nau8360_dsp_exec_command(cp, NAU8360_DSP_CMD_SET_KCS_SETUP,
+ addr_offset, data_len, (void *)data_buf, 0, &kcs_rst, dsp_addr);
+ if (ret) {
+ if (retries++ < NAU8360_DSP_RETRY_MAX)
+ continue;
+ return ret;
+ }
+
+ data_buf += (u8)data_len;
+ addr_offset += data_len;
+ data_rem -= data_len;
+
+ /* checking KCS result */
+ ret = nau8360_dsp_exec_command(cp, NAU8360_DSP_CMD_GET_KCS_RSLTS,
+ 0, NAU8360_DSP_DATA_BYTE, NULL,
+ NAU8360_DSP_DATA_BYTE, &kcs_rst, dsp_addr);
+ if (ret)
+ return ret;
+ if (kcs_rst != NAU8360_DSP_KCS_RSLTS_SUCCESS)
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8360_dsp_get_cmd_put(struct snd_soc_component *cp,
+ int dsp_addr, int cmd, int *value)
+{
+ struct device *dev = cp->dev;
+ int ret;
+
+ dev_dbg(dev, "send DSP %x command %s", dsp_addr, dsp_cmd_table[cmd]);
+
+ ret = nau8360_dsp_exec_command(cp, cmd, 0, sizeof(int), NULL,
+ sizeof(int), value, dsp_addr);
+ if (ret) {
+ dev_err(dev, "do command fail (%d)", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int nau8360_dsp_clock_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *cp = snd_soc_dapm_to_component(w->dapm);
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ int ret = 0;
+
+ mutex_lock(&nau8360->lock);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ ret = nau8360_send_dsp_broadcast(cp, NAU8360_DSP_CMD_CLK_RESTART);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ ret = nau8360_send_dsp_broadcast(cp, NAU8360_DSP_CMD_CLK_STOP);
+
+ mutex_unlock(&nau8360->lock);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_widget nau8360_dsp_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("DSP Clock", SND_SOC_NOPM, 0, 0, nau8360_dsp_clock_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route nau8360_dsp_dapm_routes[] = {
+ { "DSP", NULL, "HW3 Engine" },
+ { "DSP", NULL, "DSP Clock" },
+};
+
+static int nau8360_dsp_chan_kcs_setup(struct snd_soc_component *cp,
+ const char *fw_name, int dsp_addr)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ const struct firmware *fw;
+ int buf_off, buf_len;
+ int ret = 0, status = 0;
+
+ mutex_lock(&nau8360->lock);
+ ret = nau8360_dsp_get_cmd_put(cp, dsp_addr,
+ NAU8360_DSP_CMD_GET_FRAME_STATUS, &status);
+ mutex_unlock(&nau8360->lock);
+ if (ret || !(status & NAU8360_DSP_ALGO_OK)) {
+ dev_err(cp->dev, "DSP %x is not ready", dsp_addr);
+ return -EIO;
+ }
+
+ dev_info(cp->dev, "DSP %x is ready to load firmware %s, status %x",
+ dsp_addr, fw_name, status);
+
+ ret = request_firmware(&fw, fw_name, cp->dev);
+ if (ret) {
+ dev_err(cp->dev, "failed to load firmware (%d)", ret);
+ return ret;
+ }
+
+ buf_off = 0;
+ buf_len = nau8360->kcs_setup_size = fw->size;
+ mutex_lock(&nau8360->lock);
+ ret = nau8360_dsp_kcs_setup(cp, buf_off, buf_len, fw->data, dsp_addr);
+ mutex_unlock(&nau8360->lock);
+ if (ret) {
+ dev_err(cp->dev, "send DSP command %s fail (%d)",
+ dsp_cmd_table[NAU8360_DSP_CMD_SET_KCS_SETUP], ret);
+ }
+ release_firmware(fw);
+
+ return ret;
+}
+
+int nau8360_dsp_init(struct snd_soc_component *cp)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ int i, ret;
+
+ for (i = 0; i < NAU8360_DSP_FW_NUM; i++) {
+ ret = nau8360_dsp_chan_kcs_setup(cp, nau8360->dsp_firmware[i], nau8360_dsp_addr[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int nau8360_dsp_setup_controls(struct snd_soc_component *cp)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ struct snd_soc_dapm_context *dapm = nau8360->dapm;
+ int ret;
+
+ ret = snd_soc_dapm_new_controls(dapm, nau8360_dsp_dapm_widgets,
+ ARRAY_SIZE(nau8360_dsp_dapm_widgets));
+ if (ret) {
+ dev_err(cp->dev, "add DSP widget fail (%d)", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, nau8360_dsp_dapm_routes,
+ ARRAY_SIZE(nau8360_dsp_dapm_routes));
+ if (ret) {
+ dev_err(cp->dev, "add DSP route fail (%d)", ret);
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/sound/soc/codecs/nau8360-dsp.h b/sound/soc/codecs/nau8360-dsp.h
new file mode 100644
index 000000000000..f8c2c97a25c4
--- /dev/null
+++ b/sound/soc/codecs/nau8360-dsp.h
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+//
+// Copyright (C) 2026 Nuvoton Technology Corp.
+// Author: David Lin <ctlin0@nuvoton.com>
+// Seven Lee <wtli@nuvoton.com>
+// John Hsu <kchsu0@nuvoton.com>
+// Neo Chang <ylchang2@nuvoton.com>
+
+#ifndef __NAU8360_DSP_H__
+#define __NAU8360_DSP_H__
+
+#define NAU8360_DSP_COMM_IDLE_WORD 0xf4f3f2f1
+#define NAU8360_DSP_COMM_PREAMBLE 0xb2a1
+#define NAU8360_DSP_DATA_BYTE 4
+#define NAU8360_DSP_DATA_LEN (NAU8360_DSP_DATA_BYTE << 3)
+/* max bytes of data to transfer into DSP each time during the KCS setup */
+#define NAU8360_DSP_KCS_TX_MAX 96
+#define NAU8360_DSP_RETRY_MAX 3
+#define NAU8360_DSP_KCS_DAT_LEN_MAX 1024
+#define NAU8360_DSP_KCS_OFFSET_MAX 3072
+#define NAU8360_DSP_KCS_RSLTS_SUCCESS 0x10
+
+/* FRAME_STATUS (0x9) */
+#define NAU8360_DSP_SNS_OVF_SFT 31
+#define NAU8360_DSP_SNS_OVF (0x1 << NAU8360_DSP_SNS_OVF_SFT)
+#define NAU8360_DSP_AUD_UVF_SFT 30
+#define NAU8360_DSP_AUD_UVF (0x1 << NAU8360_DSP_AUD_UVF_SFT)
+#define NAU8360_DSP_AUD_OVF_SFT 29
+#define NAU8360_DSP_AUD_OVF (0x1 << NAU8360_DSP_AUD_OVF_SFT)
+#define NAU8360_DSP_ALC_STS_SFT 28
+#define NAU8360_DSP_ALC_STS (0x1 << NAU8360_DSP_ALC_STS_SFT)
+#define NAU8360_DSP_CLK_STOP_SFT 25
+#define NAU8360_DSP_CLK_STOP (0x1 << NAU8360_DSP_CLK_STOP_SFT)
+#define NAU8360_DSP_OCP_OTP_SFT 24
+#define NAU8360_DSP_OCP_OTP (0x1 << NAU8360_DSP_OCP_OTP_SFT)
+#define NAU8360_DSP_UVLO_SFT 22
+#define NAU8360_DSP_UVLO (0x1 << NAU8360_DSP_UVLO_SFT)
+#define NAU8360_DSP_OVP_SFT 21
+#define NAU8360_DSP_OVP (0x1 << NAU8360_DSP_OVP_SFT)
+#define NAU8360_DSP_APWR_DWN_SFT 20
+#define NAU8360_DSP_APWR_DWN (0x1 << NAU8360_DSP_APWR_DWN_SFT)
+#define NAU8360_DSP_SNSR_RATE_SFT 12
+#define NAU8360_DSP_SNSR_RATE_MASK (0xff << NAU8360_DSP_SNSR_RATE_SFT)
+#define NAU8360_DSP_AUD_RATE_SFT 4
+#define NAU8360_DSP_AUD_RATE_MASK (0xff << NAU8360_DSP_AUD_RATE_SFT)
+#define NAU8360_DSP_FEED_TRU_SFT 1
+#define NAU8360_DSP_FEED_TRU (0x1 << NAU8360_DSP_FEED_TRU_SFT)
+#define NAU8360_DSP_ALGO_OK 0x1
+
+#define NAU8360_DSP_FIRMDIR "Nuvoton/"
+#define NAU8360_DSP_FIRMWARE NAU8360_DSP_FIRMDIR"NAU83G60.kcs.bin"
+#define NAU8360_DSP_FW_NUM NAU8360_DSP_CORE_NUM
+#define NAU8360_DSP_FW_NAMELEN 64
+#define NAU8360_IS_DSP_REG(reg) \
+ ((reg) == NAU8360_RF000_DSP_COMM || (reg) == NAU8360_RF002_DSP_COMM)
+
+enum {
+ NAU8360_DSP_REPLY_OK,
+ NAU8360_DSP_REPLY_MSG_INTEGRETY_ERR,
+ NAU8360_DSP_REPLY_EXECUTION_ERR,
+ NAU8360_DSP_REPLY_COMMAND_DOESNT_EXISTS_ERR,
+ NAU8360_DSP_REPLY_UNKNOWN_ERR,
+ NAU8360_DSP_REPLY_MSG_TOO_LONG,
+};
+
+enum {
+ NAU8360_DSP_CMD_GET_COUNTER = 0x1,
+ NAU8360_DSP_CMD_GET_FRAME_STATUS = 0x9,
+ NAU8360_DSP_CMD_GET_REVISION = 0xa,
+ NAU8360_DSP_CMD_GET_KCS_RSLTS = 0x4,
+ NAU8360_DSP_CMD_GET_KCS_SETUP = 0x6,
+ NAU8360_DSP_CMD_SET_KCS_SETUP = 0x7,
+ NAU8360_DSP_CMD_CLK_STOP = 0xb,
+ NAU8360_DSP_CMD_CLK_RESTART = 0xc,
+};
+
+/**
+ * struct nau8360_cmd_info - DSP command information structure
+ * @cmd_id: The command identification number
+ * @msg_param: Message parameters including offset, size, and data
+ * @setup_data: Setup data for write-only operations
+ * @reply_data: Reply data received from the DSP
+ */
+struct nau8360_cmd_info {
+ int cmd_id;
+ bool msg_param;
+ bool setup_data;
+ bool reply_data;
+};
+
+/**
+ * struct nau8360_kcs_setup - KCS setup configuration structure
+ * @set_len: Length of data written in bytes
+ * @set_kcs_offset: Address offset relative to the start of KCS
+ * @set_kcs_data: Pointer to the data written to KCS
+ * @get_len: Length of data from the reply in bytes
+ * @get_data: Pointer to the buffer for reply data
+ */
+struct nau8360_kcs_setup {
+ int set_len;
+ int set_kcs_offset;
+ void *set_kcs_data;
+ int get_len;
+ void *get_data;
+};
+
+/**
+ * struct nau8360_dsp_ctx - DSP context structure for firmware loading
+ * @cp: Pointer to the ALSA SoC component structure
+ * @dsp_addr: Register address of the DSP core (Left or Right channel)
+ */
+struct nau8360_dsp_ctx {
+ struct snd_soc_component *cp;
+ int dsp_addr;
+};
+
+int nau8360_dsp_init(struct snd_soc_component *component);
+int nau8360_dsp_setup_controls(struct snd_soc_component *component);
+
+#endif /* __NAU8360_DSP_H__ */
diff --git a/sound/soc/codecs/nau8360.c b/sound/soc/codecs/nau8360.c
new file mode 100644
index 000000000000..f0942739d7ab
--- /dev/null
+++ b/sound/soc/codecs/nau8360.c
@@ -0,0 +1,2309 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+//
+// Copyright (C) 2026 Nuvoton Technology Corp.
+// Author: David Lin <ctlin0@nuvoton.com>
+// Seven Lee <wtli@nuvoton.com>
+// John Hsu <kchsu0@nuvoton.com>
+// Neo Chang <ylchang2@nuvoton.com>
+
+
+#include <linux/unaligned.h>
+#include <linux/acpi.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/units.h>
+#include <linux/workqueue.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "nau8360-dsp.h"
+#include "nau8360.h"
+
+/* range of Master Clock MCLK (Hz) */
+#define MASTER_CLK_MIN 11025000
+#define MASTER_CLK_MAX 24576000
+/* DSP Optimal Clock Range 120MHz~126M(Hz) */
+#define DSP_OP_CLK48 122880000
+#define DSP_OP_CLK44 112896000
+/* the maximum frequency of DAC and IV sense clock */
+#define CLK_DA_IVSNS_MAX 6144000
+#define ADSP_SR_48000 48000
+#define ADSP_SR_44100 44100
+
+static const int ivsns_clk_div[] = { 1, 2, 4, 5, 8, 10 };
+
+static const int dac_clk_div[] = { 1, 2, 4, 8 };
+
+static const char * const nau8360_rx_func_names[] = {
+ [NAU8360_TDM_DACL] = "DAC_L",
+ [NAU8360_TDM_DACR] = "DAC_R",
+ [NAU8360_TDM_ANCL] = "ANC_L",
+ [NAU8360_TDM_ANCR] = "ANC_R",
+};
+
+static const char * const nau8360_tx_func_names[] = {
+ [NAU8360_TDM_AECL] = "AEC_L",
+ [NAU8360_TDM_AECR] = "AEC_R",
+ [NAU8360_TDM_ISNSL] = "ISNS_L",
+ [NAU8360_TDM_ISNSR] = "ISNS_R",
+ [NAU8360_TDM_VSNSL] = "VSNS_L",
+ [NAU8360_TDM_VSNSR] = "VSNS_R",
+ [NAU8360_TDM_TJ] = "TJ",
+ [NAU8360_TDM_VBAT] = "VBAT",
+};
+
+/* PLL threshold */
+#define PLL_FREQ_MIN 1000000
+#define PLL_FREQ_MAX 32000000
+#define PLL_FOUT_MIN 12500000
+#define PLL_FOUT_MAX 125000000
+#define PLL_FREF_MAX 8000000
+#define PLL_FVCO_MIN 50000000
+#define MSEL_MAX 32
+#define RSEL_MAX 4
+
+static const struct reg_default nau8360_reg_defaults[] = {
+ { NAU8360_R02_I2C_ADDR, 0x0000 },
+ { NAU8360_R03_CLK_CTRL0, 0x0000 },
+ { NAU8360_R04_CLK_CTRL1, 0x0000 },
+ { NAU8360_R05_INTERRUPT_CTRL, 0x40ff },
+ { NAU8360_R07_GP_CTRL, 0xaa30 },
+ { NAU8360_R08_GP_CTRL0, 0x1e1e },
+ { NAU8360_R09_GP_CTRL1, 0x1e1e },
+ { NAU8360_R0A_GP_CTRL2, 0x0000 },
+ { NAU8360_R0B_I2S_PCM_CTRL1, 0x0702 },
+ { NAU8360_R0C_I2S_PCM_CTRL2, 0x0a10 },
+ { NAU8360_R0D_I2S_PCM_CTRL3, 0x1300 },
+ { NAU8360_R0E_I2S_DATA_CTRL1, 0x0304 },
+ { NAU8360_R0F_I2S_DATA_CTRL2, 0x080b },
+ { NAU8360_R10_I2S_DATA_CTRL3, 0x0014 },
+ { NAU8360_R11_I2S_DATA_CTRL4, 0x0014 },
+ { NAU8360_R12_PATH_CTRL, 0x0400 },
+ { NAU8360_R17_I2S0_DATA_CTRL5, 0x0014 },
+ { NAU8360_R1A_DSP_CORE_CTRL2, 0x0010 },
+ { NAU8360_R2C_ALC_CTRL1, 0x2000 },
+ { NAU8360_R2D_ALC_CTRL2, 0x8400 },
+ { NAU8360_R2E_ALC_CTRL3, 0x2083 },
+ { NAU8360_R31_UVLOP_CTRL1, 0x0000 },
+ { NAU8360_R32_UVLOP_CTRL2, 0x8400 },
+ { NAU8360_R33_UVLOP_CTRL3, 0x0000 },
+ { NAU8360_R40_CLK_DET_CTRL, 0xca60 },
+ { NAU8360_R41_CLK_CTL2, 0xc400 },
+ { NAU8360_R5D_SINC_CFG, 0x0010 },
+ { NAU8360_R5F_ANA_TRIM_CFG1, 0x8400 },
+ { NAU8360_R60_RST, 0x0010 },
+ { NAU8360_R67_ANALOG_CONTROL_0, 0x0160 },
+ { NAU8360_R68_ANALOG_CONTROL_1, 0x00d4 },
+ { NAU8360_R6A_SARADC_CFG0, 0x5c09 },
+ { NAU8360_R6B_SARADC_CFG1, 0x0008 },
+ { NAU8360_R6C_IVSNS_CFG0, 0xf040 },
+ { NAU8360_R6D_IVSNS_CFG1, 0x3555 },
+ { NAU8360_R6E_DAC_CFG0, 0x0ed5 },
+ { NAU8360_R71_CLK_DIV_CFG, 0x0211 },
+ { NAU8360_R72_PLL_CFG0, 0xccc4 },
+ { NAU8360_R73_PLL_CFG1, 0x0101 },
+ { NAU8360_R74_PLL_CFG2, 0x0000 },
+ { NAU8360_R7A_DAC_TRIM_CFG2, 0x0000 },
+ { NAU8360_R7B_IVSNS_TRIM_CFG, 0x0000 },
+ { NAU8360_R7C_MISC_TRIM_CFG, 0x72d5 },
+ { NAU8360_R86_HW3_CTL0, 0x2000 },
+ { NAU8360_R88_ALC_CTRL6, 0x0000 },
+ { NAU8360_R8A_HW3_VL_CTL7, 0xc000 },
+ { NAU8360_R8B_HW3_VR_CTL8, 0xc000 },
+ { NAU8360_R8C_HW3_CTL6, 0x0000 },
+ { NAU8360_R8D_HW3_IL_CTL7, 0xc000 },
+ { NAU8360_R8E_HW3_IR_CTL8, 0xc000 },
+ { NAU8360_R8F_HW3_CTL9, 0x0000 },
+ { NAU8360_R90_HW2_CTL0, 0x2000 },
+ { NAU8360_R96_HW2_CTL6, 0x0000 },
+ { NAU8360_R97_HW2_CTL7, 0xc000 },
+ { NAU8360_R98_HW2_CTL8, 0xc000 },
+ { NAU8360_R99_HW2_CTL9, 0x0000 },
+ { NAU8360_R9A_HW1_CTL0, 0xc000 },
+ { NAU8360_R9B_HW1_CTL1, 0xc000 },
+ { NAU8360_R9C_HW1_CTL2, 0x0800 },
+ { NAU8360_R9D_PEQ_CTL, 0x0001 },
+ { NAU8360_RA0_LEFT_XODRC_CTRL, 0x0000 },
+ { NAU8360_RA2_RIGHT_XODRC_CTRL, 0x0000 },
+ { NAU8360_RA4_ANA_REG_0, 0x7f86 },
+ { NAU8360_RA5_ANA_REG_1, 0x276e },
+};
+
+static bool nau8360_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8360_R00_SOFTWARE_RST ... NAU8360_R12_PATH_CTRL:
+ case NAU8360_R17_I2S0_DATA_CTRL5:
+ case NAU8360_R1A_DSP_CORE_CTRL2:
+ case NAU8360_R21_VBAT_READOUT ... NAU8360_R22_TEMP_READOUT:
+ case NAU8360_R2C_ALC_CTRL1 ... NAU8360_R2E_ALC_CTRL3:
+ case NAU8360_R31_UVLOP_CTRL1 ... NAU8360_R33_UVLOP_CTRL3:
+ case NAU8360_R40_CLK_DET_CTRL ... NAU8360_R41_CLK_CTL2:
+ case NAU8360_R46_I2C_DEVICE_ID:
+ case NAU8360_R5D_SINC_CFG:
+ case NAU8360_R5F_ANA_TRIM_CFG1 ... NAU8360_R60_RST:
+ case NAU8360_R67_ANALOG_CONTROL_0 ... NAU8360_R6E_DAC_CFG0:
+ case NAU8360_R71_CLK_DIV_CFG ... NAU8360_R74_PLL_CFG2:
+ case NAU8360_R77_SOFT_SD ... NAU8360_R7C_MISC_TRIM_CFG:
+ case NAU8360_R7E_CLK_GATED_EN:
+ case NAU8360_R86_HW3_CTL0:
+ case NAU8360_R88_ALC_CTRL6:
+ case NAU8360_R8A_HW3_VL_CTL7 ... NAU8360_R90_HW2_CTL0:
+ case NAU8360_R96_HW2_CTL6 ... NAU8360_R9D_PEQ_CTL:
+ case NAU8360_RA0_LEFT_XODRC_CTRL:
+ case NAU8360_RA2_RIGHT_XODRC_CTRL:
+ case NAU8360_RA4_ANA_REG_0 ... NAU8360_RA5_ANA_REG_1:
+ case NAU8360_R100_LEFT_BIQ0_COE ... (NAU8360_R100_LEFT_BIQ0_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R10C_LEFT_BIQ1_COE ... (NAU8360_R10C_LEFT_BIQ1_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R118_LEFT_BIQ2_COE ... (NAU8360_R118_LEFT_BIQ2_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R124_LEFT_BIQ3_COE ... (NAU8360_R124_LEFT_BIQ3_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R130_LEFT_BIQ4_COE ... (NAU8360_R130_LEFT_BIQ4_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R13C_LEFT_BIQ5_COE ... (NAU8360_R13C_LEFT_BIQ5_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R148_LEFT_BIQ6_COE ... (NAU8360_R148_LEFT_BIQ6_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R154_LEFT_BIQ7_COE ... (NAU8360_R154_LEFT_BIQ7_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R160_LEFT_BIQ8_COE ... (NAU8360_R160_LEFT_BIQ8_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R16C_LEFT_BIQ9_COE ... (NAU8360_R16C_LEFT_BIQ9_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R178_LEFT_BIQ10_COE ... (NAU8360_R178_LEFT_BIQ10_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R184_LEFT_BIQ11_COE ... (NAU8360_R184_LEFT_BIQ11_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R190_LEFT_BIQ12_COE ... (NAU8360_R190_LEFT_BIQ12_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R19C_LEFT_BIQ13_COE ... (NAU8360_R19C_LEFT_BIQ13_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R1A8_LEFT_BIQ14_COE ... (NAU8360_R1A8_LEFT_BIQ14_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R200_RIGHT_BIQ0_COE ... (NAU8360_R200_RIGHT_BIQ0_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R20C_RIGHT_BIQ1_COE ... (NAU8360_R20C_RIGHT_BIQ1_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R218_RIGHT_BIQ2_COE ... (NAU8360_R218_RIGHT_BIQ2_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R224_RIGHT_BIQ3_COE ... (NAU8360_R224_RIGHT_BIQ3_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R230_RIGHT_BIQ4_COE ... (NAU8360_R230_RIGHT_BIQ4_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R23C_RIGHT_BIQ5_COE ... (NAU8360_R23C_RIGHT_BIQ5_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R248_RIGHT_BIQ6_COE ... (NAU8360_R248_RIGHT_BIQ6_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R254_RIGHT_BIQ7_COE ... (NAU8360_R254_RIGHT_BIQ7_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R260_RIGHT_BIQ8_COE ... (NAU8360_R260_RIGHT_BIQ8_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R26C_RIGHT_BIQ9_COE ... (NAU8360_R26C_RIGHT_BIQ9_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R278_RIGHT_BIQ10_COE ... (NAU8360_R278_RIGHT_BIQ10_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R284_RIGHT_BIQ11_COE ... (NAU8360_R284_RIGHT_BIQ11_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R290_RIGHT_BIQ12_COE ... (NAU8360_R290_RIGHT_BIQ12_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R29C_RIGHT_BIQ13_COE ... (NAU8360_R29C_RIGHT_BIQ13_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R2A8_RIGHT_BIQ14_COE ... (NAU8360_R2A8_RIGHT_BIQ14_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_RF000_DSP_COMM:
+ case NAU8360_RF002_DSP_COMM:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8360_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8360_R00_SOFTWARE_RST ... NAU8360_R12_PATH_CTRL:
+ case NAU8360_R17_I2S0_DATA_CTRL5:
+ case NAU8360_R1A_DSP_CORE_CTRL2:
+ case NAU8360_R2C_ALC_CTRL1 ... NAU8360_R2E_ALC_CTRL3:
+ case NAU8360_R31_UVLOP_CTRL1 ... NAU8360_R33_UVLOP_CTRL3:
+ case NAU8360_R40_CLK_DET_CTRL ... NAU8360_R41_CLK_CTL2:
+ case NAU8360_R5D_SINC_CFG:
+ case NAU8360_R5F_ANA_TRIM_CFG1:
+ case NAU8360_R60_RST:
+ case NAU8360_R67_ANALOG_CONTROL_0 ... NAU8360_R68_ANALOG_CONTROL_1:
+ case NAU8360_R6A_SARADC_CFG0 ... NAU8360_R6E_DAC_CFG0:
+ case NAU8360_R71_CLK_DIV_CFG ... NAU8360_R74_PLL_CFG2:
+ case NAU8360_R77_SOFT_SD ... NAU8360_R7C_MISC_TRIM_CFG:
+ case NAU8360_R7E_CLK_GATED_EN:
+ case NAU8360_R86_HW3_CTL0:
+ case NAU8360_R88_ALC_CTRL6:
+ case NAU8360_R8A_HW3_VL_CTL7 ... NAU8360_R90_HW2_CTL0:
+ case NAU8360_R96_HW2_CTL6 ... NAU8360_R9D_PEQ_CTL:
+ case NAU8360_RA4_ANA_REG_0 ... NAU8360_RA5_ANA_REG_1:
+ case NAU8360_R100_LEFT_BIQ0_COE ... (NAU8360_R100_LEFT_BIQ0_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R10C_LEFT_BIQ1_COE ... (NAU8360_R10C_LEFT_BIQ1_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R118_LEFT_BIQ2_COE ... (NAU8360_R118_LEFT_BIQ2_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R124_LEFT_BIQ3_COE ... (NAU8360_R124_LEFT_BIQ3_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R130_LEFT_BIQ4_COE ... (NAU8360_R130_LEFT_BIQ4_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R13C_LEFT_BIQ5_COE ... (NAU8360_R13C_LEFT_BIQ5_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R148_LEFT_BIQ6_COE ... (NAU8360_R148_LEFT_BIQ6_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R154_LEFT_BIQ7_COE ... (NAU8360_R154_LEFT_BIQ7_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R160_LEFT_BIQ8_COE ... (NAU8360_R160_LEFT_BIQ8_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R16C_LEFT_BIQ9_COE ... (NAU8360_R16C_LEFT_BIQ9_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R178_LEFT_BIQ10_COE ... (NAU8360_R178_LEFT_BIQ10_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R184_LEFT_BIQ11_COE ... (NAU8360_R184_LEFT_BIQ11_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R190_LEFT_BIQ12_COE ... (NAU8360_R190_LEFT_BIQ12_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R19C_LEFT_BIQ13_COE ... (NAU8360_R19C_LEFT_BIQ13_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R1A8_LEFT_BIQ14_COE ... (NAU8360_R1A8_LEFT_BIQ14_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R200_RIGHT_BIQ0_COE ... (NAU8360_R200_RIGHT_BIQ0_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R20C_RIGHT_BIQ1_COE ... (NAU8360_R20C_RIGHT_BIQ1_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R218_RIGHT_BIQ2_COE ... (NAU8360_R218_RIGHT_BIQ2_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R224_RIGHT_BIQ3_COE ... (NAU8360_R224_RIGHT_BIQ3_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R230_RIGHT_BIQ4_COE ... (NAU8360_R230_RIGHT_BIQ4_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R23C_RIGHT_BIQ5_COE ... (NAU8360_R23C_RIGHT_BIQ5_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R248_RIGHT_BIQ6_COE ... (NAU8360_R248_RIGHT_BIQ6_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R254_RIGHT_BIQ7_COE ... (NAU8360_R254_RIGHT_BIQ7_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R260_RIGHT_BIQ8_COE ... (NAU8360_R260_RIGHT_BIQ8_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R26C_RIGHT_BIQ9_COE ... (NAU8360_R26C_RIGHT_BIQ9_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R278_RIGHT_BIQ10_COE ... (NAU8360_R278_RIGHT_BIQ10_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R284_RIGHT_BIQ11_COE ... (NAU8360_R284_RIGHT_BIQ11_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R290_RIGHT_BIQ12_COE ... (NAU8360_R290_RIGHT_BIQ12_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R29C_RIGHT_BIQ13_COE ... (NAU8360_R29C_RIGHT_BIQ13_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_R2A8_RIGHT_BIQ14_COE ... (NAU8360_R2A8_RIGHT_BIQ14_COE +
+ NAU8360_PEQ_REG_WIDTH):
+ case NAU8360_RF000_DSP_COMM:
+ case NAU8360_RF002_DSP_COMM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8360_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8360_R00_SOFTWARE_RST ... NAU8360_R02_I2C_ADDR:
+ case NAU8360_R06_INT_CLR_STATUS:
+ case NAU8360_R21_VBAT_READOUT ... NAU8360_R22_TEMP_READOUT:
+ case NAU8360_R41_CLK_CTL2:
+ case NAU8360_R46_I2C_DEVICE_ID:
+ case NAU8360_R69_ANALOG_CONTROL_3:
+ case NAU8360_R77_SOFT_SD ... NAU8360_R79_EN_HIRC48M:
+ case NAU8360_R7E_CLK_GATED_EN:
+ case NAU8360_R9D_PEQ_CTL:
+ case NAU8360_RF000_DSP_COMM:
+ case NAU8360_RF002_DSP_COMM:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int nau8360_get_tdm_chan_len(struct nau8360 *nau8360)
+{
+ int val = 0;
+
+ regmap_read(nau8360->regmap, NAU8360_R0C_I2S_PCM_CTRL2, &val);
+ val = (val & NAU8360_TDM_CLEN_MASK) >> NAU8360_TDM_CLEN_SFT;
+
+ return (val << 3) + 16;
+}
+
+static inline bool nau8360_dsp_active(struct snd_soc_component *comp)
+{
+ return (snd_soc_component_read(comp, NAU8360_R12_PATH_CTRL) &
+ NAU8360_DAC_SEL_DSP);
+}
+
+static int nau8360_anc_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ int ret, value = NAU8360_PEQ_BAND_8;
+
+ mutex_lock(&nau8360->lock);
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ /* update anc flag if return value 1 and register value changed */
+ if (ret != 1)
+ goto unlock;
+
+ nau8360->anc_enable = ucontrol->value.integer.value[0];
+ if (nau8360_dsp_active(cp))
+ value = nau8360->anc_enable ? NAU8360_PEQ_BAND_15 : NAU8360_PEQ_BAND_12;
+ regmap_update_bits(nau8360->regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_BAND_MASK,
+ value << NAU8360_PEQ_BAND_SFT);
+
+unlock:
+ mutex_unlock(&nau8360->lock);
+
+ return ret;
+}
+
+static inline void nau8360_peq_mem_enable(struct regmap *regmap, bool enable)
+{
+ regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL,
+ NAU8360_HW1_MEM_TEST, enable ? NAU8360_HW1_MEM_TEST : 0);
+}
+
+static inline int nau8360_peq_regaddr(const char *id_name)
+{
+ int reg, band_num, dsp_addr = NAU8360_DSP_ADDR_BYNAME(id_name);
+ char *band = strstr(id_name, "BIQ");
+
+ if (!band || kstrtoint((band + 3), 10, &band_num))
+ return -EINVAL;
+ reg = dsp_addr == NAU8360_RF000_DSP_COMM ? NAU8360_R100_LEFT_BIQ0_COE :
+ NAU8360_R200_RIGHT_BIQ0_COE;
+ reg += band_num * NAU8360_TOT_BAND_COE_RANGE;
+
+ return reg;
+}
+
+static int nau8360_peq_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(cp);
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ int i, value, reg, ret = 0;
+ __be16 *val = (__be16 *)ucontrol->value.bytes.data;
+
+ /* Use the DAPM lock to prevent race conditions during DAPM power-up
+ * state transitions, and check component active status to prohibit
+ * PEQ access during active audio streams (playback and capture).
+ */
+ snd_soc_dapm_mutex_lock(dapm);
+ if (snd_soc_component_active(cp)) {
+ dev_dbg(nau8360->dev,
+ "PEQ coefficient access is ignored during audio is active");
+ ret = -EBUSY;
+ goto unlock_dapm;
+ }
+
+ reg = nau8360_peq_regaddr(kcontrol->id.name);
+ if (reg < 0) {
+ ret = reg;
+ goto unlock_dapm;
+ }
+
+ mutex_lock(&nau8360->lock);
+ nau8360_peq_mem_enable(nau8360->regmap, true);
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ value = snd_soc_component_read(cp, reg + i);
+ *(val + i) = cpu_to_be16(value);
+ }
+ nau8360_peq_mem_enable(nau8360->regmap, false);
+ mutex_unlock(&nau8360->lock);
+
+unlock_dapm:
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int nau8360_peq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *cp = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(cp);
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ int i, reg, ret = 0;
+ __be16 *data = NULL;
+ bool changed = false;
+
+ /* Use the DAPM lock to prevent race conditions during DAPM power-up
+ * state transitions, and check component active status to prohibit
+ * PEQ access during active audio streams (playback and capture).
+ */
+ snd_soc_dapm_mutex_lock(dapm);
+ if (snd_soc_component_active(cp)) {
+ dev_dbg(nau8360->dev,
+ "PEQ coefficient access is ignored during audio is active");
+ ret = -EBUSY;
+ goto unlock_dapm;
+ }
+
+ reg = nau8360_peq_regaddr(kcontrol->id.name);
+ if (reg < 0) {
+ ret = reg;
+ goto unlock_dapm;
+ }
+
+ data = kmemdup(ucontrol->value.bytes.data, params->max, GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto unlock_dapm;
+ }
+
+ mutex_lock(&nau8360->lock);
+ nau8360_peq_mem_enable(nau8360->regmap, true);
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ if (snd_soc_component_read(cp, reg + i) != be16_to_cpu(*(data + i))) {
+ snd_soc_component_write(cp, reg + i, be16_to_cpu(*(data + i)));
+ changed = true;
+ }
+ }
+ nau8360_peq_mem_enable(nau8360->regmap, false);
+ mutex_unlock(&nau8360->lock);
+
+ ret = changed ? 1 : 0;
+
+unlock_dapm:
+ snd_soc_dapm_mutex_unlock(dapm);
+ kfree(data);
+
+ return ret;
+}
+
+#define NAU8360_PEQ_COEF_BYTES_EXT(ch, band) \
+ SND_SOC_BYTES_EXT(ch " PEQ Coefficients " band, 20, nau8360_peq_coeff_get, \
+ nau8360_peq_coeff_put)
+
+static const struct snd_kcontrol_new nau8360_snd_controls[] = {
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ0"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ1"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ2"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ3"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ4"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ5"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ6"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ7"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ8"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ9"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ10"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ11"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ12"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ13"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Left", "BIQ14"),
+
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ0"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ1"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ2"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ3"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ4"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ5"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ6"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ7"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ8"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ9"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ10"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ11"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ12"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ13"),
+ NAU8360_PEQ_COEF_BYTES_EXT("Right", "BIQ14"),
+ SOC_SINGLE("Low Latency Switch", NAU8360_R96_HW2_CTL6,
+ NAU8360_HW2_LATENCY_SFT, 1, 0),
+ SOC_SINGLE_EXT("ANC Path Switch", NAU8360_R96_HW2_CTL6, NAU8360_HW1_ANC_EN_SFT,
+ 1, 0, snd_soc_get_volsw, nau8360_anc_put),
+};
+
+static int nau8360_adci_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);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+ NAU8360_PD_ISNS_R_PMD | NAU8360_PD_ISNS_L_PMD, 0);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+ NAU8360_PD_ISNS_R_PMD | NAU8360_PD_ISNS_L_PMD,
+ NAU8360_PD_ISNS_R_PMD | NAU8360_PD_ISNS_L_PMD);
+
+ return 0;
+}
+
+static int nau8360_adcv_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);
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+ NAU8360_PD_VSNS_R_PMD | NAU8360_PD_VSNS_L_PMD, 0);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(component, NAU8360_R6C_IVSNS_CFG0,
+ NAU8360_PD_VSNS_R_PMD | NAU8360_PD_VSNS_L_PMD,
+ NAU8360_PD_VSNS_R_PMD | NAU8360_PD_VSNS_L_PMD);
+
+ return 0;
+}
+
+static int nau8360_aif_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);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+ NAU8360_HV_EN, 0);
+
+ return 0;
+}
+
+static int nau8360_hw1_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);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+ NAU8360_ANA_MUTE, NAU8360_ANA_MUTE);
+
+ return 0;
+}
+
+static int nau8360_hw1_mux_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);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, NAU8360_R68_ANALOG_CONTROL_1,
+ NAU8360_DRVCTL_SEGL_FULL | NAU8360_DRVCTL_SEGR_FULL, 0);
+ snd_soc_component_update_bits(component, NAU8360_RA5_ANA_REG_1,
+ NAU8360_CLASSD_SHT_IN | NAU8360_HVEN_SYNC_SAW,
+ NAU8360_CLASSD_SHT_IN | NAU8360_HVEN_SYNC_SAW);
+ msleep(20);
+ }
+
+ return 0;
+}
+
+static int nau8360_hw2_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);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ snd_soc_component_update_bits(component, NAU8360_R99_HW2_CTL9,
+ NAU8360_HW2_CH_MUTE, 0);
+ snd_soc_component_update_bits(component, NAU8360_R9C_HW1_CTL2,
+ NAU8360_HW1_CH_MUTE, 0);
+ }
+
+ return 0;
+}
+
+static int nau8360_dac_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *cp = snd_soc_dapm_to_component(w->dapm);
+ unsigned int mask = 1 << w->shift;
+
+ if (SND_SOC_DAPM_EVENT_ON(event))
+ snd_soc_component_update_bits(cp, NAU8360_R6E_DAC_CFG0, mask, 0);
+ else if (SND_SOC_DAPM_EVENT_OFF(event))
+ snd_soc_component_update_bits(cp, NAU8360_R6E_DAC_CFG0, mask, mask);
+
+ return 0;
+}
+
+static int nau8360_hv_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);
+
+ if (SND_SOC_DAPM_EVENT_ON(event)) {
+ /* enable Class D HV power after DAC power is stable */
+ snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+ NAU8360_HV_EN, NAU8360_HV_EN);
+ msleep(50);
+ /* Class D modulator input short setting for mute and de-pop purpose.
+ * Restore normal after initiation. Set segment driver as full driving
+ * strength.
+ */
+ snd_soc_component_update_bits(component, NAU8360_RA5_ANA_REG_1,
+ NAU8360_CLASSD_SHT_IN | NAU8360_HVEN_SYNC_SAW, 0);
+ snd_soc_component_update_bits(component, NAU8360_R68_ANALOG_CONTROL_1,
+ NAU8360_DRVCTL_SEGL_FULL | NAU8360_DRVCTL_SEGR_FULL,
+ NAU8360_DRVCTL_SEGL_FULL | NAU8360_DRVCTL_SEGR_FULL);
+
+ snd_soc_component_update_bits(component, NAU8360_R67_ANALOG_CONTROL_0,
+ NAU8360_ANA_MUTE, 0);
+ } else if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ snd_soc_component_update_bits(component, NAU8360_R9C_HW1_CTL2,
+ NAU8360_HW1_CH_MUTE, NAU8360_HW1_CH_MUTE);
+ snd_soc_component_update_bits(component, NAU8360_R99_HW2_CTL9,
+ NAU8360_HW2_CH_MUTE, NAU8360_HW2_CH_MUTE);
+ }
+
+ return 0;
+}
+
+static inline void nau8360_dsp_enable(struct regmap *regmap, bool enable)
+{
+ regmap_update_bits(regmap, NAU8360_R86_HW3_CTL0, NAU8360_HW3_STALL,
+ enable ? 0 : NAU8360_HW3_STALL);
+ regmap_update_bits(regmap, NAU8360_R1A_DSP_CORE_CTRL2, NAU8360_DSP_RUNSTALL,
+ enable ? 0 : NAU8360_DSP_RUNSTALL);
+}
+
+static void nau8360_dsp_switch(struct snd_soc_component *component, bool enable)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8360->regmap;
+ int value = NAU8360_PEQ_BAND_8;
+
+ mutex_lock(&nau8360->lock);
+
+ /* If DSP is enabled, unstall HW3 engine and DSP, loading DSP firmware,
+ * and configure PEQ after dsp reset.
+ */
+ if (enable) {
+ value = nau8360->anc_enable ? NAU8360_PEQ_BAND_15 : NAU8360_PEQ_BAND_12;
+ nau8360_dsp_enable(regmap, true);
+ } else {
+ dev_dbg(nau8360->dev, "Bypass DSP path");
+ nau8360_dsp_enable(regmap, false);
+ }
+ regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_BAND_MASK,
+ value << NAU8360_PEQ_BAND_SFT);
+
+ mutex_unlock(&nau8360->lock);
+}
+
+static int nau8360_dac_mux_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_to_dapm(kcontrol);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int *item = ucontrol->value.enumerated.item;
+ int ret = 0;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ if (snd_soc_dapm_get_bias_level(dapm) > SND_SOC_BIAS_STANDBY) {
+ dev_warn_ratelimited(nau8360->dev, "changing path is not allowed during playback");
+ snd_soc_dapm_mutex_unlock(dapm);
+ return -EBUSY;
+ }
+
+ if (item[0] == NAU8360_DAC_SRC_DSP && !nau8360->load_fw_done) {
+ dev_warn_ratelimited(nau8360->dev, "Cannot enable DSP: Firmware not ready or disabled\n");
+ snd_soc_dapm_mutex_unlock(dapm);
+ return -EBUSY;
+ }
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+ if (ret <= 0)
+ return ret;
+
+ nau8360_dsp_switch(component, snd_soc_enum_item_to_val(e, item[0]));
+
+ return ret;
+}
+
+/* Select HW1 output source (enables PEQ bypass) */
+static const char *const nau8360_hw1out_src[] = { "Audio", "PEQ" };
+
+static SOC_ENUM_SINGLE_DECL(nau8360_hw1out_enum, NAU8360_R12_PATH_CTRL,
+ NAU8360_SEL_HW1_SFT, nau8360_hw1out_src);
+
+static const struct snd_kcontrol_new nau8360_hw1out_mux =
+ SOC_DAPM_ENUM("HW1 Output Source", nau8360_hw1out_enum);
+
+/* Select DAC input source (enables DSP bypass) */
+static const char *const nau8360_dac_src[] = { "HW1", "DSP" };
+
+static SOC_ENUM_SINGLE_DECL(nau8360_dac_enum, NAU8360_R12_PATH_CTRL,
+ NAU8360_DAC_SEL_SFT, nau8360_dac_src);
+
+static const struct snd_kcontrol_new nau8360_dac_mux =
+ SOC_DAPM_ENUM_EXT("DAC Source", nau8360_dac_enum,
+ snd_soc_dapm_get_enum_double, nau8360_dac_mux_put_enum);
+
+static const struct snd_soc_dapm_widget nau8360_dapm_widgets[] = {
+ SND_SOC_DAPM_SIGGEN("Sense"),
+ SND_SOC_DAPM_ADC_E("ADC_I", NULL, SND_SOC_NOPM, 0, 0, nau8360_adci_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_ADC_E("ADC_V", NULL, SND_SOC_NOPM, 0, 0, nau8360_adcv_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA("DSP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HW3 Engine", NAU8360_R8C_HW3_CTL6, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN_E("AIFRX", "Playback", 0, SND_SOC_NOPM, 0, 0,
+ nau8360_aif_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_S("HW1 Engine", 0, NAU8360_R9D_PEQ_CTL, 0, 1,
+ nau8360_hw1_event, SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX_E("HW1 Mux", SND_SOC_NOPM, 0, 0, &nau8360_hw1out_mux,
+ nau8360_hw1_mux_event, SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MUX("DAC Mux", SND_SOC_NOPM, 0, 0, &nau8360_dac_mux),
+
+ SND_SOC_DAPM_PGA_S("HW2 Engine", 1, NAU8360_R96_HW2_CTL6, 5, 0,
+ nau8360_hw2_event, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("DAC Clock", NAU8360_R71_CLK_DIV_CFG, 9, 1, NULL, 0),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, SND_SOC_NOPM, NAU8360_PD_DACL_SFT, 0,
+ nau8360_dac_power_event, SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, SND_SOC_NOPM, NAU8360_PD_DACR_SFT, 0,
+ nau8360_dac_power_event, SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("ADACL", 2, SND_SOC_NOPM, NAU8360_PD_DACL_SFT, 0,
+ nau8360_dac_power_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("ADACR", 2, SND_SOC_NOPM, NAU8360_PD_DACR_SFT, 0,
+ nau8360_dac_power_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_S("Class D", 3, SND_SOC_NOPM, 0, 0,
+ nau8360_hv_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route nau8360_dapm_routes[] = {
+ { "ADC_I", NULL, "Sense" },
+ { "ADC_V", NULL, "Sense" },
+ { "HW3 Engine", NULL, "ADC_I" },
+ { "HW3 Engine", NULL, "ADC_V" },
+ { "Capture", NULL, "HW3 Engine" },
+
+ { "HW1 Engine", NULL, "AIFRX" },
+ { "HW1 Mux", "Audio", "AIFRX" },
+ { "HW1 Mux", "PEQ", "HW1 Engine" },
+
+ { "DSP", NULL, "HW1 Mux" },
+ { "DAC Mux", "HW1", "HW1 Mux" },
+ { "DAC Mux", "DSP", "DSP" },
+
+ { "HW2 Engine", NULL, "DAC Mux" },
+ { "DACL", NULL, "HW2 Engine" },
+ { "DACR", NULL, "HW2 Engine" },
+ { "DACL", NULL, "DAC Clock" },
+ { "DACR", NULL, "DAC Clock" },
+
+ { "ADACL", NULL, "DACL" },
+ { "ADACR", NULL, "DACR" },
+ { "Class D", NULL, "ADACL" },
+ { "Class D", NULL, "ADACR" },
+
+ { "OUTL", NULL, "Class D" },
+ { "OUTR", NULL, "Class D" },
+};
+
+static int nau8360_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ unsigned int i2s_mask = NAU8360_FRAME_START_MASK | NAU8360_RX_OFFSET_MASK;
+ unsigned int i2s_fmt = NAU8360_FRAME_START_H2L | NAU8360_RX_OFFSET_I2S;
+ int val = 0;
+
+ flush_work(&nau8360->load_fw_work);
+
+ if (nau8360_dsp_active(component) && !nau8360->load_fw_done) {
+ dev_warn(nau8360->dev, "DSP firmware is not ready yet!");
+ return -EBUSY;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8360->regmap, NAU8360_R0B_I2S_PCM_CTRL1, &val);
+ if ((val & i2s_mask) == i2s_fmt)
+ regmap_update_bits(nau8360->regmap, NAU8360_R0B_I2S_PCM_CTRL1,
+ NAU8360_EN_TDM_RX, NAU8360_EN_TDM_RX);
+
+ if (nau8360_dsp_active(component))
+ snd_soc_dapm_enable_pin(nau8360->dapm, "Sense");
+ }
+
+ return 0;
+}
+
+static void nau8360_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ unsigned int tdm_mask;
+
+ tdm_mask = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ NAU8360_EN_TDM_RX : NAU8360_EN_TDM_TX;
+ regmap_update_bits(nau8360->regmap, NAU8360_R0B_I2S_PCM_CTRL1,
+ tdm_mask, 0);
+ if (nau8360_dsp_active(component) &&
+ substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dapm_disable_pin(nau8360->dapm, "Sense");
+}
+
+static int nau8360_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 nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ unsigned int val_len, val_srate;
+ int dlen = params_width(params);
+
+ if (dlen > nau8360_get_tdm_chan_len(nau8360)) {
+ dev_err(nau8360->dev, "Invalid data length");
+ return -EINVAL;
+ }
+
+ switch (dlen) {
+ case 16:
+ val_len = NAU8360_TDM_DLEN_16;
+ break;
+ case 20:
+ val_len = NAU8360_TDM_DLEN_20;
+ break;
+ case 24:
+ val_len = NAU8360_TDM_DLEN_24;
+ break;
+ case 32:
+ val_len = NAU8360_TDM_DLEN_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 16000:
+ val_srate = NAU8360_SRATE_16000;
+ break;
+ case 32000:
+ val_srate = NAU8360_SRATE_32000;
+ break;
+ case 44100:
+ case 48000:
+ val_srate = NAU8360_SRATE_48000;
+ break;
+ case 88200:
+ case 96000:
+ val_srate = NAU8360_SRATE_96000;
+ break;
+ case 176400:
+ case 192000:
+ val_srate = NAU8360_SRATE_192000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8360->regmap, NAU8360_R0C_I2S_PCM_CTRL2,
+ NAU8360_TDM_DLEN_MASK, val_len);
+ regmap_update_bits(nau8360->regmap, NAU8360_R40_CLK_DET_CTRL,
+ NAU8360_SRATE_MASK, val_srate);
+
+ return 0;
+}
+
+static int nau8360_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ unsigned int ctrl_val, ctrl1_val;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl_val = NAU8360_FRAME_START_H2L | NAU8360_RX_OFFSET_I2S;
+ ctrl1_val = NAU8360_TX_OFFSET_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl_val = NAU8360_RX_OFFSET_LEFT | NAU8360_RX_LEFT_JUSTIFY;
+ ctrl1_val = NAU8360_TX_OFFSET_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl_val = NAU8360_RX_OFFSET_RIGHT | NAU8360_RX_RIGHT_JUSTIFY;
+ ctrl1_val = NAU8360_TX_OFFSET_RIGHT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl_val = NAU8360_RX_OFFSET_PCM_A;
+ ctrl1_val = NAU8360_TX_OFFSET_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl_val = NAU8360_RX_OFFSET_PCM_B;
+ ctrl1_val = NAU8360_TX_OFFSET_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_component_update_bits(component, NAU8360_R0B_I2S_PCM_CTRL1,
+ NAU8360_FRAME_START_MASK | NAU8360_RX_OFFSET_MASK |
+ NAU8360_RX_JUSTIFY_MASK, ctrl_val);
+ snd_soc_component_update_bits(component, NAU8360_R0D_I2S_PCM_CTRL3,
+ NAU8360_TX_OFFSET_MASK, ctrl1_val);
+ return 0;
+}
+
+static void nau8360_set_tdm_rx_slot(struct snd_soc_component *cp, int type, int slot)
+{
+ switch (type) {
+ case NAU8360_TDM_DACL:
+ snd_soc_component_update_bits(cp, NAU8360_R0C_I2S_PCM_CTRL2,
+ NAU8360_RX_DACL_MASK, slot);
+ break;
+
+ case NAU8360_TDM_DACR:
+ snd_soc_component_update_bits(cp, NAU8360_R0C_I2S_PCM_CTRL2,
+ NAU8360_RX_DACR_MASK, (slot << NAU8360_RX_DACR_SFT));
+ break;
+
+ case NAU8360_TDM_ANCL:
+ snd_soc_component_update_bits(cp, NAU8360_R10_I2S_DATA_CTRL3,
+ NAU8360_RX_ANC_L_MASK, (slot << NAU8360_RX_ANC_L_SFT));
+ break;
+
+ case NAU8360_TDM_ANCR:
+ snd_soc_component_update_bits(cp, NAU8360_R10_I2S_DATA_CTRL3,
+ NAU8360_RX_ANC_R_MASK, (slot << NAU8360_RX_ANC_R_SFT));
+ break;
+ }
+}
+
+static void nau8360_set_tdm_tx_slot(struct snd_soc_component *cp, int type, int slot)
+{
+ switch (type) {
+ case NAU8360_TDM_AECL:
+ snd_soc_component_update_bits(cp, NAU8360_R17_I2S0_DATA_CTRL5,
+ NAU8360_AEC_L_SLOT_MASK, slot << NAU8360_AEC_L_SLOT_SFT);
+ break;
+
+ case NAU8360_TDM_AECR:
+ snd_soc_component_update_bits(cp, NAU8360_R17_I2S0_DATA_CTRL5,
+ NAU8360_AEC_R_SLOT_MASK, slot);
+ break;
+
+ case NAU8360_TDM_ISNSL:
+ snd_soc_component_update_bits(cp, NAU8360_R0E_I2S_DATA_CTRL1,
+ NAU8360_ISNS_L_SLOT_MASK, slot << NAU8360_ISNS_L_SLOT_SFT);
+ break;
+
+ case NAU8360_TDM_ISNSR:
+ snd_soc_component_update_bits(cp, NAU8360_R11_I2S_DATA_CTRL4,
+ NAU8360_ISNS_R_SLOT_MASK, slot);
+ break;
+
+ case NAU8360_TDM_VSNSL:
+ snd_soc_component_update_bits(cp, NAU8360_R0D_I2S_PCM_CTRL3,
+ NAU8360_VSNS_L_SLOT_MASK, slot);
+ break;
+
+ case NAU8360_TDM_VSNSR:
+ snd_soc_component_update_bits(cp, NAU8360_R11_I2S_DATA_CTRL4,
+ NAU8360_VSNS_R_SLOT_MASK, slot << NAU8360_VSNS_R_SLOT_SFT);
+ break;
+
+ case NAU8360_TDM_TJ:
+ snd_soc_component_update_bits(cp, NAU8360_R10_I2S_DATA_CTRL3,
+ NAU8360_TEMP_SLOT_MASK, slot);
+ break;
+
+ case NAU8360_TDM_VBAT:
+ snd_soc_component_update_bits(cp, NAU8360_R0F_I2S_DATA_CTRL2,
+ NAU8360_VBAT_SLOT_MASK, slot << NAU8360_VBAT_SLOT_SFT);
+ break;
+ }
+}
+
+static void nau8360_set_tdm_tx_func(struct snd_soc_component *cp, int type, bool enable)
+{
+ switch (type) {
+ case NAU8360_TDM_AECL:
+ snd_soc_component_update_bits(cp, NAU8360_R17_I2S0_DATA_CTRL5,
+ NAU8360_AEC_L_EN, enable ? NAU8360_AEC_L_EN : 0);
+ break;
+ case NAU8360_TDM_AECR:
+ snd_soc_component_update_bits(cp, NAU8360_R17_I2S0_DATA_CTRL5,
+ NAU8360_AEC_R_EN, enable ? NAU8360_AEC_R_EN : 0);
+ break;
+ case NAU8360_TDM_ISNSL:
+ snd_soc_component_update_bits(cp, NAU8360_R0E_I2S_DATA_CTRL1,
+ NAU8360_ISNS_L_TX_EN, enable ? NAU8360_ISNS_L_TX_EN : 0);
+ break;
+ case NAU8360_TDM_ISNSR:
+ snd_soc_component_update_bits(cp, NAU8360_R11_I2S_DATA_CTRL4,
+ NAU8360_ISNS_R_TX_EN, enable ? NAU8360_ISNS_R_TX_EN : 0);
+ break;
+ case NAU8360_TDM_VSNSL:
+ snd_soc_component_update_bits(cp, NAU8360_R0D_I2S_PCM_CTRL3,
+ NAU8360_VSNS_L_TX_EN, enable ? NAU8360_VSNS_L_TX_EN : 0);
+ break;
+ case NAU8360_TDM_VSNSR:
+ snd_soc_component_update_bits(cp, NAU8360_R11_I2S_DATA_CTRL4,
+ NAU8360_VSNS_R_TX_EN, enable ? NAU8360_VSNS_R_TX_EN : 0);
+ break;
+ case NAU8360_TDM_TJ:
+ snd_soc_component_update_bits(cp, NAU8360_R10_I2S_DATA_CTRL3,
+ NAU8360_TEMP_TX_EN, enable ? NAU8360_TEMP_TX_EN : 0);
+ break;
+ case NAU8360_TDM_VBAT:
+ snd_soc_component_update_bits(cp, NAU8360_R0F_I2S_DATA_CTRL2,
+ NAU8360_VBAT_TX_EN, enable ? NAU8360_VBAT_TX_EN : 0);
+ break;
+ }
+}
+
+static int nau8360_validate_tdm_slots(struct device *dev, unsigned int mask,
+ const u32 *func_slots, const char * const *func_names,
+ int num_funcs, const char *dir,
+ unsigned int *slot_used)
+{
+ int i;
+ unsigned int func_slot;
+ *slot_used = 0;
+
+ if (!mask)
+ return 0;
+
+ for (i = 0; i < num_funcs; i++) {
+ func_slot = func_slots[i];
+ if (func_slot == TDM_SLOT_NONE) {
+ dev_warn(dev, "%s %s slot disabled",
+ dir, func_names[i]);
+ continue;
+ }
+
+ if (!(mask & BIT(func_slot))) {
+ dev_warn(dev, "%s %s mapped to slot %d, but disabled by mask!",
+ dir, func_names[i], func_slot);
+ continue;
+ }
+
+ if (*slot_used & BIT(func_slot)) {
+ dev_err(dev, "%s %s slot %d collision!",
+ dir, func_names[i], func_slot);
+ return -EINVAL;
+ }
+
+ *slot_used |= BIT(func_slot);
+ }
+
+ return 0;
+}
+
+static void nau8360_enable_tdm_channels(struct snd_soc_component *cp,
+ int rx_slot_used, int tx_slot_used)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ int i, slot;
+ unsigned int val = 0;
+ bool enable;
+
+ for (i = 0; i < NAU8360_TDM_TXN; i++) {
+ slot = nau8360->tdm_tx_func_slot[i];
+ enable = (slot != TDM_SLOT_NONE) && (tx_slot_used & BIT(slot));
+ nau8360_set_tdm_tx_func(cp, i, enable);
+ }
+
+ if (rx_slot_used)
+ val |= NAU8360_EN_TDM_RX;
+ if (tx_slot_used)
+ val |= NAU8360_EN_TDM_TX;
+
+ snd_soc_component_update_bits(cp, NAU8360_R0B_I2S_PCM_CTRL1,
+ NAU8360_EN_TDM_RX | NAU8360_EN_TDM_TX, val);
+}
+
+static void nau8360_tdm_apply(struct snd_soc_component *cp, int slot_width)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ int i, chan_tx;
+
+ for (i = 0; i < NAU8360_TDM_TXN; i++) {
+ if (nau8360->tdm_tx_func_slot[i] == TDM_SLOT_NONE)
+ continue;
+
+ /* compute the slot location in bytes according to slot/chan width */
+ chan_tx = (slot_width >> 3) * nau8360->tdm_tx_func_slot[i];
+ nau8360_set_tdm_tx_slot(cp, i, chan_tx);
+ }
+
+ regmap_update_bits(nau8360->regmap, NAU8360_R0C_I2S_PCM_CTRL2,
+ NAU8360_TDM_CLEN_MASK,
+ ((slot_width - 16) >> 3) << NAU8360_TDM_CLEN_SFT);
+}
+
+static int nau8360_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 *cp = dai->component;
+ struct device *dev = cp->dev;
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ unsigned int tx_slot_used = 0, rx_slot_used = 0;
+ int ret = 0;
+
+ if (!slots || !slot_width) {
+ nau8360_enable_tdm_channels(cp, 0, 0);
+ return ret;
+ }
+
+ if (slots > NAU8360_TDM_MAX_CHAN) {
+ dev_err(dev, "Invalid TDM slots: %d", slots);
+ return -EINVAL;
+ }
+
+ if (slot_width != 16 && slot_width != 24 && slot_width != 32) {
+ dev_err(dev, "Invalid TDM channel length: %d", slot_width);
+ return -EINVAL;
+ }
+
+ ret = nau8360_validate_tdm_slots(cp->dev, rx_mask,
+ nau8360->tdm_rx_func_slot,
+ nau8360_rx_func_names,
+ NAU8360_TDM_RXN, "RX",
+ &rx_slot_used);
+ if (ret < 0)
+ goto err;
+
+ ret = nau8360_validate_tdm_slots(cp->dev, tx_mask,
+ nau8360->tdm_tx_func_slot,
+ nau8360_tx_func_names,
+ NAU8360_TDM_TXN, "TX",
+ &tx_slot_used);
+ if (ret < 0)
+ goto err;
+
+ if (nau8360_get_tdm_chan_len(nau8360) != slot_width)
+ nau8360_tdm_apply(cp, slot_width);
+
+ nau8360_enable_tdm_channels(cp, rx_slot_used, tx_slot_used);
+
+ dev_dbg(dev, "TDM: tx_mask 0x%x, rx_mask 0x%x", tx_mask, rx_mask);
+err:
+ return ret;
+}
+
+static inline int dyn_clk_div(int div_max, int fin, int fout)
+{
+ int clk_div;
+
+ if (!fin || !fout)
+ return -EINVAL;
+
+ for (clk_div = 0; clk_div <= div_max; clk_div++)
+ if (fin / (clk_div + 1) <= fout)
+ break;
+ if (clk_div > div_max)
+ return -EINVAL;
+
+ return clk_div;
+}
+
+static inline int tab_clk_div(const int clk_div[], int num_div, int fin)
+{
+ int i;
+
+ if (!fin)
+ return -EINVAL;
+
+ for (i = 0; i < num_div; i++)
+ if ((fin / clk_div[i]) <= CLK_DA_IVSNS_MAX)
+ break;
+ if (i == num_div)
+ return -EINVAL;
+
+ return i;
+}
+
+static int nau8360_set_sysclk_output(struct nau8360 *nau8360, unsigned int freq)
+{
+ struct device *dev = nau8360->dev;
+ int ratio = 0, mclk_rate;
+
+ if (freq < MASTER_CLK_MIN || freq > MASTER_CLK_MAX) {
+ dev_err(dev, "system clock %d Hz exceed range", freq);
+ return -EINVAL;
+ }
+
+ if (freq % ADSP_SR_48000 == 0)
+ ratio = freq / ADSP_SR_48000;
+ else if (freq % ADSP_SR_44100 == 0)
+ ratio = freq / ADSP_SR_44100;
+
+ switch (ratio) {
+ case NAU8360_MCLK_FS_RATIO_250:
+ mclk_rate = NAU8360_MCLK_RATE_12000;
+ break;
+ case NAU8360_MCLK_FS_RATIO_256:
+ mclk_rate = NAU8360_MCLK_RATE_12288;
+ break;
+ case NAU8360_MCLK_FS_RATIO_400:
+ mclk_rate = NAU8360_MCLK_RATE_19200;
+ break;
+ case NAU8360_MCLK_FS_RATIO_500:
+ mclk_rate = NAU8360_MCLK_RATE_24000;
+ break;
+ case NAU8360_MCLK_FS_RATIO_512:
+ mclk_rate = NAU8360_MCLK_RATE_24576;
+ break;
+ default:
+ dev_err(dev, "invalid sysclk %d", freq);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "sysclk %d Hz, ratio %d", freq, ratio);
+
+ nau8360->sys_clk = freq;
+ regmap_update_bits(nau8360->regmap, NAU8360_R40_CLK_DET_CTRL,
+ NAU8360_MCLK_RATE_MASK, mclk_rate);
+
+ return 0;
+}
+
+static int nau8360_dig_sys_clk(struct nau8360 *nau8360, int source, unsigned int freq)
+{
+ struct device *dev = nau8360->dev;
+ struct regmap *regmap = nau8360->regmap;
+ int value, mclk_div;
+
+ switch (source) {
+ case NAU8360_CLK_SRC_MCLK:
+ value = NAU8360_MCLK_SEL_MCLK;
+ break;
+
+ case NAU8360_CLK_SRC_PLL:
+ value = NAU8360_MCLK_SEL_PLL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(regmap, NAU8360_R04_CLK_CTRL1, NAU8360_MCLK_SEL_MASK, value);
+
+ mclk_div = dyn_clk_div(NAU8360_MCLK_DIV_MAX, freq, nau8360->sys_clk);
+ if (mclk_div < 0) {
+ dev_err(dev, "mclk_div (%d -> %d): error", freq, nau8360->sys_clk);
+ return -EINVAL;
+ }
+ regmap_update_bits(regmap, NAU8360_R03_CLK_CTRL0, NAU8360_MCLK_DIV_MASK,
+ mclk_div << NAU8360_MCLK_DIV_SFT);
+
+ dev_dbg(dev, " mclk_div (%d -> %d): %d", freq, nau8360->sys_clk, mclk_div + 1);
+
+ return 0;
+}
+
+static int nau8360_ana_sys_clk(struct nau8360 *nau8360, int source, unsigned int freq)
+{
+ struct device *dev = nau8360->dev;
+ struct regmap *regmap = nau8360->regmap;
+ struct nau8360_pll *pll = &nau8360->pll;
+ int value, pclk_div, ivdiv_sel, ddiv_sel;
+
+ switch (source) {
+ case NAU8360_CLK_SRC_PLL:
+ value = NAU8360_CLK_ANA_SEL_PLL;
+ break;
+ case NAU8360_CLK_SRC_MCLK:
+ value = NAU8360_CLK_ANA_SEL_MCLK;
+ break;
+ case NAU8360_CLK_SRC_BCLK:
+ value = NAU8360_CLK_ANA_SEL_BCLK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (source == NAU8360_CLK_SRC_PLL) {
+ freq = pll->output;
+ pclk_div = dyn_clk_div(NAU8360_PLLOUT_DIV_MAX, freq, nau8360->sys_clk);
+ if (pclk_div < 0) {
+ dev_err(dev, "pll_div (%d -> %d): error", freq, nau8360->sys_clk);
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, " pll_div (%d -> %d): %d", freq, nau8360->sys_clk, pclk_div + 1);
+
+ regmap_update_bits(regmap, NAU8360_R72_PLL_CFG0, NAU8360_PLLOUT_DIV_MASK,
+ pclk_div << NAU8360_PLLOUT_DIV_SFT);
+ freq /= (pclk_div + 1);
+ }
+
+ ivdiv_sel = tab_clk_div(ivsns_clk_div, ARRAY_SIZE(ivsns_clk_div), freq);
+ if (ivdiv_sel < 0) {
+ dev_err(dev, "iv_div (%d -> %d): error", freq, CLK_DA_IVSNS_MAX);
+ return -EINVAL;
+ }
+ value |= ivdiv_sel << NAU8360_IVSNS_CLK_DIV_SFT;
+
+ ddiv_sel = tab_clk_div(dac_clk_div, ARRAY_SIZE(dac_clk_div), freq);
+ if (ddiv_sel < 0) {
+ dev_err(dev, "dac_div (%d -> %d): error", freq, CLK_DA_IVSNS_MAX);
+ return -EINVAL;
+ }
+ value |= ddiv_sel << NAU8360_DAC_CLK_DIV_SFT;
+
+ dev_dbg(dev, " clk_div (%d -> %d): ivsns %d, dac %d", freq, CLK_DA_IVSNS_MAX,
+ ivsns_clk_div[ivdiv_sel], dac_clk_div[ddiv_sel]);
+
+ regmap_update_bits(regmap, NAU8360_R71_CLK_DIV_CFG, NAU8360_CLK_ANA_SEL_MASK |
+ NAU8360_IVSNS_CLK_DIV_MASK | NAU8360_DAC_CLK_DIV_MASK, value);
+
+ return 0;
+}
+
+static int nau8360_dsp_hw_clk(struct nau8360 *nau8360, int source, unsigned int freq)
+{
+ struct device *dev = nau8360->dev;
+ struct regmap *regmap = nau8360->regmap;
+ int val_dsp, val_hw, clk_div, dsp_clk;
+
+ if (freq % ADSP_SR_48000 == 0)
+ dsp_clk = DSP_OP_CLK48;
+ else if (freq % ADSP_SR_44100 == 0)
+ dsp_clk = DSP_OP_CLK44;
+ else
+ return -EINVAL;
+ clk_div = dyn_clk_div(NAU8360_DSP_CLK_DIV_MAX, freq, dsp_clk);
+ if (clk_div < 0) {
+ dev_err(dev, "dsp/hw clk_div (%d -> %d): error", freq, dsp_clk);
+ return -EINVAL;
+ }
+ val_dsp = clk_div << NAU8360_DSP_CLK_DIV_SFT;
+ val_hw = clk_div << NAU8360_HW_CLK_DIV_SFT;
+ if (source == NAU8360_CLK_SRC_MCLK) {
+ val_dsp |= NAU8360_DSP_CLK_SEL_MCLK;
+ val_hw |= NAU8360_HW_CLK_SEL_MCLK;
+ } else if (source == NAU8360_CLK_SRC_PLL) {
+ val_dsp |= NAU8360_DSP_CLK_SEL_PLL;
+ val_hw |= NAU8360_HW_CLK_SEL_PLL;
+ } else
+ return -EINVAL;
+
+ dev_dbg(dev, " dsp/hw clk_div (%d -> %d): %d", freq, dsp_clk, clk_div + 1);
+
+ regmap_update_bits(regmap, NAU8360_R03_CLK_CTRL0, NAU8360_DSP_CLK_SEL_MASK |
+ NAU8360_DSP_CLK_DIV_MASK, val_dsp);
+ regmap_update_bits(regmap, NAU8360_R04_CLK_CTRL1, NAU8360_HW_CLK_SEL_MASK |
+ NAU8360_HW_CLK_DIV_MASK, val_hw);
+
+ return 0;
+}
+
+static int nau8360_set_sysclk(struct snd_soc_component *cp,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ struct regmap *regmap = nau8360->regmap;
+ struct device *dev = nau8360->dev;
+ static const char * const idtab[] = { "DIG", "ANA", "Internal" };
+ static const char * const srctab[] = { "MCLK", "PLL", "HIRC48M", "BCLK" };
+ int ret;
+
+ if (clk_id < 0 || clk_id >= ARRAY_SIZE(idtab))
+ return -EINVAL;
+
+ if (source < 0 || source >= ARRAY_SIZE(srctab))
+ return -EINVAL;
+
+ if (dir == SND_SOC_CLOCK_OUT) {
+ dev_dbg(dev, "sysclk: freq %d (out)", freq);
+ return nau8360_set_sysclk_output(nau8360, freq);
+ }
+
+ switch (clk_id) {
+ case NAU8360_CLK_ID_INT:
+ source = NAU8360_CLK_SRC_ICLK;
+ dev_dbg(dev, "sysclk: id %d (%s), src %d (%s) (in)",
+ clk_id, idtab[clk_id], source, srctab[source]);
+ regmap_update_bits(regmap, NAU8360_R03_CLK_CTRL0,
+ NAU8360_DSP_CLK_SEL_MASK, NAU8360_DSP_CLK_SEL_HIRC48M);
+ regmap_update_bits(regmap, NAU8360_R04_CLK_CTRL1,
+ NAU8360_HW_CLK_SEL_MASK | NAU8360_MCLK_SEL_MASK,
+ NAU8360_HW_CLK_SEL_HIRC48M | NAU8360_MCLK_SEL_HIRC48M);
+ regmap_update_bits(regmap, NAU8360_R72_PLL_CFG0,
+ NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_DIS);
+ break;
+
+ case NAU8360_CLK_ID_DIG:
+ dev_dbg(dev, "sysclk: id %d (%s), src %d (%s), freq %d (in)",
+ clk_id, idtab[clk_id], source, srctab[source], freq);
+
+ if (source == NAU8360_CLK_SRC_BCLK)
+ return -EINVAL;
+
+ if (source == NAU8360_CLK_SRC_MCLK)
+ regmap_update_bits(regmap, NAU8360_R72_PLL_CFG0,
+ NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_DIS);
+
+ ret = nau8360_dig_sys_clk(nau8360, source, freq);
+ if (ret)
+ return -EINVAL;
+ ret = nau8360_dsp_hw_clk(nau8360, source, freq);
+ if (ret)
+ return -EINVAL;
+ break;
+
+ case NAU8360_CLK_ID_ANA:
+ dev_dbg(dev, "sysclk: id %d (%s), src %d (%s), freq %d (in)",
+ clk_id, idtab[clk_id], source, srctab[source], freq);
+
+ if (source == NAU8360_CLK_SRC_MCLK || source == NAU8360_CLK_SRC_BCLK)
+ regmap_update_bits(regmap, NAU8360_R72_PLL_CFG0,
+ NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_DIS);
+
+ ret = nau8360_ana_sys_clk(nau8360, source, freq);
+ if (ret)
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8360_calc_pll(struct nau8360 *nau8360)
+{
+ struct nau8360_pll *pll = &nau8360->pll;
+ u64 fref = 0ULL, fvco = 0ULL, ratio;
+ int fr, fv;
+
+ if (pll->src != NAU8360_PLL_INTERNAL &&
+ (pll->input > PLL_FREQ_MAX || pll->input < PLL_FREQ_MIN))
+ return -EINVAL;
+
+ if (pll->output > PLL_FOUT_MAX || pll->output < PLL_FOUT_MIN)
+ return -EINVAL;
+
+ for (pll->msel = 1; pll->msel <= MSEL_MAX; pll->msel++) {
+ fr = pll->input / pll->msel;
+ if (fr <= PLL_FREF_MAX) {
+ fref = fr;
+ break;
+ }
+ }
+ if (!fref)
+ return -ERANGE;
+
+ for (pll->rsel = 1; pll->rsel <= RSEL_MAX; pll->rsel++) {
+ fv = pll->output * pll->rsel;
+ if (fv >= PLL_FVCO_MIN) {
+ fvco = fv;
+ break;
+ }
+ }
+ if (!fvco)
+ return -ERANGE;
+
+ dev_dbg(nau8360->dev, "fref(1-8MHz): %lld, fvco(50-125MHz): %lld", fref, fvco);
+
+ /* Calculate the PLL 8-bit integer input and 12-bit fractional */
+ ratio = div_u64(fvco << 12, fref);
+ pll->nsel = (ratio >> 12) & 0xff;
+ pll->xsel = ratio & 0xfff;
+
+ return 0;
+}
+
+static int nau8360_set_pll(struct snd_soc_component *cp, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ struct device *dev = nau8360->dev;
+ struct nau8360_pll *pll = &nau8360->pll;
+ int ctrl_val, ret;
+
+ switch (source) {
+ case NAU8360_PLL_MCLK:
+ ctrl_val = NAU8360_PLL_CLK_SEL_MCLK;
+ break;
+ case NAU8360_PLL_BCLK:
+ ctrl_val = NAU8360_PLL_CLK_SEL_BCLK;
+ break;
+ case NAU8360_PLL_INTERNAL:
+ ctrl_val = NAU8360_PLL_CLK_SEL_HIRC;
+ break;
+ default:
+ return -EINVAL;
+ }
+ pll->src = source;
+ pll->input = freq_in;
+ pll->output = freq_out;
+ ret = nau8360_calc_pll(nau8360);
+ if (ret) {
+ dev_err(dev, "clock error input %d output %d", freq_in, freq_out);
+ return ret;
+ }
+ dev_dbg(dev, "src:%d, input:%d, output:%d, M:%d, R:%d, N:%d, X:%d", pll->src,
+ pll->input, pll->output, pll->msel, pll->rsel, pll->nsel, pll->xsel);
+
+ regmap_update_bits(nau8360->regmap, NAU8360_R72_PLL_CFG0, NAU8360_PD_PLL_MASK |
+ NAU8360_PLL_CLK_SEL_MASK, NAU8360_PD_PLL_EN | ctrl_val);
+ regmap_write_bits(nau8360->regmap, NAU8360_R73_PLL_CFG1,
+ NAU8360_RSEL_MASK | NAU8360_MSEL_MASK | NAU8360_NSEL_MASK,
+ (pll->rsel - 1) << NAU8360_RSEL_SFT |
+ (pll->msel - 1) << NAU8360_MSEL_SFT | (pll->nsel - 1));
+ regmap_write_bits(nau8360->regmap, NAU8360_R74_PLL_CFG2, NAU8360_XSEL_MASK,
+ pll->xsel);
+
+ return 0;
+}
+
+static void nau8360_coeff_set_def(struct nau8360 *nau8360)
+{
+ struct regmap *regmap = nau8360->regmap;
+ int i;
+
+ mutex_lock(&nau8360->lock);
+ regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_CLEAR,
+ NAU8360_HW1_MEM_CLEAR);
+ regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_HW1_MEM_CLEAR, 0);
+ nau8360_peq_mem_enable(regmap, true);
+ for (i = 0; i < NAU8360_TOT_BAND_PER_CH; i++) {
+ regmap_write(regmap, NAU8360_R100_LEFT_BIQ0_COE + 1 +
+ i * NAU8360_TOT_BAND_COE_RANGE, 0x20);
+ regmap_write(regmap, NAU8360_R200_RIGHT_BIQ0_COE + 1 +
+ i * NAU8360_TOT_BAND_COE_RANGE, 0x20);
+ }
+ nau8360_peq_mem_enable(regmap, false);
+ regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_BAND_MASK,
+ NAU8360_PEQ_BAND_8 << NAU8360_PEQ_BAND_SFT);
+ mutex_unlock(&nau8360->lock);
+}
+
+static inline int nau8360_vbat_level(struct regmap *regmap, int *vbat)
+{
+ struct device *dev = regmap_get_device(regmap);
+ int value = 0, ret;
+
+ ret = regmap_read(regmap, NAU8360_R21_VBAT_READOUT, &value);
+ if (ret)
+ return ret;
+
+ /* multiple 100 on value scale */
+ *vbat = (value * 100 + NAU8360_VBAT_BASE) / NAU8360_VBAT_STEP;
+ if (*vbat < NAU8360_VBAT_MIN || *vbat > NAU8360_VBAT_MAX) {
+ dev_err(dev, "VBAT %dV is out of valid range (%dV-%dV)",
+ *vbat, NAU8360_VBAT_MIN, NAU8360_VBAT_MAX);
+ return -ERANGE;
+ }
+
+ return 0;
+}
+
+static inline void nau8360_sawtooth_params(int vbat, int *vsaw_level, int *vsaw_slope)
+{
+ if (vbat < NAU8360_VBAT_MID_THRES) {
+ *vsaw_level = 0x1;
+ *vsaw_slope = 0x0;
+ } else if (vbat < NAU8360_VBAT_HIGH_THRES) {
+ *vsaw_level = 0x2;
+ *vsaw_slope = 0x1;
+ } else {
+ *vsaw_level = 0x3;
+ *vsaw_slope = 0x2;
+ }
+}
+
+static inline void nau8360_dsp_software_reset(struct snd_soc_component *component)
+{
+ /* Enable PLL for successful DSP reset. After DSP is alive,
+ * system clock switches to internal clock and disable PLL.
+ */
+ snd_soc_component_update_bits(component, NAU8360_R72_PLL_CFG0,
+ NAU8360_PD_PLL_MASK, NAU8360_PD_PLL_EN);
+ msleep(50);
+ snd_soc_component_write(component, NAU8360_R01_DSP_SOFTWARE_RST, 0x5a5a);
+ snd_soc_component_write(component, NAU8360_R01_DSP_SOFTWARE_RST, 0xa5a5);
+ snd_soc_component_set_sysclk(component, NAU8360_CLK_ID_INT, 0, 0,
+ SND_SOC_CLOCK_IN);
+}
+
+static void nau8360_dsp_bootup(struct snd_soc_component *component)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8360->regmap;
+
+ regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_STALL, 0);
+ nau8360_dsp_software_reset(component);
+ nau8360_dsp_enable(regmap, true);
+}
+
+static void nau8360_tdm_function_config(struct snd_soc_component *cp)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(cp);
+ int i, chan_tx, tdm_chan_len;
+
+ for (i = 0; i < NAU8360_TDM_RXN; i++) {
+ if (nau8360->tdm_rx_func_slot[i] == TDM_SLOT_NONE)
+ continue;
+
+ nau8360_set_tdm_rx_slot(cp, i, nau8360->tdm_rx_func_slot[i]);
+ }
+
+ tdm_chan_len = nau8360_get_tdm_chan_len(nau8360) >> 3;
+ for (i = 0; i < NAU8360_TDM_TXN; i++) {
+ if (nau8360->tdm_tx_func_slot[i] == TDM_SLOT_NONE)
+ continue;
+
+ /* compute the slot location in bytes according to slot/chan width */
+ chan_tx = (tdm_chan_len * nau8360->tdm_tx_func_slot[i]);
+ nau8360_set_tdm_tx_slot(cp, i, chan_tx);
+ }
+}
+
+static void nau8360_dsp_fw_load(struct nau8360 *nau8360)
+{
+ nau8360->load_fw_done = false;
+ schedule_work(&nau8360->load_fw_work);
+}
+
+static void nau8360_load_fw_work(struct work_struct *work)
+{
+ struct nau8360 *nau8360 = container_of(work, struct nau8360, load_fw_work);
+ struct snd_soc_component *cp = snd_soc_dapm_to_component(nau8360->dapm);
+ int ret;
+
+ ret = nau8360_dsp_init(cp);
+ if (ret) {
+ dev_err(nau8360->dev, "Failed to initialize DSP: %d\n", ret);
+ nau8360_dsp_enable(nau8360->regmap, false);
+ return;
+ }
+ nau8360->load_fw_done = true;
+}
+
+static int nau8360_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8360->regmap;
+ struct device *dev = nau8360->dev;
+ int ret, vbat, vsaw_level, vsaw_slope;
+
+ nau8360->dapm = dapm;
+ nau8360_dsp_bootup(component);
+ nau8360_dsp_fw_load(nau8360);
+ ret = nau8360_dsp_setup_controls(component);
+ if (ret) {
+ nau8360_dsp_enable(regmap, false);
+ dev_err(dev, "DSP setup controls failed(%d)", ret);
+ goto err;
+ }
+ nau8360_tdm_function_config(component);
+
+ /* default disable Sense signal after booting */
+ snd_soc_dapm_disable_pin(nau8360->dapm, "Sense");
+ snd_soc_dapm_sync(nau8360->dapm);
+
+ nau8360_coeff_set_def(nau8360);
+ /* VBAT is sensed by chip. */
+ ret = nau8360_vbat_level(regmap, &vbat);
+ if (ret) {
+ dev_err(dev, "Failed to get valid VBAT level: %d", ret);
+ goto err;
+ }
+ dev_dbg(dev, "VBAT %dV for nau8360", vbat);
+
+ /* Config sawtooth clock according to VBAT. Class D modulator input short setting
+ * for mute and de-pop purpose. Restore normal after initiation.
+ */
+ nau8360_sawtooth_params(vbat, &vsaw_level, &vsaw_slope);
+
+ regmap_update_bits(regmap, NAU8360_RA5_ANA_REG_1, NAU8360_VSAW_LV_MASK |
+ NAU8360_KVCO_SAW_MASK, (vsaw_level << NAU8360_VSAW_LV_SFT) |
+ (vsaw_slope << NAU8360_KVCO_SAW_SFT));
+
+ return 0;
+
+err:
+ cancel_work_sync(&nau8360->load_fw_work);
+ return ret;
+}
+
+static int __maybe_unused nau8360_suspend(struct snd_soc_component *component)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&nau8360->load_fw_work);
+
+ regmap_update_bits(nau8360->regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_STALL,
+ NAU8360_HW2_STALL);
+ nau8360_dsp_enable(nau8360->regmap, false);
+
+ regcache_cache_only(nau8360->regmap, true);
+ regcache_mark_dirty(nau8360->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8360_resume(struct snd_soc_component *component)
+{
+ struct nau8360 *nau8360 = snd_soc_component_get_drvdata(component);
+ struct regmap *regmap = nau8360->regmap;
+
+ regcache_cache_only(regmap, false);
+
+ nau8360_dsp_bootup(component);
+
+ nau8360_peq_mem_enable(regmap, true);
+ regcache_sync(regmap);
+ nau8360_peq_mem_enable(regmap, false);
+
+ /* disable Sense at standby */
+ snd_soc_dapm_disable_pin(nau8360->dapm, "Sense");
+ snd_soc_dapm_sync(nau8360->dapm);
+
+ nau8360_dsp_fw_load(nau8360);
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_comp_dev_nau8360 = {
+ .probe = nau8360_codec_probe,
+ .set_sysclk = nau8360_set_sysclk,
+ .set_pll = nau8360_set_pll,
+ .suspend = nau8360_suspend,
+ .resume = nau8360_resume,
+ .controls = nau8360_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8360_snd_controls),
+ .dapm_widgets = nau8360_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8360_dapm_widgets),
+ .dapm_routes = nau8360_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8360_dapm_routes),
+ .suspend_bias_off = 1,
+ .idle_bias_on = 1,
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static const struct snd_soc_dai_ops nau8360_dai_ops = {
+ .startup = nau8360_startup,
+ .shutdown = nau8360_shutdown,
+ .hw_params = nau8360_hw_params,
+ .set_fmt = nau8360_set_fmt,
+ .set_tdm_slot = nau8360_set_tdm_slot,
+};
+
+#define NAU8360_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \
+ SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000)
+
+#define NAU8360_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8360_dai = {
+ .name = NAU8360_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = NAU8360_RATES,
+ .formats = NAU8360_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = NAU8360_RATES,
+ .formats = NAU8360_FORMATS,
+ },
+ .ops = &nau8360_dai_ops,
+ .symmetric_rate = 1,
+};
+
+static int nau8360_reg_write(void *context, unsigned int reg, unsigned int value)
+{
+ struct i2c_client *client = context;
+ struct nau8360 *nau8360 = i2c_get_clientdata(client);
+ int ret, count = 0;
+
+ put_unaligned_be16(reg, &nau8360->i2c_write_buf[count]);
+ count += 2;
+
+ if (NAU8360_IS_DSP_REG(reg)) {
+ /* format for DSP, 4 bytes value and native */
+ put_unaligned_le32(value, &nau8360->i2c_write_buf[count]);
+ count += 4;
+ } else {
+ /* format for Codec, 2 bytes value and endian big */
+ put_unaligned_be16(value, &nau8360->i2c_write_buf[count]);
+ count += 2;
+ }
+
+ ret = i2c_master_send(client, nau8360->i2c_write_buf, count);
+
+ if (ret == count)
+ return 0;
+ if (ret < 0)
+ return ret;
+
+ return -EIO;
+}
+
+static int nau8360_reg_read(void *context, unsigned int reg, unsigned int *value)
+{
+ struct i2c_client *client = context;
+ struct nau8360 *nau8360 = i2c_get_clientdata(client);
+ struct i2c_msg xfer[2];
+ int ret;
+
+ put_unaligned_be16(reg, &nau8360->i2c_read_reg);
+ xfer[0].addr = client->addr;
+ xfer[0].len = 2;
+ xfer[0].buf = nau8360->i2c_read_reg;
+ xfer[0].flags = 0;
+
+ xfer[1].addr = client->addr;
+ xfer[1].len = (NAU8360_IS_DSP_REG(reg)) ? 4 : 2;
+ xfer[1].buf = nau8360->i2c_read_buf;
+ xfer[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, xfer, ARRAY_SIZE(xfer));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(xfer))
+ return -EIO;
+
+ if (NAU8360_IS_DSP_REG(reg))
+ *value = get_unaligned_le32(nau8360->i2c_read_buf);
+ else
+ *value = get_unaligned_be16(nau8360->i2c_read_buf);
+
+ return 0;
+}
+
+static const struct regmap_config nau8360_regmap_config = {
+ .reg_bits = NAU8360_REG_ADDR_LEN,
+ .val_bits = NAU8360_REG_DATA_LEN,
+
+ .max_register = NAU8360_REG_MAX,
+ .readable_reg = nau8360_readable_reg,
+ .writeable_reg = nau8360_writeable_reg,
+ .volatile_reg = nau8360_volatile_reg,
+ .reg_read = nau8360_reg_read,
+ .reg_write = nau8360_reg_write,
+
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = nau8360_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8360_reg_defaults),
+};
+
+static inline void nau8360_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8360_R00_SOFTWARE_RST, 0x5a5a);
+ regmap_write(regmap, NAU8360_R00_SOFTWARE_RST, 0xa5a5);
+}
+
+static void nau8360_init_regs(struct nau8360 *nau8360)
+{
+ struct regmap *regmap = nau8360->regmap;
+
+ /* Enable Digital LDO */
+ regmap_write(regmap, NAU8360_R78_PD_SW_DLDO, NAU8360_PD_SW_DLDO_EN);
+ /* Enable Software Shutdown Mode */
+ regmap_write(regmap, NAU8360_R77_SOFT_SD, NAU8360_SOFT_SD_EN);
+ /* Stall HW1 PEQ Engine and Clear DRAM to Zero */
+ regmap_update_bits(regmap, NAU8360_R9D_PEQ_CTL, NAU8360_PEQ_STALL,
+ NAU8360_PEQ_STALL);
+ /* Stall HW2 engine */
+ regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_STALL,
+ NAU8360_HW2_STALL);
+ /* Stall HW3 engine and DSP processor */
+ nau8360_dsp_enable(regmap, false);
+ /* Enable Clock Gate */
+ regmap_write(regmap, NAU8360_R7E_CLK_GATED_EN, NAU8360_CLK_GATED_EN);
+ /* Latch I2C LSB Value */
+ regmap_write(regmap, NAU8360_R02_I2C_ADDR, 0x0001);
+ /* ANC Left/Right Channel as Slot 2/3 and switch */
+ regmap_update_bits(regmap, NAU8360_R10_I2S_DATA_CTRL3,
+ NAU8360_RX_ANC_R_MASK | NAU8360_RX_ANC_L_MASK,
+ 0x3 << NAU8360_RX_ANC_R_SFT | 0x2 << NAU8360_RX_ANC_L_SFT);
+ /* Set DAC Clock Divider as 2 and Chopper Divider as 16 */
+ regmap_update_bits(regmap, NAU8360_R71_CLK_DIV_CFG,
+ NAU8360_DAC_CLK_DIV_MASK | NAU8360_DAC_CHOP_CLK_DIV_MASK,
+ NAU8360_DAC_CLK_DIV_2 | NAU8360_DAC_CHOP_CLK_DIV_16);
+ /* Set DAC SINC OSR as 128 and IVSENSE BS OSR as 32 */
+ regmap_update_bits(regmap, NAU8360_R5D_SINC_CFG,
+ NAU8360_DAC_SINC_OSR_MASK | NAU8360_IVSENSE_BS_OSR_MASK,
+ NAU8360_DAC_SINC_OSR_128 | NAU8360_IVSENSE_BS_OSR_32);
+
+ /* Set Trim Bit Control of DLDO and GVDD to The Highest Voltage */
+ regmap_write(regmap, NAU8360_R5F_ANA_TRIM_CFG1, 0xf407);
+
+ /* Disable Data Det and Clock Detection Settings*/
+ regmap_update_bits(regmap, NAU8360_R40_CLK_DET_CTRL, NAU8360_APWRUPEN |
+ NAU8360_CLKPWRUPEN | NAU8360_FS_MCLK_DET | NAU8360_FS_BCLK_DET, 0);
+
+ /* Set HW3 Droop and Filter Sample as 192K */
+ regmap_update_bits(regmap, NAU8360_R8C_HW3_CTL6, NAU8360_HW3_DROOP_MASK |
+ NAU8360_HW3_FS_MASK, NAU8360_HW3_DROOP_192K | NAU8360_HW3_FS_192K);
+ /* Set HW1 Mute, Mute Interval as 699ms and HW1 Zero THD as 0xff */
+ regmap_update_bits(regmap, NAU8360_R9C_HW1_CTL2, NAU8360_MUTE_INTRVL_MASK |
+ NAU8360_HW1_CH_MUTE | NAU8360_HW1_ZERO_THD_MASK,
+ NAU8360_MUTE_INTRVL_699MS | NAU8360_HW1_CH_MUTE | 0xff);
+ /* set HW2 droop with 192K*/
+ regmap_update_bits(regmap, NAU8360_R96_HW2_CTL6, NAU8360_HW2_DROOP_SEL_MASK |
+ NAU8360_HW2_DROOP_EN | NAU8360_HW2_FS_MASK,
+ NAU8360_HW2_DROOP_SEL_LARGE | NAU8360_HW2_DROOP_EN | NAU8360_HW2_FS_192K);
+ /* Set HW2 default volume */
+ regmap_write(regmap, NAU8360_R97_HW2_CTL7, 0xbf66);
+ regmap_write(regmap, NAU8360_R98_HW2_CTL8, 0xbf66);
+ /* Set HW2 Mute and Threshold of Zero Crossing */
+ regmap_update_bits(regmap, NAU8360_R99_HW2_CTL9, NAU8360_HW2_CH_MUTE |
+ NAU8360_HW2_ZERO_THD_MASK, NAU8360_HW2_CH_MUTE | 0xff);
+ /* According to DSP enabled or not, set stream path as
+ * HW1->DSP->HW2->SINC->HW3 or HW1->HW2->SINC->HW3
+ */
+ regmap_update_bits(regmap, NAU8360_R12_PATH_CTRL, NAU8360_SEL_HW1_MASK |
+ NAU8360_AUD_SEL_MASK | NAU8360_SEL_HW2_MASK | NAU8360_SEL_HW3_MASK |
+ NAU8360_DAC_SEL_MASK, NAU8360_SEL_HW1_OUT | NAU8360_AUD_SEL_SINCOUT |
+ NAU8360_SEL_HW2_OUT | NAU8360_SEL_HW3_OUT);
+ /* Set Input Status as Low and Input/Output Mode Disable for All GPIO */
+ regmap_write(regmap, NAU8360_R07_GP_CTRL, 0x0000);
+ /* enable SARADC with extra average filter of VBAT */
+ regmap_update_bits(regmap, NAU8360_R6A_SARADC_CFG0, NAU8360_SARADC_EN |
+ NAU8360_VBAT_AVG_EN, NAU8360_SARADC_EN | NAU8360_VBAT_AVG_EN);
+ /* Enable Temperature Sensor, VBATDIV and VRF.
+ * Set Average Filter Data as 512 for VTEMP and VBAT.
+ */
+ regmap_update_bits(regmap, NAU8360_R6B_SARADC_CFG1, NAU8360_VTEMP_EN |
+ NAU8360_VBATDIV_EN | NAU8360_VREF_EN | NAU8360_PRELOAD_VREF_EN |
+ NAU8360_VTEMP_AVG_N_MASK | NAU8360_VBAT_AVG_N_MASK, NAU8360_VTEMP_EN |
+ NAU8360_VBATDIV_EN | NAU8360_VREF_EN | NAU8360_PRELOAD_VREF_EN |
+ NAU8360_VTEMP_AVG_N_512 | NAU8360_VBAT_AVG_N_512);
+ /* Enable IV sense internal LDO */
+ regmap_update_bits(regmap, NAU8360_R6C_IVSNS_CFG0,
+ NAU8360_PD_LDO_SDM_IVSNS_PMD, 0);
+ /* Enable Bias Current and Reference Voltage for IVSENSE. Set VSNS Gain
+ * as 1/12 and ISNS Gain as x16(24dB). Set PGA Current as 2'b11 and SDN
+ * Delay as 2'b00.
+ */
+ regmap_write(regmap, NAU8360_R6D_IVSNS_CFG1, 0x0f5c);
+ /* sawtooth clock from ivsense clock, PWM frequency 432 Khz */
+ regmap_update_bits(regmap, NAU8360_RA4_ANA_REG_0, NAU8360_SEL_STCLK_MASK |
+ NAU8360_NSEL_SAW_MASK, NAU8360_SEL_STCLK_IVCLK | 0x8);
+ /* sawtooth PLL APR */
+ regmap_update_bits(regmap, NAU8360_RA5_ANA_REG_1, NAU8360_SAW_PLL_MASK,
+ NAU8360_SAW_PLL_NOR);
+
+ /* Set DAC gain (0dB/+3.2dB) by current cell adjustment */
+ regmap_update_bits(regmap, NAU8360_R6E_DAC_CFG0, NAU8360_DAC_CUR_MASK,
+ nau8360->dac_cur_enable ? NAU8360_DAC_CUR_3_2DB : NAU8360_DAC_CUR_0DB);
+ /* Config trim short current reference. Enable Non-overlap longer delay.
+ * Set segment driver as half driving strength.
+ */
+ regmap_update_bits(regmap, NAU8360_R68_ANALOG_CONTROL_1, NAU8360_DTX_EN |
+ NAU8360_TRIMSCR_MASK | NAU8360_DRVCTL_SEGL_FULL |
+ NAU8360_DRVCTL_SEGR_FULL, NAU8360_TRIMSCR_MLOW | NAU8360_DTX_EN);
+ /* Enable SCP Clear Mode and Class D Modulator to Common Mode */
+ regmap_update_bits(regmap, NAU8360_R67_ANALOG_CONTROL_0,
+ NAU8360_SCP_CLEAR_MODE_MASK | NAU8360_CM_COMP_EN,
+ NAU8360_SCP_CLEAR_MODE_AUTO | NAU8360_CM_COMP_EN);
+ /* Set Analog Mute and Class D Modulator Gain as 14dB */
+ regmap_update_bits(regmap, NAU8360_R67_ANALOG_CONTROL_0, NAU8360_ANA_MUTE |
+ NAU8360_MOD_GAIN_MASK, NAU8360_ANA_MUTE | NAU8360_MOD_GAIN_14DB);
+ /* Set Stereo or PBTL Mode */
+ regmap_update_bits(regmap, NAU8360_R67_ANALOG_CONTROL_0,
+ NAU8360_V_PBTL_EN, nau8360->pbtl_enable ? NAU8360_V_PBTL_EN : 0);
+ regmap_update_bits(regmap, NAU8360_R6C_IVSNS_CFG0,
+ NAU8360_PBTL_ISENE_LR_MASK, NAU8360_PBTL_ISENE_LR_ILR);
+ /* adjust HW3 default volume of voltage/current sense */
+ regmap_write(regmap, NAU8360_R8A_HW3_VL_CTL7, 0xbe90);
+ regmap_write(regmap, NAU8360_R8B_HW3_VR_CTL8, 0xbe90);
+ regmap_write(regmap, NAU8360_R8D_HW3_IL_CTL7, nau8360->pbtl_enable ? 0xc3aa : 0xc24c);
+ regmap_write(regmap, NAU8360_R8E_HW3_IR_CTL8, nau8360->pbtl_enable ? 0xc3aa : 0xc24c);
+ /* Set HW3 Zero THD as 0xff */
+ regmap_update_bits(regmap, NAU8360_R8F_HW3_CTL9,
+ NAU8360_HW3_ZERO_THD_MASK, 0xff);
+ /* Enable TX SDOUT and Data at BCLK Rising. */
+ regmap_update_bits(regmap, NAU8360_R0D_I2S_PCM_CTRL3, NAU8360_TX_FILL_MASK,
+ NAU8360_TX_FILL_ZERO);
+ regmap_update_bits(regmap, NAU8360_R0D_I2S_PCM_CTRL3,
+ NAU8360_VSNS_L_SLEN_MASK, NAU8360_VSNS_L_SLEN_16);
+ regmap_update_bits(regmap, NAU8360_R0E_I2S_DATA_CTRL1,
+ NAU8360_ISNS_L_SLEN_MASK, NAU8360_ISNS_L_SLEN_16);
+ regmap_update_bits(regmap, NAU8360_R11_I2S_DATA_CTRL4,
+ NAU8360_VSNS_R_SLEN_MASK | NAU8360_ISNS_R_SLEN_MASK,
+ NAU8360_VSNS_R_SLEN_16 | NAU8360_ISNS_R_SLEN_16);
+ /* set DAC channel temperature trim code slope as 0x7D */
+ regmap_update_bits(regmap, NAU8360_R7A_DAC_TRIM_CFG2,
+ NAU8360_DAC_TEMP_SLOPE_MASK, 0x7D << NAU8360_DAC_TEMP_SLOPE_SFT);
+ /* set ISNS temperature trim code slope as 0x1a */
+ regmap_update_bits(regmap, NAU8360_R7B_IVSNS_TRIM_CFG,
+ NAU8360_ISNS_TEMP_SLOPE_MASK, 0x1a << NAU8360_ISNS_TEMP_SLOPE_SFT);
+ /* set misc trim config as 0x67F0 */
+ regmap_update_bits(regmap, NAU8360_R7C_MISC_TRIM_CFG, NAU8360_DAC_GAIN_SB_MASK |
+ NAU8360_DAC_TEMP_SB_MASK | NAU8360_ISNS_TEMP_SB_MASK |
+ NAU8360_VSNS_TEMP_SB_MASK | NAU8360_ISNS_GAIN_SB_MASK |
+ NAU8360_VSNS_GAIN_SB_MASK, (0x3 << NAU8360_DAC_TEMP_SB_SFT) |
+ (0x7 << NAU8360_ISNS_TEMP_SB_SFT) | (0x3 << NAU8360_VSNS_TEMP_SB_SFT));
+ /* Enable Efuse Initianllization */
+ regmap_update_bits(regmap, NAU8360_R60_RST, NAU8360_EFUSE_CTRL_EN,
+ NAU8360_EFUSE_CTRL_EN);
+ /* Clear HW2 DRAM */
+ regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0,
+ NAU8360_HW2_DRAM_CLR, NAU8360_HW2_DRAM_CLR);
+ /* Finish HW DRAM Clear */
+ regmap_update_bits(regmap, NAU8360_R90_HW2_CTL0, NAU8360_HW2_DRAM_CLR, 0);
+ /* Clear HW3 DRAM */
+ regmap_update_bits(regmap, NAU8360_R86_HW3_CTL0,
+ NAU8360_HW3_DRAM_CLR, NAU8360_HW3_DRAM_CLR);
+ /* Finish HW DRAM Clear */
+ regmap_update_bits(regmap, NAU8360_R86_HW3_CTL0, NAU8360_HW3_DRAM_CLR, 0);
+}
+
+static void nau8360_print_device_properties(struct nau8360 *nau8360)
+{
+ int i;
+
+ dev_dbg(nau8360->dev, "pbtl-enable: %d", nau8360->pbtl_enable);
+ dev_dbg(nau8360->dev, "dac-cur-enable: %d", nau8360->dac_cur_enable);
+ for (i = 0; i < NAU8360_DSP_FW_NUM; i++)
+ dev_dbg(nau8360->dev, "firmware-name[%d]: %s", i,
+ nau8360->dsp_firmware[i]);
+
+ for (i = 0; i < NAU8360_TDM_TXN; i++)
+ dev_dbg(nau8360->dev, "dsp-tx-slot[%d] (%s): %d%s", i,
+ nau8360_tx_func_names[i], nau8360->tdm_tx_func_slot[i],
+ nau8360->tdm_tx_func_slot[i] == TDM_SLOT_NONE ? " (none)" : "");
+
+ for (i = 0; i < NAU8360_TDM_RXN; i++)
+ dev_dbg(nau8360->dev, "dsp-rx-slot[%d] (%s): %d%s", i,
+ nau8360_rx_func_names[i], nau8360->tdm_rx_func_slot[i],
+ nau8360->tdm_rx_func_slot[i] == TDM_SLOT_NONE ? " (none)" : "");
+}
+
+static int nau8360_read_device_properties(struct nau8360 *nau8360)
+{
+ const char *def_fws[NAU8360_DSP_FW_NUM] = {
+ NAU8360_DSP_FIRMWARE".l", NAU8360_DSP_FIRMWARE".r"
+ };
+ struct device *dev = nau8360->dev;
+ const char *firmware_names[NAU8360_DSP_FW_NUM];
+ int i, ret;
+
+ ret = device_property_read_u32_array(dev, "nuvoton,dsp-tx-slot-mapping",
+ nau8360->tdm_tx_func_slot, NAU8360_TDM_TXN);
+ if (ret) {
+ for (i = 0; i < NAU8360_TDM_TXN; i++)
+ nau8360->tdm_tx_func_slot[i] = TDM_SLOT_NONE;
+ } else {
+ for (i = 0; i < NAU8360_TDM_TXN; i++) {
+ if (nau8360->tdm_tx_func_slot[i] == TDM_SLOT_NONE)
+ continue;
+ if (nau8360->tdm_tx_func_slot[i] >= NAU8360_TDM_MAX_CHAN) {
+ dev_warn(dev, "Invalid TX slot %d, set to none\n",
+ nau8360->tdm_tx_func_slot[i]);
+ nau8360->tdm_tx_func_slot[i] = TDM_SLOT_NONE;
+ }
+ }
+ }
+
+ ret = device_property_read_u32_array(dev, "nuvoton,dsp-rx-slot-mapping",
+ nau8360->tdm_rx_func_slot, NAU8360_TDM_RXN);
+ if (ret) {
+ nau8360->tdm_rx_func_slot[NAU8360_TDM_DACL] = 0;
+ nau8360->tdm_rx_func_slot[NAU8360_TDM_DACR] = 1;
+ nau8360->tdm_rx_func_slot[NAU8360_TDM_ANCL] = TDM_SLOT_NONE;
+ nau8360->tdm_rx_func_slot[NAU8360_TDM_ANCR] = TDM_SLOT_NONE;
+ } else {
+ for (i = 0; i < NAU8360_TDM_RXN; i++) {
+ if (nau8360->tdm_rx_func_slot[i] == TDM_SLOT_NONE)
+ continue;
+ if (nau8360->tdm_rx_func_slot[i] >= NAU8360_TDM_MAX_CHAN) {
+ dev_warn(dev, "Invalid RX slot %d, set to none\n",
+ nau8360->tdm_rx_func_slot[i]);
+ nau8360->tdm_rx_func_slot[i] = TDM_SLOT_NONE;
+ }
+ }
+ }
+
+ nau8360->pbtl_enable = device_property_read_bool(dev, "nuvoton,pbtl-enable");
+ nau8360->dac_cur_enable = device_property_read_bool(dev, "nuvoton,dac-cur-enable");
+
+ ret = device_property_read_string_array(dev, "firmware-name",
+ firmware_names, NAU8360_DSP_FW_NUM);
+ if (ret != NAU8360_DSP_FW_NUM) {
+ dev_warn(dev, "The firmware-name was not found, using default.");
+ for (i = 0; i < NAU8360_DSP_FW_NUM; i++) {
+ nau8360->dsp_firmware[i] = devm_kstrdup(dev, def_fws[i], GFP_KERNEL);
+
+ if (!nau8360->dsp_firmware[i])
+ return -ENOMEM;
+ }
+ } else {
+ for (i = 0; i < NAU8360_DSP_FW_NUM; i++) {
+ nau8360->dsp_firmware[i] = devm_kasprintf(dev, GFP_KERNEL,
+ NAU8360_DSP_FIRMDIR "%s", firmware_names[i]);
+
+ if (!nau8360->dsp_firmware[i])
+ return -ENOMEM;
+ }
+ }
+
+ return 0;
+}
+
+static struct reg_default *nau8360_alloc_defaults(struct device *dev, int *total_regs)
+{
+ struct reg_default *dyn_defaults;
+ int reg_num = ARRAY_SIZE(nau8360_reg_defaults);
+ int total = reg_num + (2 * NAU8360_TOT_BAND_PER_CH * NAU8360_TOT_BAND_COE);
+ int i, j, idx;
+
+ dyn_defaults = devm_kzalloc(dev, total * sizeof(*dyn_defaults), GFP_KERNEL);
+ if (!dyn_defaults)
+ return NULL;
+
+ memcpy(dyn_defaults, nau8360_reg_defaults, sizeof(*dyn_defaults) * reg_num);
+ idx = reg_num;
+
+ for (i = 0; i < NAU8360_TOT_BAND_PER_CH; i++) {
+ unsigned int range = i * NAU8360_TOT_BAND_COE_RANGE;
+ unsigned int l_base = NAU8360_R100_LEFT_BIQ0_COE + range;
+ unsigned int r_base = NAU8360_R200_RIGHT_BIQ0_COE + range;
+
+ for (j = 0; j < NAU8360_TOT_BAND_COE; j++) {
+ dyn_defaults[idx++].reg = l_base + j;
+ dyn_defaults[idx++].reg = r_base + j;
+ }
+ }
+
+ *total_regs = total;
+
+ return dyn_defaults;
+}
+
+static int nau8360_i2c_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8360 *nau8360;
+ struct regmap_config regmap_cfg = nau8360_regmap_config;
+ struct reg_default *dyn_defaults;
+ int num_total_regs;
+ int ret, value;
+
+ nau8360 = devm_kzalloc(dev, sizeof(*nau8360), GFP_KERNEL);
+ if (!nau8360)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c, nau8360);
+ mutex_init(&nau8360->lock);
+ INIT_WORK(&nau8360->load_fw_work, nau8360_load_fw_work);
+
+ dyn_defaults = nau8360_alloc_defaults(dev, &num_total_regs);
+ if (!dyn_defaults)
+ return -ENOMEM;
+
+ regmap_cfg.reg_defaults = dyn_defaults;
+ regmap_cfg.num_reg_defaults = num_total_regs;
+
+ nau8360->regmap = devm_regmap_init(dev, NULL, i2c, &regmap_cfg);
+ if (IS_ERR(nau8360->regmap))
+ return PTR_ERR(nau8360->regmap);
+ nau8360->dev = dev;
+
+ nau8360_reset_chip(nau8360->regmap);
+ ret = regmap_read(nau8360->regmap, NAU8360_R46_I2C_DEVICE_ID, &value);
+ if (ret) {
+ dev_err(dev, "Failed to read NAU83G60 device id %d", ret);
+ return ret;
+ }
+
+ ret = nau8360_read_device_properties(nau8360);
+ if (ret)
+ return ret;
+
+ nau8360_print_device_properties(nau8360);
+ nau8360_init_regs(nau8360);
+
+ return devm_snd_soc_register_component(dev, &soc_comp_dev_nau8360, &nau8360_dai, 1);
+}
+
+static void nau8360_i2c_remove(struct i2c_client *client)
+{
+ struct nau8360 *nau8360 = i2c_get_clientdata(client);
+
+ cancel_work_sync(&nau8360->load_fw_work);
+}
+
+static const struct i2c_device_id nau8360_i2c_ids[] = {
+ { .name = "nau8360" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, nau8360_i2c_ids);
+
+static const struct of_device_id nau8360_of_ids[] = {
+ { .compatible = "nuvoton,nau8360" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8360_of_ids);
+
+static const struct acpi_device_id nau8360_acpi_match[] = {
+ { .id = "NVTN2002" },
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, nau8360_acpi_match);
+
+static struct i2c_driver nau8360_i2c_driver = {
+ .driver = {
+ .name = "nau8360",
+ .of_match_table = of_match_ptr(nau8360_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8360_acpi_match),
+ },
+ .probe = nau8360_i2c_probe,
+ .remove = nau8360_i2c_remove,
+ .id_table = nau8360_i2c_ids,
+};
+module_i2c_driver(nau8360_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver");
+MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>");
+MODULE_AUTHOR("Seven Lee <wtli@nuvoton.com>");
+MODULE_AUTHOR("John Hsu <kchsu0@nuvoton.com>");
+MODULE_AUTHOR("Neo Chang <ylchang2@nuvoton.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8360.h b/sound/soc/codecs/nau8360.h
new file mode 100644
index 000000000000..71396747c0fa
--- /dev/null
+++ b/sound/soc/codecs/nau8360.h
@@ -0,0 +1,917 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// The NAU83G60 Stereo Class-D Amplifier with DSP and I/V-sense driver.
+//
+// Copyright (C) 2026 Nuvoton Technology Corp.
+// Author: David Lin <ctlin0@nuvoton.com>
+// Seven Lee <wtli@nuvoton.com>
+// John Hsu <kchsu0@nuvoton.com>
+// Neo Chang <ylchang2@nuvoton.com>
+
+#ifndef __NAU8360_H__
+#define __NAU8360_H__
+
+#define NAU8360_R00_SOFTWARE_RST 0x00
+#define NAU8360_R01_DSP_SOFTWARE_RST 0x01
+#define NAU8360_R02_I2C_ADDR 0x02
+#define NAU8360_R03_CLK_CTRL0 0x03
+#define NAU8360_R04_CLK_CTRL1 0x04
+#define NAU8360_R05_INTERRUPT_CTRL 0x05
+#define NAU8360_R06_INT_CLR_STATUS 0x06
+#define NAU8360_R07_GP_CTRL 0x07
+#define NAU8360_R08_GP_CTRL0 0x08
+#define NAU8360_R09_GP_CTRL1 0x09
+#define NAU8360_R0A_GP_CTRL2 0x0a
+#define NAU8360_R0B_I2S_PCM_CTRL1 0x0b
+#define NAU8360_R0C_I2S_PCM_CTRL2 0x0c
+#define NAU8360_R0D_I2S_PCM_CTRL3 0x0d
+#define NAU8360_R0E_I2S_DATA_CTRL1 0x0e
+#define NAU8360_R0F_I2S_DATA_CTRL2 0x0f
+#define NAU8360_R10_I2S_DATA_CTRL3 0x10
+#define NAU8360_R11_I2S_DATA_CTRL4 0x11
+#define NAU8360_R12_PATH_CTRL 0x12
+#define NAU8360_R16_NO_NAME 0x16
+#define NAU8360_R17_I2S0_DATA_CTRL5 0x17
+#define NAU8360_R1A_DSP_CORE_CTRL2 0x1a
+#define NAU8360_R21_VBAT_READOUT 0x21
+#define NAU8360_R22_TEMP_READOUT 0x22
+#define NAU8360_R2C_ALC_CTRL1 0x2c
+#define NAU8360_R2D_ALC_CTRL2 0x2d
+#define NAU8360_R2E_ALC_CTRL3 0x2e
+#define NAU8360_R31_UVLOP_CTRL1 0x31
+#define NAU8360_R32_UVLOP_CTRL2 0x32
+#define NAU8360_R33_UVLOP_CTRL3 0x33
+#define NAU8360_R40_CLK_DET_CTRL 0x40
+#define NAU8360_R41_CLK_CTL2 0x41
+#define NAU8360_R46_I2C_DEVICE_ID 0x46
+#define NAU8360_R5D_SINC_CFG 0x5d
+#define NAU8360_R5F_ANA_TRIM_CFG1 0x5f
+#define NAU8360_R60_RST 0x60
+#define NAU8360_R67_ANALOG_CONTROL_0 0x67
+#define NAU8360_R68_ANALOG_CONTROL_1 0x68
+#define NAU8360_R69_ANALOG_CONTROL_3 0x69
+#define NAU8360_R6A_SARADC_CFG0 0x6a
+#define NAU8360_R6B_SARADC_CFG1 0x6b
+#define NAU8360_R6C_IVSNS_CFG0 0x6c
+#define NAU8360_R6D_IVSNS_CFG1 0x6d
+#define NAU8360_R6E_DAC_CFG0 0x6e
+#define NAU8360_R71_CLK_DIV_CFG 0x71
+#define NAU8360_R72_PLL_CFG0 0x72
+#define NAU8360_R73_PLL_CFG1 0x73
+#define NAU8360_R74_PLL_CFG2 0x74
+#define NAU8360_R77_SOFT_SD 0x77
+#define NAU8360_R78_PD_SW_DLDO 0x78
+#define NAU8360_R79_EN_HIRC48M 0x79
+#define NAU8360_R7A_DAC_TRIM_CFG2 0x7a
+#define NAU8360_R7B_IVSNS_TRIM_CFG 0x7b
+#define NAU8360_R7C_MISC_TRIM_CFG 0x7c
+#define NAU8360_R7E_CLK_GATED_EN 0x7e
+#define NAU8360_R86_HW3_CTL0 0x86
+#define NAU8360_R88_ALC_CTRL6 0x88
+#define NAU8360_R8A_HW3_VL_CTL7 0x8a
+#define NAU8360_R8B_HW3_VR_CTL8 0x8b
+#define NAU8360_R8C_HW3_CTL6 0x8c
+#define NAU8360_R8D_HW3_IL_CTL7 0x8d
+#define NAU8360_R8E_HW3_IR_CTL8 0x8e
+#define NAU8360_R8F_HW3_CTL9 0x8f
+#define NAU8360_R90_HW2_CTL0 0x90
+#define NAU8360_R96_HW2_CTL6 0x96
+#define NAU8360_R97_HW2_CTL7 0x97
+#define NAU8360_R98_HW2_CTL8 0x98
+#define NAU8360_R99_HW2_CTL9 0x99
+#define NAU8360_R9A_HW1_CTL0 0x9a
+#define NAU8360_R9B_HW1_CTL1 0x9b
+#define NAU8360_R9C_HW1_CTL2 0x9c
+#define NAU8360_R9D_PEQ_CTL 0x9d
+#define NAU8360_RA0_LEFT_XODRC_CTRL 0xa0
+#define NAU8360_RA2_RIGHT_XODRC_CTRL 0xa2
+#define NAU8360_RA4_ANA_REG_0 0xa4
+#define NAU8360_RA5_ANA_REG_1 0xa5
+/* Left PEQ includes 15 band, 10 coeff per band, from start addr 0x100 */
+#define NAU8360_R100_LEFT_BIQ0_COE 0x100
+#define NAU8360_R10C_LEFT_BIQ1_COE 0x10c
+#define NAU8360_R118_LEFT_BIQ2_COE 0x118
+#define NAU8360_R124_LEFT_BIQ3_COE 0x124
+#define NAU8360_R130_LEFT_BIQ4_COE 0x130
+#define NAU8360_R13C_LEFT_BIQ5_COE 0x13c
+#define NAU8360_R148_LEFT_BIQ6_COE 0x148
+#define NAU8360_R154_LEFT_BIQ7_COE 0x154
+#define NAU8360_R160_LEFT_BIQ8_COE 0x160
+#define NAU8360_R16C_LEFT_BIQ9_COE 0x16c
+#define NAU8360_R178_LEFT_BIQ10_COE 0x178
+#define NAU8360_R184_LEFT_BIQ11_COE 0x184
+#define NAU8360_R190_LEFT_BIQ12_COE 0x190
+#define NAU8360_R19C_LEFT_BIQ13_COE 0x19c
+#define NAU8360_R1A8_LEFT_BIQ14_COE 0x1a8
+/* Right PEQ includes 15 band, 10 coeff per band, from start addr 0x200 */
+#define NAU8360_R200_RIGHT_BIQ0_COE 0x200
+#define NAU8360_R20C_RIGHT_BIQ1_COE 0x20c
+#define NAU8360_R218_RIGHT_BIQ2_COE 0x218
+#define NAU8360_R224_RIGHT_BIQ3_COE 0x224
+#define NAU8360_R230_RIGHT_BIQ4_COE 0x230
+#define NAU8360_R23C_RIGHT_BIQ5_COE 0x23c
+#define NAU8360_R248_RIGHT_BIQ6_COE 0x248
+#define NAU8360_R254_RIGHT_BIQ7_COE 0x254
+#define NAU8360_R260_RIGHT_BIQ8_COE 0x260
+#define NAU8360_R26C_RIGHT_BIQ9_COE 0x26c
+#define NAU8360_R278_RIGHT_BIQ10_COE 0x278
+#define NAU8360_R284_RIGHT_BIQ11_COE 0x284
+#define NAU8360_R290_RIGHT_BIQ12_COE 0x290
+#define NAU8360_R29C_RIGHT_BIQ13_COE 0x29c
+#define NAU8360_R2A8_RIGHT_BIQ14_COE 0x2a8
+#define NAU8360_RF000_DSP_COMM 0xf000
+#define NAU8360_RF002_DSP_COMM 0xf002
+#define NAU8360_REG_MAX NAU8360_RF002_DSP_COMM
+
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8360_REG_ADDR_LEN 16
+#define NAU8360_REG_DATA_LEN 16
+
+/* NAU8360_R03_CLK_CTRL0 (0x03) */
+#define NAU8360_MCLK_DIV_SFT 10
+#define NAU8360_MCLK_DIV_MAX 0x3f
+#define NAU8360_MCLK_DIV_MASK (0x3f << NAU8360_MCLK_DIV_SFT)
+#define NAU8360_DSP_CLK_DIV_SFT 7
+#define NAU8360_DSP_CLK_DIV_MAX 0x7
+#define NAU8360_DSP_CLK_DIV_MASK (0x7 << NAU8360_DSP_CLK_DIV_SFT)
+#define NAU8360_DSP_CLK_SEL_SFT 5
+#define NAU8360_DSP_CLK_SEL_MASK (0x3 << NAU8360_DSP_CLK_SEL_SFT)
+#define NAU8360_DSP_CLK_SEL_PLL (0x2 << NAU8360_DSP_CLK_SEL_SFT)
+#define NAU8360_DSP_CLK_SEL_HIRC48M (0x1 << NAU8360_DSP_CLK_SEL_SFT)
+#define NAU8360_DSP_CLK_SEL_MCLK (0x0 << NAU8360_DSP_CLK_SEL_SFT)
+
+/* NAU8360_R04_CLK_CTRL1 (0x4) */
+#define NAU8360_MCLK_SEL_SFT 14
+#define NAU8360_MCLK_SEL_MASK (0x3 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_MCLK_SEL_PLL (0x2 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_MCLK_SEL_HIRC48M (0x1 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_MCLK_SEL_MCLK (0x0 << NAU8360_MCLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_SFT 10
+#define NAU8360_HW_CLK_SEL_MASK (0x3 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_PLL (0x2 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_HIRC48M (0x1 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_SEL_MCLK (0x0 << NAU8360_HW_CLK_SEL_SFT)
+#define NAU8360_HW_CLK_DIV_SFT 7
+#define NAU8360_HW_CLK_DIV_MAX NAU8360_DSP_CLK_DIV_MAX
+#define NAU8360_HW_CLK_DIV_MASK (0x7 << NAU8360_HW_CLK_DIV_SFT)
+
+/* NAU8360_R07_GP_CTRL (0x7) */
+#define NAU8360_GPIO1_IEOE_SFT 14
+#define NAU8360_GPIO1_IEOE_MASK (0x3 << NAU8360_GPIO1_IEOE_SFT)
+#define NAU8360_GPIO2_IEOE_SFT 12
+#define NAU8360_GPIO2_IEOE_MASK (0x3 << NAU8360_GPIO2_IEOE_SFT)
+#define NAU8360_GPIO3_IEOE_SFT 10
+#define NAU8360_GPIO3_IEOE_MASK (0x3 << NAU8360_GPIO3_IEOE_SFT)
+#define NAU8360_GPIO4_IEOE_SFT 8
+#define NAU8360_GPIO4_IEOE_MASK (0x3 << NAU8360_GPIO4_IEOE_SFT)
+#define NAU8360_GPIO1_IN_STAT_SFT 7
+#define NAU8360_GPIO1_IN_STAT_MASK (0x1 << NAU8360_GPIO1_IN_STAT_SFT)
+#define NAU8360_GPIO2_IN_STAT_SFT 6
+#define NAU8360_GPIO2_IN_STAT_MASK (0x1 << NAU8360_GPIO2_IN_STAT_SFT)
+#define NAU8360_GPIO3_IN_STAT_SFT 5
+#define NAU8360_GPIO3_IN_STAT_MASK (0x1 << NAU8360_GPIO3_IN_STAT_SFT)
+#define NAU8360_GPIO4_IN_STAT_SFT 4
+#define NAU8360_GPIO4_IN_STAT_MASK (0x1 << NAU8360_GPIO4_IN_STAT_SFT)
+
+/* NAU8360_R08_GP_CTRL0 (0x8) */
+#define NAU8360_GPIO2_MUX_SFT 14
+#define NAU8360_GPIO2_MUX_MASK (0x3 << NAU8360_GPIO2_MUX_SFT)
+#define NAU8360_GPIO2_SR_SFT 13
+#define NAU8360_GPIO2_SR_MASK (0x1 << NAU8360_GPIO2_SR_SFT)
+#define NAU8360_GPIO2_CDS_SFT 11
+#define NAU8360_GPIO2_CDS_MASK (0x3 << NAU8360_GPIO2_CDS_SFT)
+#define NAU8360_GPIO2_PS_SFT 10
+#define NAU8360_GPIO2_PS_MASK (0x1 << NAU8360_GPIO2_PS_SFT)
+#define NAU8360_GPIO2_PE_SFT 9
+#define NAU8360_GPIO2_PE_EN (0x1 << NAU8360_GPIO2_PE_SFT)
+#define NAU8360_GPIO1_MUX_SFT 6
+#define NAU8360_GPIO1_MUX_MASK (0x3 << NAU8360_GPIO1_MUX_SFT)
+#define NAU8360_GPIO1_SR_SFT 5
+#define NAU8360_GPIO1_SR_MASK (0x1 << NAU8360_GPIO1_SR_SFT)
+#define NAU8360_GPIO1_CDS_SFT 3
+#define NAU8360_GPIO1_CDS_MASK (0x3 << NAU8360_GPIO1_CDS_SFT)
+#define NAU8360_GPIO1_PS_SFT 2
+#define NAU8360_GPIO1_PS_MASK (0x1 << NAU8360_GPIO1_PS_SFT)
+#define NAU8360_GPIO1_PE_SFT 1
+#define NAU8360_GPIO1_PE_EN (0x1 << NAU8360_GPIO1_PE_SFT)
+
+/* NAU8360_R09_GP_CTRL1 (0x9) */
+#define NAU8360_GPIO4_MUX_SFT 14
+#define NAU8360_GPIO4_MUX_MASK (0x3 << NAU8360_GPIO4_MUX_SFT)
+#define NAU8360_GPIO4_SR_SFT 13
+#define NAU8360_GPIO4_SR_MASK (0x1 << NAU8360_GPIO4_SR_SFT)
+#define NAU8360_GPIO4_CDS_SFT 11
+#define NAU8360_GPIO4_CDS_MASK (0x3 << NAU8360_GPIO4_CDS_SFT)
+#define NAU8360_GPIO4_PS_SFT 10
+#define NAU8360_GPIO4_PS_MASK (0x1 << NAU8360_GPIO4_PS_SFT)
+#define NAU8360_GPIO4_PE_SFT 9
+#define NAU8360_GPIO4_PE_EN (0x1 << NAU8360_GPIO4_PE_SFT)
+#define NAU8360_GPIO3_MUX_SFT 6
+#define NAU8360_GPIO3_MUX_MASK (0x3 << NAU8360_GPIO3_MUX_SFT)
+#define NAU8360_GPIO3_SR_SFT 5
+#define NAU8360_GPIO3_SR_MASK (0x1 << NAU8360_GPIO3_SR_SFT)
+#define NAU8360_GPIO3_CDS_SFT 3
+#define NAU8360_GPIO3_CDS_MASK (0x3 << NAU8360_GPIO3_CDS_SFT)
+#define NAU8360_GPIO3_PS_SFT 2
+#define NAU8360_GPIO3_PS_MASK (0x1 << NAU8360_GPIO3_PS_SFT)
+#define NAU8360_GPIO3_PE_SFT 1
+#define NAU8360_GPIO3_PE_EN (0x1 << NAU8360_GPIO3_PE_SFT)
+
+/* NAU8360_R0B_I2S_PCM_CTRL1 (0x0b) */
+#define NAU8360_EN_TDM_TX_SFT 15
+#define NAU8360_EN_TDM_TX (0x1 << NAU8360_EN_TDM_TX_SFT)
+#define NAU8360_EN_TDM_RX_SFT 14
+#define NAU8360_EN_TDM_RX (0x1 << NAU8360_EN_TDM_RX_SFT)
+#define NAU8360_FRAME_START_SFT 8
+#define NAU8360_FRAME_START_MASK (0x1 << NAU8360_FRAME_START_SFT)
+#define NAU8360_FRAME_START_H2L (0x1 << NAU8360_FRAME_START_SFT)
+#define NAU8360_FRAME_START_L2H (0x0 << NAU8360_FRAME_START_SFT)
+#define NAU8360_RX_JUSTIFY_SFT 7
+#define NAU8360_RX_JUSTIFY_MASK (0x1 << NAU8360_RX_JUSTIFY_SFT)
+#define NAU8360_RX_RIGHT_JUSTIFY (0x1 << NAU8360_RX_JUSTIFY_SFT)
+#define NAU8360_RX_LEFT_JUSTIFY (0x0 << NAU8360_RX_JUSTIFY_SFT)
+#define NAU8360_RX_OFFSET_SFT 2
+#define NAU8360_RX_OFFSET_MASK (0x1f << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_I2S (0x1 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_LEFT (0x0 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_RIGHT (0x0 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_PCM_A (0x1 << NAU8360_RX_OFFSET_SFT)
+#define NAU8360_RX_OFFSET_PCM_B (0x0 << NAU8360_RX_OFFSET_SFT)
+
+/* NAU8360_R0C_I2S_PCM_CTRL2 (0xc) */
+#define NAU8360_TDM_DLEN_SFT 10
+#define NAU8360_TDM_DLEN_MASK (0x3 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_32 (0x3 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_24 (0x2 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_20 (0x1 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_DLEN_16 (0x0 << NAU8360_TDM_DLEN_SFT)
+#define NAU8360_TDM_CLEN_SFT 8
+#define NAU8360_TDM_CLEN_MASK (0x3 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_TDM_CLEN_32 (0x2 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_TDM_CLEN_24 (0x1 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_TDM_CLEN_16 (0x0 << NAU8360_TDM_CLEN_SFT)
+#define NAU8360_RX_DACR_SFT 4
+#define NAU8360_RX_DACR_MASK (0xf << NAU8360_RX_DACR_SFT)
+#define NAU8360_RX_DACL_MASK 0xf
+
+/* NAU8360_R0D_I2S_PCM_CTRL3 (0x0d) */
+#define NAU8360_TX_FILL_SFT 12
+#define NAU8360_TX_FILL_MASK (0x1 << NAU8360_TX_FILL_SFT)
+#define NAU8360_TX_FILL_HIGHZ (0x1 << NAU8360_TX_FILL_SFT)
+#define NAU8360_TX_FILL_ZERO (0x0 << NAU8360_TX_FILL_SFT)
+#define NAU8360_TX_OFFSET_SFT 9
+#define NAU8360_TX_OFFSET_MASK (0x7 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_I2S (0x1 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_LEFT (0x0 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_RIGHT (0x0 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_PCM_A (0x1 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_TX_OFFSET_PCM_B (0x0 << NAU8360_TX_OFFSET_SFT)
+#define NAU8360_VSNS_L_SLEN_SFT 7
+#define NAU8360_VSNS_L_SLEN_MASK (0x1 << NAU8360_VSNS_L_SLEN_SFT)
+#define NAU8360_VSNS_L_SLEN_16 (0x1 << NAU8360_VSNS_L_SLEN_SFT)
+#define NAU8360_VSNS_L_SLEN_8 (0x0 << NAU8360_VSNS_L_SLEN_SFT)
+#define NAU8360_VSNS_L_TX_EN_SFT 6
+#define NAU8360_VSNS_L_TX_EN (0x1 << NAU8360_VSNS_L_TX_EN_SFT)
+#define NAU8360_VSNS_L_SLOT_MASK 0x3f
+
+/* NAU8360_R0E_I2S_DATA_CTRL1 (0x0e) */
+#define NAU8360_ISNS_L_SLEN_SFT 15
+#define NAU8360_ISNS_L_SLEN_MASK (0x1 << NAU8360_ISNS_L_SLEN_SFT)
+#define NAU8360_ISNS_L_SLEN_16 (0x1 << NAU8360_ISNS_L_SLEN_SFT)
+#define NAU8360_ISNS_L_SLEN_8 (0x0 << NAU8360_ISNS_L_SLEN_SFT)
+#define NAU8360_ISNS_L_TX_EN_SFT 14
+#define NAU8360_ISNS_L_TX_EN (0x1 << NAU8360_ISNS_L_TX_EN_SFT)
+#define NAU8360_ISNS_L_SLOT_SFT 8
+#define NAU8360_ISNS_L_SLOT_MASK (0x3f << NAU8360_ISNS_L_SLOT_SFT)
+#define NAU8360_PDM_L_SLEN_SFT 7
+#define NAU8360_PDM_L_SLEN_MASK (0x1 << NAU8360_PDM_L_SLEN_SFT)
+#define NAU8360_PDM_L_SLEN_24 (0x1 << NAU8360_PDM_L_SLEN_SFT)
+#define NAU8360_PDM_L_SLEN_16 (0x0 << NAU8360_PDM_L_SLEN_SFT)
+#define NAU8360_PDM_L_TX_EN_SFT 6
+#define NAU8360_PDM_L_TX_EN (0x1 << NAU8360_PDM_L_TX_EN_SFT)
+#define NAU8360_PDM_L_SLOT_MASK 0x3f
+
+/* NAU8360_R0F_I2S_DATA_CTRL2 (0xf) */
+#define NAU8360_VBAT_TX_EN_SFT 14
+#define NAU8360_VBAT_TX_EN (0x1 << NAU8360_VBAT_TX_EN_SFT)
+#define NAU8360_VBAT_SLOT_SFT 8
+#define NAU8360_VBAT_SLOT_MASK (0x3f << NAU8360_VBAT_SLOT_SFT)
+#define NAU8360_PDM_R_SLEN_SFT 7
+#define NAU8360_PDM_R_SLEN_MASK (0x1 << NAU8360_PDM_R_SLEN_SFT)
+#define NAU8360_PDM_R_SLEN_24 (0x1 << NAU8360_PDM_R_SLEN_SFT)
+#define NAU8360_PDM_R_SLEN_16 (0x0 << NAU8360_PDM_R_SLEN_SFT)
+#define NAU8360_PDM_R_TX_EN_SFT 6
+#define NAU8360_PDM_R_TX_EN (0x1 << NAU8360_PDM_R_TX_EN_SFT)
+#define NAU8360_PDM_R_SLOT_MASK 0x3f
+
+/* NAU8360_R10_I2S_DATA_CTRL3 (0x10) */
+#define NAU8360_RX_ANC_R_SFT 12
+#define NAU8360_RX_ANC_R_MASK (0xf << NAU8360_RX_ANC_R_SFT)
+#define NAU8360_RX_ANC_L_SFT 8
+#define NAU8360_RX_ANC_L_MASK (0xf << NAU8360_RX_ANC_L_SFT)
+#define NAU8360_TDM_LOOPBACK_SFT 7
+#define NAU8360_TDM_LOOPBACK (0x1 << NAU8360_TDM_LOOPBACK_SFT)
+#define NAU8360_TEMP_TX_EN_SFT 6
+#define NAU8360_TEMP_TX_EN (0x1 << NAU8360_TEMP_TX_EN_SFT)
+#define NAU8360_TEMP_SLOT_MASK 0x3f
+
+/* NAU8360_R11_I2S_DATA_CTRL4 (0x11) */
+#define NAU8360_VSNS_R_SLEN_SFT 15
+#define NAU8360_VSNS_R_SLEN_MASK (0x1 << NAU8360_VSNS_R_SLEN_SFT)
+#define NAU8360_VSNS_R_SLEN_16 (0x1 << NAU8360_VSNS_R_SLEN_SFT)
+#define NAU8360_VSNS_R_SLEN_8 (0x0 << NAU8360_VSNS_R_SLEN_SFT)
+#define NAU8360_VSNS_R_TX_EN_SFT 14
+#define NAU8360_VSNS_R_TX_EN (0x1 << NAU8360_VSNS_R_TX_EN_SFT)
+#define NAU8360_VSNS_R_SLOT_SFT 8
+#define NAU8360_VSNS_R_SLOT_MASK (0x3f << NAU8360_VSNS_R_SLOT_SFT)
+#define NAU8360_ISNS_R_SLEN_SFT 7
+#define NAU8360_ISNS_R_SLEN_MASK (0x1 << NAU8360_ISNS_R_SLEN_SFT)
+#define NAU8360_ISNS_R_SLEN_16 (0x1 << NAU8360_ISNS_R_SLEN_SFT)
+#define NAU8360_ISNS_R_SLEN_8 (0x0 << NAU8360_ISNS_R_SLEN_SFT)
+#define NAU8360_ISNS_R_TX_EN_SFT 6
+#define NAU8360_ISNS_R_TX_EN (0x1 << NAU8360_ISNS_R_TX_EN_SFT)
+#define NAU8360_ISNS_R_SLOT_MASK 0x3f
+
+/* NAU8360_R12_PATH_CTRL (0x12) */
+#define NAU8360_SEL_HW1_SFT 9
+#define NAU8360_SEL_HW1_MASK (0x1 << NAU8360_SEL_HW1_SFT)
+#define NAU8360_SEL_HW1_OUT (0x1 << NAU8360_SEL_HW1_SFT)
+#define NAU8360_SEL_HW1_BYP (0x0 << NAU8360_SEL_HW1_SFT)
+#define NAU8360_AUD_SEL_SFT 7
+#define NAU8360_AUD_SEL_MASK (0x1 << NAU8360_AUD_SEL_SFT)
+#define NAU8360_AUD_SEL_SINCOUT (0x1 << NAU8360_AUD_SEL_SFT)
+#define NAU8360_AUD_SEL_SINCBYP (0x0 << NAU8360_AUD_SEL_SFT)
+#define NAU8360_SEL_HW2_SFT 6
+#define NAU8360_SEL_HW2_MASK (0x1 << NAU8360_SEL_HW2_SFT)
+#define NAU8360_SEL_HW2_OUT (0x1 << NAU8360_SEL_HW2_SFT)
+#define NAU8360_SEL_HW2_BYP (0x0 << NAU8360_SEL_HW2_SFT)
+#define NAU8360_DAC_SEL_SFT 5
+#define NAU8360_DAC_SEL_MASK (0x1 << NAU8360_DAC_SEL_SFT)
+#define NAU8360_DAC_SEL_DSP (0x1 << NAU8360_DAC_SEL_SFT)
+#define NAU8360_DAC_SEL_BYP (0x0 << NAU8360_DAC_SEL_SFT)
+#define NAU8360_SEL_HW3_SFT 4
+#define NAU8360_SEL_HW3_MASK (0x1 << NAU8360_SEL_HW3_SFT)
+#define NAU8360_SEL_HW3_OUT (0x1 << NAU8360_SEL_HW3_SFT)
+#define NAU8360_SEL_HW3_BYP (0x0 << NAU8360_SEL_HW3_SFT)
+
+/* NAU8360_R17_I2S0_DATA_CTRL5 (0x17)*/
+#define NAU8360_AEC_L_SLEN_SFT 15
+#define NAU8360_AEC_L_SLEN_MASK (0x1 << NAU8360_AEC_L_SLEN_SFT)
+#define NAU8360_AEC_L_SLEN_32 (0x1 << NAU8360_AEC_L_SLEN_SFT)
+#define NAU8360_AEC_L_SLEN_16 (0x0 << NAU8360_AEC_L_SLEN_SFT)
+#define NAU8360_AEC_L_EN_SFT 14
+#define NAU8360_AEC_L_EN (0x1 << NAU8360_AEC_L_EN_SFT)
+#define NAU8360_AEC_L_SLOT_SFT 8
+#define NAU8360_AEC_L_SLOT_MASK (0x3f << NAU8360_AEC_L_SLOT_SFT)
+#define NAU8360_AEC_R_SLEN_SFT 7
+#define NAU8360_AEC_R_SLEN_MASK (0x1 << NAU8360_AEC_R_SLEN_SFT)
+#define NAU8360_AEC_R_SLEN_32 (0x1 << NAU8360_AEC_R_SLEN_SFT)
+#define NAU8360_AEC_R_SLEN_16 (0x0 << NAU8360_AEC_R_SLEN_SFT)
+#define NAU8360_AEC_R_EN_SFT 6
+#define NAU8360_AEC_R_EN (0x1 << NAU8360_AEC_R_EN_SFT)
+#define NAU8360_AEC_R_SLOT_MASK 0x3f
+
+/* NAU8360_R1A_DSP_CORE_CTRL2 (0x1a) */
+#define NAU8360_DSP_RUNSTALL_SFT 4
+#define NAU8360_DSP_RUNSTALL (0x1 << NAU8360_DSP_RUNSTALL_SFT)
+
+/* NAU8360_R21_VBAT_READOUT (0x21) */
+#define NAU8360_VBAT_BASE 1108
+#define NAU8360_VBAT_STEP 12353
+
+/* NAU8360_R40_CLK_DET_CTRL (0x40) */
+#define NAU8360_APWRUPEN_SFT 15
+#define NAU8360_APWRUPEN (0x1 << NAU8360_APWRUPEN_SFT)
+#define NAU8360_CLKPWRUPEN_SFT 14
+#define NAU8360_CLKPWRUPEN (0x1 << NAU8360_CLKPWRUPEN_SFT)
+#define NAU8360_SRATE_SFT 10
+#define NAU8360_SRATE_MASK (0x7 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_192000 (0x4 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_96000 (0x3 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_48000 (0x2 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_32000 (0x1 << NAU8360_SRATE_SFT)
+#define NAU8360_SRATE_16000 (0x0 << NAU8360_SRATE_SFT)
+#define NAU8360_MCLK_RATE_SFT 7
+#define NAU8360_MCLK_RATE_MASK (0x7 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_24576 (0x4 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_24000 (0x3 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_19200 (0x2 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_12288 (0x1 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_MCLK_RATE_12000 (0x0 << NAU8360_MCLK_RATE_SFT)
+#define NAU8360_FS_MCLK_DET_SFT 6
+#define NAU8360_FS_MCLK_DET (0x1 << NAU8360_FS_MCLK_DET_SFT)
+#define NAU8360_FS_BCLK_DET_SFT 5
+#define NAU8360_FS_BCLK_DET (0x1 << NAU8360_FS_BCLK_DET_SFT)
+
+/* NAU8360_R41_CLK_CTL2 (0x41) */
+#define NAU8360_NOISE_FILTER_THD_SFT 12
+#define NAU8360_NOISE_FILTER_THDT_MASK (0xf << NAU8360_NOISE_FILTER_THD_SFT)
+#define NAU8360_SEL_BLOCK_SIZE_SFT 6
+#define NAU8360_SEL_BLOCK_SIZE_MASK (0x7 << NAU8360_SEL_BLOCK_SIZE_SFT)
+#define NAU8360_EN_ABCLKDET_SFT 3
+#define NAU8360_EN_ABCLKDET (0x1 << NAU8360_EN_ABCLKDET_SFT)
+#define NAU8360_EN_DBCLKDET_SFT 2
+#define NAU8360_EN_DBCLKDET (0x1 << NAU8360_EN_DBCLKDET_SFT)
+#define NAU8360_EN_AMCLKDET_SFT 1
+#define NAU8360_EN_AMCLKDET (0x1 << NAU8360_EN_AMCLKDET_SFT)
+#define NAU8360_EN_DMCLKDET 0x1
+
+/* NAU8360_R46_I2C_DEVICE_ID (0x46) */
+#define NAU8360_REG_SI_REV_MASK 0xff
+#define NAU8360_REG_SI_REV_B 0xf2
+#define NAU8360_REG_SI_REV_C 0xf5
+
+/* NAU8360_R5D_SINC_CFG (0x5d) */
+#define NAU8360_DAC_SINC_OSR_SFT 4
+#define NAU8360_DAC_SINC_OSR_MASK (0x3 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_250 (0x3 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_200 (0x2 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_128 (0x1 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_DAC_SINC_OSR_0 (0x0 << NAU8360_DAC_SINC_OSR_SFT)
+#define NAU8360_IVSENSE_BS_OSR_MASK 0x3
+#define NAU8360_IVSENSE_BS_OSR_25 0x3
+#define NAU8360_IVSENSE_BS_OSR_64 0x2
+#define NAU8360_IVSENSE_BS_OSR_32 0x1
+#define NAU8360_IVSENSE_BS_OSR_16 0x0
+
+/* NAU8360_R60_RST (0x60) */
+#define NAU8360_PD_POR5_SFT 4
+#define NAU8360_PD_POR5_PWD (0x1 << NAU8360_PD_POR5_SFT)
+#define NAU8360_EFUSE_CTRL_SFT 3
+#define NAU8360_EFUSE_CTRL_EN (0x1 << NAU8360_EFUSE_CTRL_SFT)
+
+/* NAU8360_R67_ANALOG_CONTROL_0 (0x67) */
+#define NAU8360_SCP_CLEAR_MODE_SFT 15
+#define NAU8360_SCP_CLEAR_MODE_MASK (0x1 << NAU8360_SCP_CLEAR_MODE_SFT)
+#define NAU8360_SCP_CLEAR_MODE_AUTO (0x1 << NAU8360_SCP_CLEAR_MODE_SFT)
+#define NAU8360_SCP_CLEAR_MODE_MANU (0x0 << NAU8360_SCP_CLEAR_MODE_SFT)
+#define NAU8360_SCP_CLEAR_SFT 14
+#define NAU8360_SCP_CLEAR (0x1 << NAU8360_SCP_CLEAR_SFT)
+#define NAU8360_HV_EN_SFT 13
+#define NAU8360_HV_EN (0x1 << NAU8360_HV_EN_SFT)
+#define NAU8360_ANA_MUTE_SFT 8
+#define NAU8360_ANA_MUTE (0x1 << NAU8360_ANA_MUTE_SFT)
+#define NAU8360_MOD_GAIN_SFT 5
+#define NAU8360_MOD_GAIN_MASK (0x7 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_20DB (0x7 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_18DB (0x6 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_16DB (0x5 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_14DB (0x4 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_12DB (0x3 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_10DB (0x2 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_8DB (0x1 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_MOD_GAIN_6DB (0x0 << NAU8360_MOD_GAIN_SFT)
+#define NAU8360_CM_COMP_SFT 4
+#define NAU8360_CM_COMP_EN (0x1 << NAU8360_CM_COMP_SFT)
+#define NAU8360_V_PBTL_SFT 3
+#define NAU8360_V_PBTL_EN (0x1 << NAU8360_V_PBTL_SFT)
+
+/* NAU8360_R68_ANALOG_CONTROL_1 (0x68) */
+#define NAU8360_TRIMSCR_SFT 6
+#define NAU8360_TRIMSCR_MASK (0x3 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_HIGH (0x3 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_MHIGH (0x2 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_MLOW (0x1 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_TRIMSCR_LOW (0x0 << NAU8360_TRIMSCR_SFT)
+#define NAU8360_DTX_SFT 5
+#define NAU8360_DTX_EN (0x1 << NAU8360_DTX_SFT)
+#define NAU8360_DRVCTL_SEGL_SFT 4
+#define NAU8360_DRVCTL_SEGL_FULL (0x1 << NAU8360_DRVCTL_SEGL_SFT)
+#define NAU8360_DRVCTL_SEGR_SFT 2
+#define NAU8360_DRVCTL_SEGR_FULL (0x1 << NAU8360_DRVCTL_SEGR_SFT)
+#define NAU8360_SCP_TEST_SFT 1
+#define NAU8360_SCP_TEST (0x1 << NAU8360_SCP_TEST_SFT)
+#define NAU8360_SEG_AUTO_CTRL_EN 0x1
+
+/* NAU8360_R69_ANALOG_CONTROL_3 (0x69) */
+#define NAU8360_ANA_STATUS_OTP_SFT 8
+#define NAU8360_ANA_STATUS_OTP_MASK (0x1 << NAU8360_ANA_STATUS_OTP_SFT)
+#define NAU8360_ANA_STATUS_OVLO_SFT 7
+#define NAU8360_ANA_STATUS_OVLO_MASK (0x1 << NAU8360_ANA_STATUS_OVLO_SFT)
+#define NAU8360_ANA_STATUS_UVLO_SFT 6
+#define NAU8360_ANA_STATUS_UVLO_MASK (0x1 << NAU8360_ANA_STATUS_UVLO_SFT)
+#define NAU8360_ANA_STATUS_SCP_SFT 4
+#define NAU8360_ANA_STATUS_SCP_MASK (0x1 << NAU8360_ANA_STATUS_SCP_SFT)
+#define NAU8360_ANA_STATUS_EN_V2I_SFT 3
+#define NAU8360_ANA_STATUS_EN_V2I_MASK (0x1 << NAU8360_ANA_STATUS_EN_V2I_SFT)
+#define NAU8360_ANA_STATUS_FAULT_SFT 2
+#define NAU8360_ANA_STATUS_FAULT_MASK (0x1 << NAU8360_ANA_STATUS_FAULT_SFT)
+#define NAU8360_ANA_STATUS_DC_DET_SFT 1
+#define NAU8360_ANA_STATUS_DC_DET_MASK (0x1 << NAU8360_ANA_STATUS_DC_DET_SFT)
+#define NAU8360_ANA_STATUS_EN_ALL_MASK 0x1
+
+/* NAU8360_R6A_SARADC_CFG0 (0x6a) */
+#define NAU8360_SARADC_SFT 15
+#define NAU8360_SARADC_EN (0x1 << NAU8360_SARADC_SFT)
+#define NAU8360_RESETB_SARADC_SFT 14
+#define NAU8360_RESETB_SARADC_MASK (0x1 << NAU8360_RESETB_SARADC_SFT)
+#define NAU8360_RESETB_SARADC_NOR (0x1 << NAU8360_RESETB_SARADC_SFT)
+#define NAU8360_RESETB_SARADC_RST (0x0 << NAU8360_RESETB_SARADC_SFT)
+#define NAU8360_SARADC_CAL_SFT 13
+#define NAU8360_SARADC_CAL (0x1 << NAU8360_SARADC_CAL_SFT)
+#define NAU8360_SARADC_DIV_SFT 12
+#define NAU8360_SARADC_DIV (0x1 << NAU8360_SARADC_DIV_SFT)
+#define NAU8360_SARADC_CALSEL_SFT 9
+#define NAU8360_SARADC_CALSEL_MASK (0x7 << NAU8360_SARADC_CALSEL_SFT)
+#define NAU8360_TRIMSMPL_SFT 3
+#define NAU8360_TRIMSMPL_MASK (0x7 << NAU8360_TRIMSMPL_SFT)
+#define NAU8360_SAR_INPUT_CH_SEL_SFT 2
+#define NAU8360_SAR_INPUT_CH_SEL_MASK (0x1 << NAU8360_SAR_INPUT_CH_SEL_SFT)
+#define NAU8360_VBAT_AVG_SFT 1
+#define NAU8360_VBAT_AVG_EN (0x1 << NAU8360_VBAT_AVG_SFT)
+#define NAU8360_VTEMP_AVG_EN 0x1
+
+/* NAU8360_R6B_SARADC_CFG1 (0x6b) */
+#define NAU8360_VTEMP_SFT 15
+#define NAU8360_VTEMP_EN (0x1 << NAU8360_VTEMP_SFT)
+#define NAU8360_VBATDIV_SFT 14
+#define NAU8360_VBATDIV_EN (0x1 << NAU8360_VBATDIV_SFT)
+#define NAU8360_VREF_SFT 10
+#define NAU8360_VREF_EN (0x1 << NAU8360_VREF_SFT)
+#define NAU8360_PRELOAD_VREF_SFT 9
+#define NAU8360_PRELOAD_VREF_EN (0x1 << NAU8360_PRELOAD_VREF_SFT)
+#define NAU8360_VTEMP_AVG_N_SFT 2
+#define NAU8360_VTEMP_AVG_N_MASK (0x3 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_512 (0x3 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_128 (0x2 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_32 (0x1 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VTEMP_AVG_N_1 (0x0 << NAU8360_VTEMP_AVG_N_SFT)
+#define NAU8360_VBAT_AVG_N_MASK 0x3
+#define NAU8360_VBAT_AVG_N_512 0x3
+#define NAU8360_VBAT_AVG_N_128 0x2
+#define NAU8360_VBAT_AVG_N_32 0x1
+#define NAU8360_VBAT_AVG_N_1 0
+
+/* NAU8360_R6C_IVSNS_CFG0 (0x6c) */
+#define NAU8360_PD_VSNS_R_SFT 15
+#define NAU8360_PD_VSNS_R_PMD (0x1 << NAU8360_PD_VSNS_R_SFT)
+#define NAU8360_PD_VSNS_L_SFT 14
+#define NAU8360_PD_VSNS_L_PMD (0x1 << NAU8360_PD_VSNS_L_SFT)
+#define NAU8360_PD_ISNS_R_SFT 13
+#define NAU8360_PD_ISNS_R_PMD (0x1 << NAU8360_PD_ISNS_R_SFT)
+#define NAU8360_PD_ISNS_L_SFT 12
+#define NAU8360_PD_ISNS_L_PMD (0x1 << NAU8360_PD_ISNS_L_SFT)
+#define NAU8360_RESET_SC_VADC_R_SFT 11
+#define NAU8360_RESET_SC_VADC_R_RST (0x1 << NAU8360_RESET_SC_VADC_R_SFT)
+#define NAU8360_RESET_SC_VADC_L_SFT 10
+#define NAU8360_RESET_SC_VADC_L_RST (0x1 << NAU8360_RESET_SC_VADC_L_SFT)
+#define NAU8360_RESET_SC_IADC_R_SFT 9
+#define NAU8360_RESET_SC_IADC_R_RST (0x1 << NAU8360_RESET_SC_IADC_R_SFT)
+#define NAU8360_RESET_SC_IADC_L_SFT 8
+#define NAU8360_RESET_SC_IADC_L_RST (0x1 << NAU8360_RESET_SC_IADC_L_SFT)
+#define NAU8360_PBTL_ISENE_LR_SFT 7
+#define NAU8360_PBTL_ISENE_LR_MASK (0x1 << NAU8360_PBTL_ISENE_LR_SFT)
+#define NAU8360_PBTL_ISENE_LR_ILR (0x1 << NAU8360_PBTL_ISENE_LR_SFT)
+#define NAU8360_PBTL_ISENE_LR_I2R (0x0 << NAU8360_PBTL_ISENE_LR_SFT)
+#define NAU8360_PD_LDO_SDM_IVSNS_SFT 6
+#define NAU8360_PD_LDO_SDM_IVSNS_PMD (0x1 << NAU8360_PD_LDO_SDM_IVSNS_SFT)
+
+/* NAU8360_R6D_IVSNS_CFG1 (0x6d) */
+#define NAU8360_PD_V2I_IVSNS_SFT 13
+#define NAU8360_PD_V2I_IVSNS_MASK (0x1 << NAU8360_PD_V2I_IVSNS_SFT)
+#define NAU8360_PD_VREF_SDM_IVSNS_SFT 12
+#define NAU8360_PD_VREF_SDM_IVSNS_MASK (0x1 << NAU8360_PD_VREF_SDM_IVSNS_SFT)
+#define NAU8360_VSNS_PGA_CUR_SET_SFT 10
+#define NAU8360_VSNS_PGA_CUR_SET_MASK (0x3 << NAU8360_VSNS_PGA_CUR_SET_SFT)
+#define NAU8360_VSNS_GAIN_SFT 8
+#define NAU8360_VSNS_GAIN_MASK (0x3 << NAU8360_VSNS_GAIN_SFT)
+#define NAU8360_VREF_CUR_SFT 6
+#define NAU8360_VREF_CUR_MASK (0x3 << NAU8360_VREF_CUR_SFT)
+#define NAU8360_ISNS_PGA_CUR_SFT 4
+#define NAU8360_ISNS_PGA_CUR_MASK (0x3 << NAU8360_ISNS_PGA_CUR_SFT)
+#define NAU8360_ISNS_GAIN_SFT 2
+#define NAU8360_ISNS_GAIN_MASK (0x3 << NAU8360_ISNS_GAIN_SFT)
+#define NAU8360_SDM_DELAY_TRIM_MASK 0x3
+
+/* NAU8360_R6E_DAC_CFG0 (0x6e) */
+#define NAU8360_PD_DACL_SFT 11
+#define NAU8360_PD_DACL_DIS (0x1 << NAU8360_PD_DACL_SFT)
+#define NAU8360_PD_DACR_SFT 10
+#define NAU8360_PD_DACR_DIS (0x1 << NAU8360_PD_DACR_SFT)
+#define NAU8360_PD_CHOP_DAC_SFT 9
+#define NAU8360_PD_CHOP_DAC_MASK (0x1 << NAU8360_PD_CHOP_DAC_SFT)
+#define NAU8360_DAC_RES_SFT 7
+#define NAU8360_DAC_RES_MASK (0x1 << NAU8360_DAC_RES_SFT)
+#define NAU8360_DAC_CUR_SFT 6
+#define NAU8360_DAC_CUR_MASK (0x1 << NAU8360_DAC_CUR_SFT)
+#define NAU8360_DAC_CUR_3_2DB (0x1 << NAU8360_DAC_CUR_SFT)
+#define NAU8360_DAC_CUR_0DB (0x0 << NAU8360_DAC_CUR_SFT)
+#define NAU8360_DAC_BUFOPCUR_SFT 4
+#define NAU8360_DAC_BUFOPCUR_MASK (0x3 << NAU8360_DAC_BUFOPCUR_SFT)
+#define NAU8360_DAC_IVOPCUR_SFT 2
+#define NAU8360_DAC_IVOPCUR_MASK (0x3 << NAU8360_DAC_IVOPCUR_SFT)
+#define NAU8360_DAC_LPFOP_CUR_MASK 0x3
+
+/* NAU8360_R71_CLK_DIV_CFG (0x71) */
+#define NAU8360_IVSNS_CLK_INV_SEL_SFT 10
+#define NAU8360_IVSNS_CLK_INV_SEL_MASK (0x1 << NAU8360_IVSNS_CLK_INV_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_SFT 7
+#define NAU8360_CLK_ANA_SEL_MASK (0x3 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_PLL (0x2 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_BCLK (0x1 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_CLK_ANA_SEL_MCLK (0x0 << NAU8360_CLK_ANA_SEL_SFT)
+#define NAU8360_IVSNS_CLK_DIV_SFT 4
+#define NAU8360_IVSNS_CLK_DIV_MASK (0x7 << NAU8360_IVSNS_CLK_DIV_SFT)
+#define NAU8360_DAC_OUTPUT_EDGE_SFT 3
+#define NAU8360_DAC_OUTPUT_EDGE_MASK (0x1 << NAU8360_DAC_OUTPUT_EDGE_SFT)
+#define NAU8360_DAC_CLK_DIV_SFT 1
+#define NAU8360_DAC_CLK_DIV_MASK (0x3 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_8 (0x3 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_4 (0x2 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_2 (0x1 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CLK_DIV_1 (0x0 << NAU8360_DAC_CLK_DIV_SFT)
+#define NAU8360_DAC_CHOP_CLK_DIV_MASK 0x1
+#define NAU8360_DAC_CHOP_CLK_DIV_32 0x1
+#define NAU8360_DAC_CHOP_CLK_DIV_16 0x0
+
+/* NAU8360_R72_PLL_CFG0 (0x72) */
+#define NAU8360_PLLOUT_DIV_SFT 10
+#define NAU8360_PLLOUT_DIV_MAX 0x1f
+#define NAU8360_PLLOUT_DIV_MASK (0x1f << NAU8360_PLLOUT_DIV_SFT)
+#define NAU8360_PLLOUT_DIV_BYPASS (0x0 << NAU8360_PLLOUT_DIV_SFT)
+#define NAU8360_PLL_CLK_SEL_SFT 8
+#define NAU8360_PLL_CLK_SEL_MASK (0x3 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PLL_CLK_SEL_HIRC (0x3 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PLL_CLK_SEL_BCLK (0x1 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PLL_CLK_SEL_MCLK (0x0 << NAU8360_PLL_CLK_SEL_SFT)
+#define NAU8360_PD_PLL_SFT 7
+#define NAU8360_PD_PLL_MASK (0x1 << NAU8360_PD_PLL_SFT)
+#define NAU8360_PD_PLL_DIS (0x1 << NAU8360_PD_PLL_SFT)
+#define NAU8360_PD_PLL_EN (0x0 << NAU8360_PD_PLL_SFT)
+
+/* NAU8360_R73_PLL_CFG1 (0x73) */
+#define NAU8360_RSEL_SFT 13
+#define NAU8360_RSEL_MASK (0x3 << NAU8360_RSEL_SFT)
+#define NAU8360_MSEL_SFT 8
+#define NAU8360_MSEL_MASK (0x1f << NAU8360_MSEL_SFT)
+#define NAU8360_NSEL_MASK 0xff
+
+/* NAU8360_R74_PLL_CFG2 (0x74) */
+#define NAU8360_XSEL_MASK 0xfff
+
+/* NAU8360_R77_SOFT_SD (0x77) */
+#define NAU8360_SOFT_SD 0x55AA
+#define NAU8360_SOFT_SD_EN 0
+
+/* NAU8360_R78_PD_SW_DLDO (0x78) */
+#define NAU8360_PD_SW_DLDO 0xAA55
+#define NAU8360_PD_SW_DLDO_EN 0
+
+/* NAU8360_R7A_DAC_TRIM_CFG2 (0x7a) */
+#define NAU8360_DAC_TEMP_SLOPE_SFT 8
+#define NAU8360_DAC_TEMP_SLOPE_MASK (0x7f << NAU8360_DAC_TEMP_SLOPE_SFT)
+
+/* NAU8360_R7B_IVSNS_TRIM_CFG (0x7b) */
+#define NAU8360_ISNS_TEMP_SLOPE_SFT 8
+#define NAU8360_ISNS_TEMP_SLOPE_MASK (0x7f << NAU8360_ISNS_TEMP_SLOPE_SFT)
+#define NAU8360_VSNS_TEMP_SLOPE_MASK 0x7f
+
+/* NAU8360_R7C_MISC_TRIM_CFG (0x7c) */
+#define NAU8360_DAC_OS_SB_SFT 13
+#define NAU8360_DAC_OS_SB_SFT_MASK (0x7 << NAU8360_DAC_OS_SB_SFT)
+#define NAU8360_DAC_GAIN_SB_SFT 11
+#define NAU8360_DAC_GAIN_SB_MASK (0x3 << NAU8360_DAC_GAIN_SB_SFT)
+#define NAU8360_DAC_TEMP_SB_SFT 9
+#define NAU8360_DAC_TEMP_SB_MASK (0x3 << NAU8360_DAC_TEMP_SB_SFT)
+#define NAU8360_ISNS_TEMP_SB_SFT 6
+#define NAU8360_ISNS_TEMP_SB_MASK (0x7 << NAU8360_ISNS_TEMP_SB_SFT)
+#define NAU8360_VSNS_TEMP_SB_SFT 4
+#define NAU8360_VSNS_TEMP_SB_MASK (0x3 << NAU8360_VSNS_TEMP_SB_SFT)
+#define NAU8360_ISNS_GAIN_SB_SFT 2
+#define NAU8360_ISNS_GAIN_SB_MASK (0x3 << NAU8360_ISNS_GAIN_SB_SFT)
+#define NAU8360_VSNS_GAIN_SB_MASK 0x3
+
+/* NAU8360_R7E_CLK_GATED_EN (0x7e)*/
+#define NAU8360_CLK_GATED_EN 0xa5a5
+#define NAU8360_CLK_GATED_DIS 0
+
+/* NAU8360_R86_HW3_CTL0 (0x86) */
+#define NAU8360_HW3_DRAM_CLR_SFT 14
+#define NAU8360_HW3_DRAM_CLR (0x1 << NAU8360_HW3_DRAM_CLR_SFT)
+#define NAU8360_HW3_STALL_SFT 13
+#define NAU8360_HW3_STALL (0x1 << NAU8360_HW3_STALL_SFT)
+
+/* NAU8360_R8C_HW3_CTL6 (0x8c)*/
+#define NAU8360_HW3_DROOP_SFT 2
+#define NAU8360_HW3_DROOP_MASK (0x1 << NAU8360_HW3_DROOP_SFT)
+#define NAU8360_HW3_DROOP_192K (0x1 << NAU8360_HW3_DROOP_SFT)
+#define NAU8360_HW3_DROOP_96K (0x0 << NAU8360_HW3_DROOP_SFT)
+#define NAU8360_HW3_FS_MASK 0x1
+#define NAU8360_HW3_FS_192K 0x1
+#define NAU8360_HW3_FS_96K 0x0
+
+/* NAU8360_R8F_HW3_CTL9 (0x8f)*/
+#define NAU8360_HW3_CH_MUTE_SFT 8
+#define NAU8360_HW3_CH_MUTE (0x1 << NAU8360_HW3_CH_MUTE_SFT)
+#define NAU8360_HW3_ZERO_THD_MASK 0xff
+
+/* NAU8360_R90_HW2_CTL0 (0x90) */
+#define NAU8360_HW2_DRAM_CLR_SFT 14
+#define NAU8360_HW2_DRAM_CLR (0x1 << NAU8360_HW2_DRAM_CLR_SFT)
+#define NAU8360_HW2_STALL_SFT 13
+#define NAU8360_HW2_STALL (0x1 << NAU8360_HW2_STALL_SFT)
+
+/* NAU8360_R96_HW2_CTL6 (0x96) */
+#define NAU8360_HW1_ANC_EN_SFT 15
+#define NAU8360_HW1_ANC_EN (0x1 << NAU8360_HW1_ANC_EN_SFT)
+#define NAU8360_HW2_DROOP_SEL_SFT 3
+#define NAU8360_HW2_DROOP_SEL_MASK (0x1 << NAU8360_HW2_DROOP_SEL_SFT)
+#define NAU8360_HW2_DROOP_SEL_LARGE (0x1 << NAU8360_HW2_DROOP_SEL_SFT)
+#define NAU8360_HW2_DROOP_SEL_SMALL (0x0 << NAU8360_HW2_DROOP_SEL_SFT)
+#define NAU8360_HW2_DROOP_EN_SFT 2
+#define NAU8360_HW2_DROOP_EN (0x1 << NAU8360_HW2_DROOP_EN_SFT)
+#define NAU8360_HW2_LATENCY_SFT 1
+#define NAU8360_HW2_LATENCY_MASK (0x1 << NAU8360_HW2_LATENCY_SFT)
+#define NAU8360_HW2_LATENCY_LOW (0x1 << NAU8360_HW2_LATENCY_SFT)
+#define NAU8360_HW2_LATENCY_NOR (0x0 << NAU8360_HW2_LATENCY_SFT)
+#define NAU8360_HW2_FS_MASK 0x1
+#define NAU8360_HW2_FS_192K 0x1
+#define NAU8360_HW2_FS_96K 0x0
+
+/* NAU8360_R99_HW2_CTL9 (0x99) */
+#define NAU8360_HW2_VSR_SFT 9
+#define NAU8360_HW2_VSR_MASK (0x3 << NAU8360_HW2_VSR_SFT)
+#define NAU8360_HW2_CH_MUTE_SFT 8
+#define NAU8360_HW2_CH_MUTE (0x1 << NAU8360_HW2_CH_MUTE_SFT)
+#define NAU8360_HW2_ZERO_THD_MASK 0xff
+
+/* NAU8360_R9C_HW1_CTL2 (0x9c) */
+#define NAU8360_MUTE_INTRVL_SFT 11
+#define NAU8360_MUTE_INTRVL_MASK (0x3 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_MUTE_INTRVL_699MS (0x3 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_MUTE_INTRVL_466MS (0x2 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_MUTE_INTRVL_233MS (0x1 << NAU8360_MUTE_INTRVL_SFT)
+#define NAU8360_HW1_CH_MUTE_SFT 8
+#define NAU8360_HW1_CH_MUTE (0x1 << NAU8360_HW1_CH_MUTE_SFT)
+#define NAU8360_HW1_ZERO_THD_MASK 0xff
+
+/* NAU8360_R9D_PEQ_CTL (0x9d) */
+#define NAU8360_PEQ_BAND_SFT 12
+#define NAU8360_PEQ_BAND_MASK (0xf << NAU8360_PEQ_BAND_SFT)
+#define NAU8360_HW1_MEM_TEST_SFT 2
+#define NAU8360_HW1_MEM_TEST (0x1 << NAU8360_HW1_MEM_TEST_SFT)
+#define NAU8360_HW1_MEM_CLEAR_SFT 1
+#define NAU8360_HW1_MEM_CLEAR (0x1 << NAU8360_HW1_MEM_CLEAR_SFT)
+#define NAU8360_PEQ_STALL 0x1
+
+/* NAU8360_RA4_ANA_REG_0 (0xa4) */
+#define NAU8360_MSEL_SAW_SFT 8
+#define NAU8360_MSEL_SAW_MASK (0xff << NAU8360_MSEL_SAW_SFT)
+#define NAU8360_FTRIM_PWM_SFT 6
+#define NAU8360_FTRIM_PWM_MASK (0x3 << NAU8360_MSEL_SAW_SFT)
+#define NAU8360_SEL_STCLK_SFT 5
+#define NAU8360_SEL_STCLK_MASK (0x1 << NAU8360_SEL_STCLK_SFT)
+#define NAU8360_SEL_STCLK_IVCLK (0x1 << NAU8360_SEL_STCLK_SFT)
+#define NAU8360_SEL_STCLK_FREERUN (0x0 << NAU8360_SEL_STCLK_SFT)
+#define NAU8360_NSEL_SAW_MASK 0x1f
+
+/* NAU8360_RA5_ANA_REG_1 (0xa5) */
+#define NAU8360_ICP_SAW_SET_SFT 14
+#define NAU8360_ICP_SAW_SET_MASK (0x3 << NAU8360_ICP_SAW_SET_SFT)
+#define NAU8360_VSAW_LV_SFT 12
+#define NAU8360_VSAW_LV_MASK (0x3 << NAU8360_VSAW_LV_SFT)
+#define NAU8360_KVCO_SAW_SFT 10
+#define NAU8360_KVCO_SAW_MASK (0x3 << NAU8360_KVCO_SAW_SFT)
+#define NAU8360_TRIM_LPF_SFT 8
+#define NAU8360_TRIM_LPF_MASK (0x3 << NAU8360_TRIM_LPF_SFT)
+#define NAU8360_SAW_PLL_SFT 7
+#define NAU8360_SAW_PLL_MASK (0x1 << NAU8360_SAW_PLL_SFT)
+#define NAU8360_SAW_PLL_NOR (0x1 << NAU8360_SAW_PLL_SFT)
+#define NAU8360_SAW_PLL_RST (0x0 << NAU8360_SAW_PLL_SFT)
+#define NAU8360_CLASSD_SHT_SFT 6
+#define NAU8360_CLASSD_SHT_IN (0x1 << NAU8360_CLASSD_SHT_SFT)
+#define NAU8360_HVEN_SYNC_SFT 5
+#define NAU8360_HVEN_SYNC_SAW (0x1 << NAU8360_HVEN_SYNC_SFT)
+#define NAU8360_SYNC_SAW_CLK_SFT 4
+#define NAU8360_SYNC_SAW_CLK_INVT (0x1 << NAU8360_SYNC_SAW_CLK_SFT)
+#define NAU8360_DEPOP_CAP_SFT 2
+#define NAU8360_DEPOP_CAP_MASK (0x3 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_48PF (0x3 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_36PF (0x2 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_24PF (0x1 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_DEPOP_CAP_12PF (0x0 << NAU8360_DEPOP_CAP_SFT)
+#define NAU8360_SW_BG_IO_SFT 1
+#define NAU8360_SW_BG_IO_CONN (0x1 << NAU8360_SW_BG_IO_SFT)
+#define NAU8360_SW_VPTC_IO_EN 0x1
+
+/* NAU8360 VBAT Range Thresholds */
+#define NAU8360_VBAT_MIN 8
+#define NAU8360_VBAT_MID_THRES 13
+#define NAU8360_VBAT_HIGH_THRES 19
+#define NAU8360_VBAT_MAX 24
+
+/* NAU8360_R100_LEFT_BIQ0_COE (0x100)
+ * NAU8360_R200_RIGHT_BIQ0_COE (0x200)
+ */
+#define NAU8360_TOT_BAND_PER_CH 15
+#define NAU8360_TOT_BAND_COE 10
+#define NAU8360_TOT_BAND_COE_RANGE (NAU8360_TOT_BAND_COE + 2)
+#define NAU8360_PEQ_REG_WIDTH (NAU8360_TOT_BAND_COE - 1)
+
+/* DSP stereo core */
+#define NAU8360_DSP_CORE_NUM 2
+#define NAU8360_DSP_ADDR_BYNAME(x) \
+ (strstr((x), "Left") ? NAU8360_RF000_DSP_COMM : NAU8360_RF002_DSP_COMM)
+
+#define NAU8360_I2C_REG_LEN 2
+#define NAU8360_I2C_RXBUF_LEN 4
+#define NAU8360_I2C_TXBUF_LEN 6
+#define NAU8360_CODEC_DAI "nau8360-hifi"
+
+/* clock source */
+enum {
+ NAU8360_CLK_SRC_MCLK,
+ NAU8360_CLK_SRC_PLL,
+ NAU8360_CLK_SRC_ICLK,
+ NAU8360_CLK_SRC_BCLK,
+};
+
+/* clock target */
+enum {
+ NAU8360_CLK_ID_DIG, /* DIG_SYS + DSP + HW */
+ NAU8360_CLK_ID_ANA, /* IV + DAC */
+ NAU8360_CLK_ID_INT, /* HIRC48M standby */
+};
+
+enum {
+ NAU8360_MCLK_FS_RATIO_250 = 250,
+ NAU8360_MCLK_FS_RATIO_256 = 256,
+ NAU8360_MCLK_FS_RATIO_400 = 400,
+ NAU8360_MCLK_FS_RATIO_500 = 500,
+ NAU8360_MCLK_FS_RATIO_512 = 512,
+};
+
+enum {
+ NAU8360_PEQ_BAND_8 = 8,
+ NAU8360_PEQ_BAND_12 = 12,
+ NAU8360_PEQ_BAND_15 = 15,
+};
+
+#define NAU8360_TDM_MAX_CHAN 8
+#define TDM_SLOT_NONE 255
+
+enum {
+ NAU8360_TDM_DACL,
+ NAU8360_TDM_DACR,
+ NAU8360_TDM_ANCL,
+ NAU8360_TDM_ANCR,
+ NAU8360_TDM_RXN,
+};
+
+enum {
+ NAU8360_TDM_AECL,
+ NAU8360_TDM_AECR,
+ NAU8360_TDM_ISNSL,
+ NAU8360_TDM_ISNSR,
+ NAU8360_TDM_VSNSL,
+ NAU8360_TDM_VSNSR,
+ NAU8360_TDM_TJ,
+ NAU8360_TDM_VBAT,
+ NAU8360_TDM_TXN,
+};
+
+/* DAC Source Path*/
+enum {
+ NAU8360_DAC_SRC_HW1 = 0,
+ NAU8360_DAC_SRC_DSP,
+};
+
+/* PLL Source */
+enum {
+ NAU8360_PLL_MCLK,
+ NAU8360_PLL_BCLK,
+ NAU8360_PLL_INTERNAL,
+};
+
+struct nau8360_pll {
+ int src;
+ int input;
+ int output;
+ int msel;
+ int rsel;
+ int nsel;
+ int xsel;
+};
+
+struct nau8360 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex lock;
+ struct snd_soc_dapm_context *dapm;
+ int sys_clk;
+ int anc_enable;
+ int pbtl_enable;
+ int dac_cur_enable;
+ int kcs_setup_size;
+ struct nau8360_pll pll;
+ u32 tdm_tx_func_slot[NAU8360_TDM_TXN];
+ u32 tdm_rx_func_slot[NAU8360_TDM_RXN];
+ u8 i2c_read_reg[NAU8360_I2C_REG_LEN] ____cacheline_aligned;
+ u8 i2c_read_buf[NAU8360_I2C_RXBUF_LEN] ____cacheline_aligned;
+ u8 i2c_write_buf[NAU8360_I2C_TXBUF_LEN] ____cacheline_aligned;
+ struct work_struct load_fw_work;
+ const char *dsp_firmware[NAU8360_DSP_CORE_NUM];
+ bool load_fw_done;
+};
+
+#endif /* __NAU8360_H__ */
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
index fefbd5722c00..d4211a76b096 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -623,12 +623,23 @@ static int nau8540_dai_trigger(struct snd_pcm_substream *substream,
return ret;
}
+static const u64 nau8540_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops nau8540_dai_ops = {
.startup = nau8540_dai_startup,
.hw_params = nau8540_hw_params,
.set_fmt = nau8540_set_fmt,
.set_tdm_slot = nau8540_set_tdm_slot,
.trigger = nau8540_dai_trigger,
+ .auto_selectable_formats = &nau8540_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000
@@ -897,9 +908,7 @@ static int __maybe_unused nau8540_resume(struct snd_soc_component *component)
struct nau8540 *nau8540 = snd_soc_component_get_drvdata(component);
regcache_cache_only(nau8540->regmap, false);
- regcache_sync(nau8540->regmap);
-
- return 0;
+ return regcache_sync(nau8540->regmap);
}
static const struct snd_soc_component_driver nau8540_component_driver = {
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
index a050387dd485..eb3e93380678 100644
--- a/sound/soc/codecs/nau8810.c
+++ b/sound/soc/codecs/nau8810.c
@@ -817,11 +817,23 @@ static int nau8810_set_bias_level(struct snd_soc_component *component,
#define NAU8810_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 nau8810_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops nau8810_ops = {
.hw_params = nau8810_pcm_hw_params,
.set_fmt = nau8810_set_dai_fmt,
.set_sysclk = nau8810_set_sysclk,
.set_pll = nau8810_set_pll,
+ .auto_selectable_formats = &nau8810_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver nau8810_dai = {
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index c45c5b864ea4..ecadbaee1037 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -981,11 +981,22 @@ static int nau8821_digital_mute(struct snd_soc_dai *dai, int mute,
NAU8821_R31_MUTE_CTRL, NAU8821_DAC_SOFT_MUTE, val);
}
+static const u64 nau8821_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops nau8821_dai_ops = {
.startup = nau8821_dai_startup,
.hw_params = nau8821_hw_params,
.set_fmt = nau8821_set_dai_fmt,
.mute_stream = nau8821_digital_mute,
+ .auto_selectable_formats = &nau8821_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
index 830164e991a7..db1931d7b901 100644
--- a/sound/soc/codecs/nau8822.c
+++ b/sound/soc/codecs/nau8822.c
@@ -1026,12 +1026,24 @@ static int nau8822_set_bias_level(struct snd_soc_component *component,
#define NAU8822_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 nau8822_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops nau8822_dai_ops = {
.hw_params = nau8822_hw_params,
.mute_stream = nau8822_mute,
.set_fmt = nau8822_set_dai_fmt,
.set_sysclk = nau8822_set_dai_sysclk,
.set_pll = nau8822_set_pll,
+ .auto_selectable_formats = &nau8822_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1059,10 +1071,14 @@ static int nau8822_suspend(struct snd_soc_component *component)
{
struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret;
snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
- regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+ ret = regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+ if (ret)
+ return ret;
+ regcache_cache_only(nau8822->regmap, true);
regcache_mark_dirty(nau8822->regmap);
return 0;
@@ -1072,7 +1088,9 @@ static int nau8822_resume(struct snd_soc_component *component)
{
struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- int ret = regulator_bulk_enable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+ int ret;
+
+ ret = regulator_bulk_enable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
if (ret) {
dev_err(component->dev,
@@ -1082,7 +1100,10 @@ static int nau8822_resume(struct snd_soc_component *component)
fsleep(100);
- regcache_sync(nau8822->regmap);
+ regcache_cache_only(nau8822->regmap, false);
+ ret = regcache_sync(nau8822->regmap);
+ if (ret)
+ return ret;
snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_STANDBY);
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
index 426a488ff2a3..853eb9472af9 100644
--- a/sound/soc/codecs/nau8824.c
+++ b/sound/soc/codecs/nau8824.c
@@ -1580,11 +1580,22 @@ static const struct snd_soc_component_driver nau8824_component_driver = {
.endianness = 1,
};
+static const u64 nau8824_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops nau8824_dai_ops = {
.startup = nau8824_dai_startup,
.hw_params = nau8824_hw_params,
.set_fmt = nau8824_set_fmt,
.set_tdm_slot = nau8824_set_tdm_slot,
+ .auto_selectable_formats = &nau8824_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index ae83b778bf93..63d927f31409 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -345,10 +345,10 @@ static u32 nau8825_intlog10_dec3(u32 value)
/**
* nau8825_xtalk_sidetone - computes cross talk suppression sidetone gain.
*
- * @sig_org: orignal signal level
+ * @sig_org: original signal level
* @sig_cros: cross talk signal level
*
- * The orignal and cross talk signal vlues need to be characterized.
+ * The original and cross talk signal values need to be characterized.
* Once these values have been characterized, this sidetone value
* can be converted to decibel with the equation below.
* sidetone = 20 * log (original signal level / crosstalk signal level)
@@ -671,11 +671,11 @@ static void nau8825_xtalk_imm_stop(struct nau8825 *nau8825)
* Thus, the measurement function has four states to complete whole sequence.
* 1. Prepare state : Prepare the resource for detection and transfer to HPR
* IMM stat to make JKR1(HPR) impedance measure.
- * 2. HPR IMM state : Read out orignal signal level of JKR1(HPR) and transfer
+ * 2. HPR IMM state : Read out original signal level of JKR1(HPR) and transfer
* to HPL IMM state to make JKTIP(HPL) impedance measure.
* 3. HPL IMM state : Read out cross talk signal level of JKTIP(HPL) and
* transfer to IMM state to determine suppression sidetone gain.
- * 4. IMM state : Computes cross talk suppression sidetone gain with orignal
+ * 4. IMM state : Computes cross talk suppression sidetone gain with original
* and cross talk signal level. Apply this gain and then restore codec
* configuration. Then transfer to Done state for ending.
*/
@@ -724,8 +724,8 @@ static void nau8825_xtalk_measure(struct nau8825 *nau8825)
nau8825->xtalk_state = NAU8825_XTALK_IMM;
break;
case NAU8825_XTALK_IMM:
- /* In impedance measure state, the orignal and cross talk
- * signal level vlues are ready. The side tone gain is deter-
+ /* In impedance measure state, the original and cross talk
+ * signal level values are ready. The side tone gain is deter-
* mined with these signal level. After all, restore codec
* configuration.
*/
@@ -771,7 +771,7 @@ static void nau8825_xtalk_work(struct work_struct *work)
static void nau8825_xtalk_cancel(struct nau8825 *nau8825)
{
- /* If the crosstalk is eanbled and the process is on going,
+ /* If the crosstalk is enabled and the process is on going,
* the driver forces to cancel the crosstalk task and
* restores the configuration to original status.
*/
@@ -1303,7 +1303,7 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
NAU8825_CLK_ADC_SRC_MASK,
osr->clk_src << NAU8825_CLK_ADC_SRC_SFT);
- /* make BCLK and LRC divde configuration if the codec as master. */
+ /* make BCLK and LRC divide configuration if the codec as master. */
regmap_read(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2, &ctrl_val);
if (ctrl_val & NAU8825_I2S_MS_MASTER) {
/* get the bclk and fs ratio */
@@ -1507,11 +1507,22 @@ static int nau8825_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
+static const u64 nau8825_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops nau8825_dai_ops = {
.startup = nau8825_dai_startup,
.hw_params = nau8825_hw_params,
.set_fmt = nau8825_set_dai_fmt,
.set_tdm_slot = nau8825_set_tdm_slot,
+ .auto_selectable_formats = &nau8825_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define NAU8825_RATES SNDRV_PCM_RATE_8000_192000
@@ -2886,7 +2897,7 @@ static int nau8825_i2c_probe(struct i2c_client *i2c)
nau8825->dev = dev;
nau8825->irq = i2c->irq;
/* Initiate parameters, semaphore and work queue which are needed in
- * cross talk suppression measurment function.
+ * cross talk suppression measurement function.
*/
nau8825->xtalk_state = NAU8825_XTALK_DONE;
nau8825->xtalk_protect = false;
diff --git a/sound/soc/codecs/ntp8835.c b/sound/soc/codecs/ntp8835.c
index 5837b9379fee..39620b84e214 100644
--- a/sound/soc/codecs/ntp8835.c
+++ b/sound/soc/codecs/ntp8835.c
@@ -385,9 +385,16 @@ static int ntp8835_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
};
+static const u64 ntp8835_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ntp8835_dai_ops = {
.hw_params = ntp8835_hw_params,
.set_fmt = ntp8835_set_fmt,
+ .auto_selectable_formats = &ntp8835_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ntp8835_dai = {
diff --git a/sound/soc/codecs/ntp8918.c b/sound/soc/codecs/ntp8918.c
index a18d79aa80aa..eca34fafc4e6 100644
--- a/sound/soc/codecs/ntp8918.c
+++ b/sound/soc/codecs/ntp8918.c
@@ -306,10 +306,17 @@ static int ntp8918_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
mute_mask, mute ? mute_mask : 0);
}
+static const u64 ntp8918_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops ntp8918_dai_ops = {
.hw_params = ntp8918_hw_params,
.set_fmt = ntp8918_set_fmt,
.mute_stream = ntp8918_digital_mute,
+ .auto_selectable_formats = &ntp8918_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ntp8918_dai = {
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
index 9bca53de2475..ed1e29e54ef8 100644
--- a/sound/soc/codecs/pcm5102a.c
+++ b/sound/soc/codecs/pcm5102a.c
@@ -6,12 +6,17 @@
* Copyright 2013
*/
+#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/soc.h>
+struct pcm5102a_priv {
+ struct gpio_desc *gpio_reset;
+};
+
static struct snd_soc_dai_driver pcm5102a_dai = {
.name = "pcm5102a-hifi",
.playback = {
@@ -32,10 +37,32 @@ static const struct snd_soc_component_driver soc_component_dev_pcm5102a = {
static int pcm5102a_probe(struct platform_device *pdev)
{
- return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_pcm5102a,
+ struct device *dev = &pdev->dev;
+ struct pcm5102a_priv *pcm5102a;
+
+ pcm5102a = devm_kzalloc(dev, sizeof(*pcm5102a), GFP_KERNEL);
+ if (!pcm5102a)
+ return -ENOMEM;
+
+ pcm5102a->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+
+ if (IS_ERR(pcm5102a->gpio_reset))
+ return dev_err_probe(dev, PTR_ERR(pcm5102a->gpio_reset),
+ "failed to acquire reset gpio\n");
+
+ platform_set_drvdata(pdev, pcm5102a);
+
+ return devm_snd_soc_register_component(dev, &soc_component_dev_pcm5102a,
&pcm5102a_dai, 1);
}
+static void pcm5102a_remove(struct platform_device *pdev)
+{
+ struct pcm5102a_priv *priv = platform_get_drvdata(pdev);
+
+ gpiod_set_value_cansleep(priv->gpio_reset, 1);
+}
+
static const struct of_device_id pcm5102a_of_match[] = {
{ .compatible = "ti,pcm5102a", },
{ }
@@ -44,6 +71,7 @@ MODULE_DEVICE_TABLE(of, pcm5102a_of_match);
static struct platform_driver pcm5102a_codec_driver = {
.probe = pcm5102a_probe,
+ .remove = pcm5102a_remove,
.driver = {
.name = "pcm5102a-codec",
.of_match_table = pcm5102a_of_match,
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
index db85ae2f8aed..db70a33d14b8 100644
--- a/sound/soc/codecs/pcm6240.c
+++ b/sound/soc/codecs/pcm6240.c
@@ -620,12 +620,12 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol,
/* Set to wide-range mode, before using vol ctrl. */
if (!val && vol_ctrl_type == PCMDEV_PCM1690_VOL_CTRL) {
ucontrol->value.integer.value[0] = -25500;
- return ret;
+ return 0;
}
/* Set to fine mode, before using fine vol ctrl. */
if (val && vol_ctrl_type == PCMDEV_PCM1690_FINE_VOL_CTRL) {
ucontrol->value.integer.value[0] = -12750;
- return ret;
+ return 0;
}
}
@@ -641,7 +641,7 @@ static int pcmdev_get_volsw(struct snd_kcontrol *kcontrol,
val = mc->invert ? max - val : val;
ucontrol->value.integer.value[0] = val;
- return ret;
+ return 0;
}
static int pcmdevice_get_volsw(struct snd_kcontrol *kcontrol,
@@ -2019,8 +2019,6 @@ static const struct regmap_config pcmdevice_i2c_regmap = {
static void pcmdevice_remove(struct pcmdevice_priv *pcm_dev)
{
- if (pcm_dev->irq)
- free_irq(pcm_dev->irq, pcm_dev);
mutex_destroy(&pcm_dev->codec_lock);
}
diff --git a/sound/soc/codecs/rk3308_codec.c b/sound/soc/codecs/rk3308_codec.c
index 60befe9d37f0..abed31ba660c 100644
--- a/sound/soc/codecs/rk3308_codec.c
+++ b/sound/soc/codecs/rk3308_codec.c
@@ -679,9 +679,20 @@ static int rk3308_codec_hw_params(struct snd_pcm_substream *substream,
rk3308_codec_adc_dig_config(rk3308, params);
}
+static const u64 rk3308_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops rk3308_codec_dai_ops = {
.hw_params = rk3308_codec_hw_params,
.set_fmt = rk3308_codec_set_dai_fmt,
+ .auto_selectable_formats = &rk3308_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rk3308_codec_dai_driver = {
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
index 5871b5a81975..c2798ee94059 100644
--- a/sound/soc/codecs/rk3328_codec.c
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -312,12 +312,21 @@ static void rk3328_pcm_shutdown(struct snd_pcm_substream *substream,
rk3328_codec_close_playback(rk3328);
}
+static const u64 rk3328_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops rk3328_dai_ops = {
.hw_params = rk3328_hw_params,
.set_fmt = rk3328_set_dai_fmt,
.mute_stream = rk3328_mute_stream,
.startup = rk3328_pcm_startup,
.shutdown = rk3328_pcm_shutdown,
+ .auto_selectable_formats = &rk3328_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
index 15488a059283..dda38e6e56b6 100644
--- a/sound/soc/codecs/rt1011.c
+++ b/sound/soc/codecs/rt1011.c
@@ -2053,29 +2053,45 @@ static int rt1011_probe(struct snd_soc_component *component)
schedule_work(&rt1011->cali_work);
rt1011->i2s_ref = 0;
- rt1011->bq_drc_params = devm_kcalloc(component->dev,
- RT1011_ADVMODE_NUM, sizeof(struct rt1011_bq_drc_params *),
- GFP_KERNEL);
+ rt1011->bq_drc_params = kcalloc(RT1011_ADVMODE_NUM,
+ sizeof(struct rt1011_bq_drc_params *),
+ GFP_KERNEL);
if (!rt1011->bq_drc_params)
return -ENOMEM;
for (i = 0; i < RT1011_ADVMODE_NUM; i++) {
- rt1011->bq_drc_params[i] = devm_kcalloc(component->dev,
- RT1011_BQ_DRC_NUM, sizeof(struct rt1011_bq_drc_params),
- GFP_KERNEL);
+ rt1011->bq_drc_params[i] = kcalloc(RT1011_BQ_DRC_NUM,
+ sizeof(struct rt1011_bq_drc_params),
+ GFP_KERNEL);
if (!rt1011->bq_drc_params[i])
- return -ENOMEM;
+ goto err;
}
return 0;
+
+err:
+ while (i--)
+ kfree(rt1011->bq_drc_params[i]);
+ kfree(rt1011->bq_drc_params);
+ rt1011->bq_drc_params = NULL;
+
+ return -ENOMEM;
}
static void rt1011_remove(struct snd_soc_component *component)
{
struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
+ int i;
cancel_work_sync(&rt1011->cali_work);
rt1011_reset(rt1011->regmap);
+
+ if (rt1011->bq_drc_params) {
+ for (i = 0; i < RT1011_ADVMODE_NUM; i++)
+ kfree(rt1011->bq_drc_params[i]);
+ kfree(rt1011->bq_drc_params);
+ rt1011->bq_drc_params = NULL;
+ }
}
#ifdef CONFIG_PM
@@ -2094,9 +2110,7 @@ static int rt1011_resume(struct snd_soc_component *component)
struct rt1011_priv *rt1011 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt1011->regmap, false);
- regcache_sync(rt1011->regmap);
-
- return 0;
+ return regcache_sync(rt1011->regmap);
}
#else
#define rt1011_suspend NULL
@@ -2133,10 +2147,20 @@ static int rt1011_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 rt1011_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt1011_aif_dai_ops = {
.hw_params = rt1011_hw_params,
.set_fmt = rt1011_set_dai_fmt,
.set_tdm_slot = rt1011_set_tdm_slot,
+ .auto_selectable_formats = &rt1011_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt1011_dai[] = {
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
index ff0c08ff610e..5d6be7cc67b4 100644
--- a/sound/soc/codecs/rt1015.c
+++ b/sound/soc/codecs/rt1015.c
@@ -488,35 +488,72 @@ static int rt1015_bypass_boost_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static void rt1015_calibrate(struct rt1015_priv *rt1015)
+static int rt1015_calibrate(struct rt1015_priv *rt1015)
{
struct snd_soc_component *component = rt1015->component;
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct regmap *regmap = rt1015->regmap;
+ int ret, sync_ret;
snd_soc_dapm_mutex_lock(dapm);
regcache_cache_bypass(regmap, true);
- regmap_write(regmap, RT1015_CLK_DET, 0x0000);
- regmap_write(regmap, RT1015_PWR4, 0x00B2);
- regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0009);
+ ret = regmap_write(regmap, RT1015_CLK_DET, 0x0000);
+ if (ret)
+ goto restore_cache;
+
+ ret = regmap_write(regmap, RT1015_PWR4, 0x00B2);
+ if (ret)
+ goto restore_cache;
+
+ ret = regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0009);
+ if (ret)
+ goto restore_cache;
+
msleep(100);
- regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000A);
+ ret = regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000A);
+ if (ret)
+ goto restore_cache;
+
msleep(100);
- regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000C);
+ ret = regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000C);
+ if (ret)
+ goto restore_cache;
+
msleep(100);
- regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2028);
- regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0140);
- regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000D);
+ ret = regmap_write(regmap, RT1015_CLSD_INTERNAL8, 0x2028);
+ if (ret)
+ goto restore_cache;
+
+ ret = regmap_write(regmap, RT1015_CLSD_INTERNAL9, 0x0140);
+ if (ret)
+ goto restore_cache;
+
+ ret = regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x000D);
+ if (ret)
+ goto restore_cache;
+
msleep(300);
- regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0008);
- regmap_write(regmap, RT1015_SYS_RST1, 0x05F5);
- regmap_write(regmap, RT1015_CLK_DET, 0x8000);
+ ret = regmap_write(regmap, RT1015_PWR_STATE_CTRL, 0x0008);
+ if (ret)
+ goto restore_cache;
+
+ ret = regmap_write(regmap, RT1015_SYS_RST1, 0x05F5);
+ if (ret)
+ goto restore_cache;
+
+ ret = regmap_write(regmap, RT1015_CLK_DET, 0x8000);
+
+restore_cache:
regcache_cache_bypass(regmap, false);
regcache_mark_dirty(regmap);
- regcache_sync(regmap);
+ sync_ret = regcache_sync(regmap);
+ if (!ret)
+ ret = sync_ret;
snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
}
static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol,
@@ -525,6 +562,8 @@ static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt1015_priv *rt1015 =
snd_soc_component_get_drvdata(component);
+ int bypass_boost = rt1015->bypass_boost;
+ int ret;
if (rt1015->dac_is_used) {
dev_err(component->dev, "DAC is being used!\n");
@@ -534,13 +573,23 @@ static int rt1015_bypass_boost_put(struct snd_kcontrol *kcontrol,
rt1015->bypass_boost = ucontrol->value.integer.value[0];
if (rt1015->bypass_boost == RT1015_Bypass_Boost &&
!rt1015->cali_done) {
- rt1015_calibrate(rt1015);
- rt1015->cali_done = 1;
+ ret = rt1015_calibrate(rt1015);
+ if (ret)
+ goto restore_boost;
- regmap_write(rt1015->regmap, RT1015_MONO_DYNA_CTRL, 0x0010);
+ ret = regmap_write(rt1015->regmap, RT1015_MONO_DYNA_CTRL,
+ 0x0010);
+ if (ret)
+ goto restore_boost;
+
+ rt1015->cali_done = 1;
}
return 0;
+
+restore_boost:
+ rt1015->bypass_boost = bypass_boost;
+ return ret;
}
static const char * const rt1015_dac_output_vol_select[] = {
@@ -1016,10 +1065,20 @@ static void rt1015_remove(struct snd_soc_component *component)
#define RT1015_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt1015_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt1015_aif_dai_ops = {
.hw_params = rt1015_hw_params,
.set_fmt = rt1015_set_dai_fmt,
.set_tdm_slot = rt1015_set_tdm_slot,
+ .auto_selectable_formats = &rt1015_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt1015_dai[] = {
diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c
index 11c8c45574a1..1e97d9ffa352 100644
--- a/sound/soc/codecs/rt1016.c
+++ b/sound/soc/codecs/rt1016.c
@@ -534,9 +534,19 @@ static void rt1016_remove(struct snd_soc_component *component)
#define RT1016_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt1016_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt1016_aif_dai_ops = {
.hw_params = rt1016_hw_params,
.set_fmt = rt1016_set_dai_fmt,
+ .auto_selectable_formats = &rt1016_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt1016_dai[] = {
@@ -570,9 +580,7 @@ static int rt1016_resume(struct snd_soc_component *component)
struct rt1016_priv *rt1016 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt1016->regmap, false);
- regcache_sync(rt1016->regmap);
-
- return 0;
+ return regcache_sync(rt1016->regmap);
}
#else
#define rt1016_suspend NULL
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
index 370521149ac8..3998abccd93a 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -492,12 +492,22 @@ static int rt1019_probe(struct snd_soc_component *component)
#define RT1019_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt1019_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt1019_aif_dai_ops = {
.hw_params = rt1019_hw_params,
.set_fmt = rt1019_set_dai_fmt,
.set_sysclk = rt1019_set_dai_sysclk,
.set_pll = rt1019_set_dai_pll,
.set_tdm_slot = rt1019_set_tdm_slot,
+ .auto_selectable_formats = &rt1019_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt1019_dai[] = {
diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c
index 2d5fad76cf52..0130ff9f050d 100644
--- a/sound/soc/codecs/rt1305.c
+++ b/sound/soc/codecs/rt1305.c
@@ -896,9 +896,7 @@ static int rt1305_resume(struct snd_soc_component *component)
struct rt1305_priv *rt1305 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt1305->regmap, false);
- regcache_sync(rt1305->regmap);
-
- return 0;
+ return regcache_sync(rt1305->regmap);
}
#else
#define rt1305_suspend NULL
@@ -910,9 +908,19 @@ static int rt1305_resume(struct snd_soc_component *component)
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 rt1305_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt1305_aif_dai_ops = {
.hw_params = rt1305_hw_params,
.set_fmt = rt1305_set_dai_fmt,
+ .auto_selectable_formats = &rt1305_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt1305_dai[] = {
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
index 630946cd7cdf..3d4aadb91206 100644
--- a/sound/soc/codecs/rt1308.c
+++ b/sound/soc/codecs/rt1308.c
@@ -715,9 +715,7 @@ static int rt1308_resume(struct snd_soc_component *component)
struct rt1308_priv *rt1308 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt1308->regmap, false);
- regcache_sync(rt1308->regmap);
-
- return 0;
+ return regcache_sync(rt1308->regmap);
}
#else
#define rt1308_suspend NULL
@@ -729,9 +727,19 @@ static int rt1308_resume(struct snd_soc_component *component)
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 rt1308_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt1308_aif_dai_ops = {
.hw_params = rt1308_hw_params,
.set_fmt = rt1308_set_dai_fmt,
+ .auto_selectable_formats = &rt1308_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt1308_dai[] = {
diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c
index 55607f1b9a1e..5c80957b0f01 100644
--- a/sound/soc/codecs/rt1318.c
+++ b/sound/soc/codecs/rt1318.c
@@ -1075,8 +1075,7 @@ static int rt1318_resume(struct snd_soc_component *component)
struct rt1318_priv *rt1318 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt1318->regmap, false);
- regcache_sync(rt1318->regmap);
- return 0;
+ return regcache_sync(rt1318->regmap);
}
#else
#define rt1318_suspend NULL
@@ -1087,12 +1086,22 @@ static int rt1318_resume(struct snd_soc_component *component)
#define RT1318_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt1318_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt1318_aif_dai_ops = {
.hw_params = rt1318_hw_params,
.set_fmt = rt1318_set_dai_fmt,
.set_sysclk = rt1318_set_dai_sysclk,
.set_pll = rt1318_set_dai_pll,
.set_tdm_slot = rt1318_set_tdm_slot,
+ .auto_selectable_formats = &rt1318_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt1318_dai[] = {
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index 72af413306fa..fa89db096651 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -890,8 +890,9 @@ static bool rt1320_readable_register(struct device *dev, unsigned int reg)
case 0xf01e:
case 0xf717 ... 0xf719:
case 0xf720 ... 0xf723:
- case 0x1000cd91 ... 0x1000cd96:
- case RT1321_PATCH_MAIN_VER ... RT1321_PATCH_BETA_VER:
+ case 0x10000000 ... 0x10008fff:
+ case 0x1000c000 ... 0x1000cfff:
+ case 0x1000d800 ... 0x1000dfff:
case 0x1000f008:
case 0x1000f021:
case 0x20003000 ... 0x2000300f:
@@ -996,7 +997,8 @@ static bool rt1320_volatile_register(struct device *dev, unsigned int reg)
case 0xf717 ... 0xf719:
case 0xf720 ... 0xf723:
case 0x10000000 ... 0x10008fff:
- case 0x1000c000 ... 0x1000dfff:
+ case 0x1000c000 ... 0x1000cfff:
+ case 0x1000d800 ... 0x1000dfff:
case 0x1000f008:
case 0x1000f021:
case 0x2000300e ... 0x2000300f:
@@ -2084,7 +2086,7 @@ static int rt1320_rae_load(struct rt1320_sdw_priv *rt1320)
regmap_update_bits(rt1320->regmap, 0x20005818, 0x80, 0x80);
/* RAE run */
regmap_update_bits(rt1320->regmap, 0x2000301c, 0x01, 0x01);
- /* Phase sync eanble */
+ /* Phase sync enable */
regmap_update_bits(rt1320->regmap, 0xc047, 0x80, 0x80);
break;
}
@@ -2470,7 +2472,7 @@ static int rt1320_io_init(struct device *dev, struct sdw_slave *slave)
dev_dbg(dev, "%s amp func_status=0x%x\n", __func__, amp_func_status);
/* initialization write */
- if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION) || !rt1320->first_hw_init) {
+ if ((amp_func_status & FUNCTION_NEEDS_INITIALIZATION)) {
switch (rt1320->dev_id) {
case RT1320_DEV_ID:
if (rt1320->version_id < RT1320_VC)
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index 3c47f1ae8db9..5c67b1d210b2 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -1029,6 +1029,12 @@ static int rt274_resume(struct snd_soc_component *component)
#define RT274_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt274_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops rt274_aif_dai_ops = {
.hw_params = rt274_hw_params,
.set_fmt = rt274_set_dai_fmt,
@@ -1036,6 +1042,8 @@ static const struct snd_soc_dai_ops rt274_aif_dai_ops = {
.set_pll = rt274_set_dai_pll,
.set_bclk_ratio = rt274_set_bclk_ratio,
.set_tdm_slot = rt274_set_tdm_slot,
+ .auto_selectable_formats = &rt274_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt274_dai[] = {
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index bcd9f5abcda8..c60e81ba87f3 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -995,11 +995,19 @@ static int rt286_resume(struct snd_soc_component *component)
#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt286_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops rt286_aif_dai_ops = {
.hw_params = rt286_hw_params,
.set_fmt = rt286_set_dai_fmt,
.set_sysclk = rt286_set_dai_sysclk,
.set_bclk_ratio = rt286_set_bclk_ratio,
+ .auto_selectable_formats = &rt286_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt286_dai[] = {
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 09aed08b5b79..72407fec0f34 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1056,11 +1056,19 @@ static int rt298_resume(struct snd_soc_component *component)
#define RT298_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt298_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops rt298_aif_dai_ops = {
.hw_params = rt298_hw_params,
.set_fmt = rt298_set_dai_fmt,
.set_sysclk = rt298_set_dai_sysclk,
.set_bclk_ratio = rt298_set_bclk_ratio,
+ .auto_selectable_formats = &rt298_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt298_dai[] = {
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
index 91290bfe8daa..bcc2ff28b125 100644
--- a/sound/soc/codecs/rt5514-spi.c
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -43,6 +43,7 @@ struct rt5514_dsp {
struct snd_pcm_substream *substream;
unsigned int buf_base, buf_limit, buf_rp;
size_t buf_size, get_size, dma_offset;
+ int irq;
};
static const struct snd_pcm_hardware rt5514_spi_pcm_hardware = {
@@ -257,8 +258,7 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
struct rt5514_dsp *rt5514_dsp;
int ret;
- rt5514_dsp = devm_kzalloc(component->dev, sizeof(*rt5514_dsp),
- GFP_KERNEL);
+ rt5514_dsp = kzalloc_obj(*rt5514_dsp);
if (!rt5514_dsp)
return -ENOMEM;
@@ -268,21 +268,41 @@ static int rt5514_spi_pcm_probe(struct snd_soc_component *component)
snd_soc_component_set_drvdata(component, rt5514_dsp);
if (rt5514_spi->irq) {
- ret = devm_request_threaded_irq(&rt5514_spi->dev,
- rt5514_spi->irq, NULL, rt5514_spi_irq,
- IRQF_TRIGGER_RISING | IRQF_ONESHOT, "rt5514-spi",
- rt5514_dsp);
- if (ret)
+ ret = request_threaded_irq(rt5514_spi->irq, NULL,
+ rt5514_spi_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ "rt5514-spi", rt5514_dsp);
+ if (ret) {
dev_err(&rt5514_spi->dev,
"%s Failed to request IRQ: %d\n", __func__,
ret);
- else
+ } else {
+ rt5514_dsp->irq = rt5514_spi->irq;
device_init_wakeup(rt5514_dsp->dev, true);
+ }
}
return 0;
}
+static void rt5514_spi_pcm_remove(struct snd_soc_component *component)
+{
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ snd_soc_component_set_drvdata(component, NULL);
+
+ if (rt5514_dsp->irq) {
+ free_irq(rt5514_dsp->irq, rt5514_dsp);
+ device_init_wakeup(rt5514_dsp->dev, false);
+ }
+
+ cancel_delayed_work_sync(&rt5514_dsp->copy_work);
+
+ mutex_destroy(&rt5514_dsp->dma_lock);
+ kfree(rt5514_dsp);
+}
+
static int rt5514_spi_pcm_new(struct snd_soc_component *component,
struct snd_soc_pcm_runtime *rtd)
{
@@ -294,6 +314,7 @@ static int rt5514_spi_pcm_new(struct snd_soc_component *component,
static const struct snd_soc_component_driver rt5514_spi_component = {
.name = DRV_NAME,
.probe = rt5514_spi_pcm_probe,
+ .remove = rt5514_spi_pcm_remove,
.open = rt5514_spi_pcm_open,
.hw_params = rt5514_spi_hw_params,
.hw_free = rt5514_spi_hw_free,
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index 58aecd4c83a6..a33fbc9fb047 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -1099,18 +1099,6 @@ static int rt5514_set_bias_level(struct snd_soc_component *component,
static int rt5514_probe(struct snd_soc_component *component)
{
struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
- struct platform_device *pdev = to_platform_device(component->dev);
-
- rt5514->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(rt5514->mclk))
- return PTR_ERR(rt5514->mclk);
-
- if (rt5514->pdata.dsp_calib_clk_name) {
- rt5514->dsp_calib_clk = devm_clk_get(&pdev->dev,
- rt5514->pdata.dsp_calib_clk_name);
- if (PTR_ERR(rt5514->dsp_calib_clk) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
- }
rt5514->component = component;
rt5514->pll3_cal_value = 0x0078b000;
@@ -1142,12 +1130,24 @@ static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val)
#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5514_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops rt5514_aif_dai_ops = {
.hw_params = rt5514_hw_params,
.set_fmt = rt5514_set_dai_fmt,
.set_sysclk = rt5514_set_dai_sysclk,
.set_pll = rt5514_set_dai_pll,
.set_tdm_slot = rt5514_set_tdm_slot,
+ .auto_selectable_formats = &rt5514_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5514_dai[] = {
@@ -1273,6 +1273,17 @@ static int rt5514_i2c_probe(struct i2c_client *i2c)
else
rt5514_parse_dp(rt5514, &i2c->dev);
+ rt5514->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5514->mclk))
+ return PTR_ERR(rt5514->mclk);
+
+ if (rt5514->pdata.dsp_calib_clk_name) {
+ rt5514->dsp_calib_clk = devm_clk_get(&i2c->dev,
+ rt5514->pdata.dsp_calib_clk_name);
+ if (PTR_ERR(rt5514->dsp_calib_clk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+ }
+
rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap);
if (IS_ERR(rt5514->i2c_regmap)) {
ret = PTR_ERR(rt5514->i2c_regmap);
@@ -1320,7 +1331,7 @@ static int rt5514_i2c_probe(struct i2c_client *i2c)
rt5514_dai, ARRAY_SIZE(rt5514_dai));
}
-static const struct dev_pm_ops rt5514_i2_pm_ops = {
+static const struct dev_pm_ops rt5514_i2c_pm_ops = {
SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume)
};
@@ -1329,7 +1340,7 @@ static struct i2c_driver rt5514_i2c_driver = {
.name = "rt5514",
.acpi_match_table = ACPI_PTR(rt5514_acpi_match),
.of_match_table = of_match_ptr(rt5514_of_match),
- .pm = pm_ptr(&rt5514_i2_pm_ops),
+ .pm = pm_ptr(&rt5514_i2c_pm_ops),
},
.probe = rt5514_i2c_probe,
.id_table = rt5514_i2c_id,
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index 46ee412dc81d..af6299baea7c 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1222,11 +1222,6 @@ static int rt5616_probe(struct snd_soc_component *component)
{
struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
- /* Check if MCLK provided */
- rt5616->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(rt5616->mclk))
- return PTR_ERR(rt5616->mclk);
-
rt5616->component = component;
return 0;
@@ -1248,8 +1243,7 @@ static int rt5616_resume(struct snd_soc_component *component)
struct rt5616_priv *rt5616 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5616->regmap, false);
- regcache_sync(rt5616->regmap);
- return 0;
+ return regcache_sync(rt5616->regmap);
}
#else
#define rt5616_suspend NULL
@@ -1260,11 +1254,21 @@ static int rt5616_resume(struct snd_soc_component *component)
#define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5616_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5616_aif_dai_ops = {
.hw_params = rt5616_hw_params,
.set_fmt = rt5616_set_dai_fmt,
.set_sysclk = rt5616_set_dai_sysclk,
.set_pll = rt5616_set_dai_pll,
+ .auto_selectable_formats = &rt5616_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5616_dai[] = {
@@ -1347,6 +1351,11 @@ static int rt5616_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, rt5616);
+ /* Check if MCLK provided */
+ rt5616->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5616->mclk))
+ return PTR_ERR(rt5616->mclk);
+
rt5616->regmap = devm_regmap_init_i2c(i2c, &rt5616_regmap);
if (IS_ERR(rt5616->regmap)) {
ret = PTR_ERR(rt5616->regmap);
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index ed1233973eb8..f97fd24e4229 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1626,11 +1626,21 @@ static int rt5631_probe(struct snd_soc_component *component)
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5631_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5631_ops = {
.hw_params = rt5631_hifi_pcm_params,
.set_fmt = rt5631_hifi_codec_set_dai_fmt,
.set_sysclk = rt5631_hifi_codec_set_dai_sysclk,
.set_pll = rt5631_codec_set_dai_pll,
+ .auto_selectable_formats = &rt5631_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5631_dai[] = {
diff --git a/sound/soc/codecs/rt5631.h b/sound/soc/codecs/rt5631.h
index 8a6b99a48c7c..00734207ac9f 100644
--- a/sound/soc/codecs/rt5631.h
+++ b/sound/soc/codecs/rt5631.h
@@ -415,7 +415,7 @@
#define RT5631_ADDA_FILTER_CLK_SEL_256FS (0 << 7) /* 256FS */
#define RT5631_ADDA_FILTER_CLK_SEL_384FS (1 << 7) /* 384FS */
-/* Power managment addition 1 (0x3A) */
+/* Power management addition 1 (0x3A) */
#define RT5631_PWR_MAIN_I2S_EN (0x1 << 15)
#define RT5631_PWR_MAIN_I2S_BIT 15
#define RT5631_PWR_CLASS_D (0x1 << 12)
@@ -435,7 +435,7 @@
#define RT5631_PWR_DAC_R_TO_MIXER (0x1 << 5)
#define RT5631_PWR_DAC_R_TO_MIXER_BIT 5
-/* Power managment addition 2 (0x3B) */
+/* Power management addition 2 (0x3B) */
#define RT5631_PWR_OUTMIXER_L (0x1 << 15)
#define RT5631_PWR_OUTMIXER_L_BIT 15
#define RT5631_PWR_OUTMIXER_R (0x1 << 14)
@@ -461,7 +461,7 @@
#define RT5631_PWR_PLL2 (0x1 << 0)
#define RT5631_PWR_PLL2_BIT 0
-/* Power managment addition 3(0x3C) */
+/* Power management addition 3(0x3C) */
#define RT5631_PWR_VREF (0x1 << 15)
#define RT5631_PWR_VREF_BIT 15
#define RT5631_PWR_FAST_VREF_CTRL (0x1 << 14)
@@ -489,7 +489,7 @@
#define RT5631_PWR_HP_AMP_DRIVING (0x1 << 0)
#define RT5631_PWR_HP_AMP_DRIVING_BIT 0
-/* Power managment addition 4(0x3E) */
+/* Power management addition 4(0x3E) */
#define RT5631_PWR_SPK_L_VOL (0x1 << 15)
#define RT5631_PWR_SPK_L_VOL_BIT 15
#define RT5631_PWR_SPK_R_VOL (0x1 << 14)
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 74fd05176eff..d6068bb5cb4c 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1216,7 +1216,7 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
SND_SOC_DAPM_MIXER("SPK MIXR", RT5640_PWR_MIXER, RT5640_PWR_SM_R_BIT,
0, rt5640_spk_r_mix, ARRAY_SIZE(rt5640_spk_r_mix)),
- /* Ouput Volume */
+ /* Output Volume */
SND_SOC_DAPM_PGA("SPKVOL L", RT5640_PWR_VOL,
RT5640_PWR_SV_L_BIT, 0, NULL, 0),
SND_SOC_DAPM_PGA("SPKVOL R", RT5640_PWR_VOL,
@@ -2667,11 +2667,6 @@ static int rt5640_probe(struct snd_soc_component *component)
bool dmic_en = false;
u32 val;
- /* Check if MCLK provided */
- rt5640->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(rt5640->mclk))
- return PTR_ERR(rt5640->mclk);
-
rt5640->component = component;
snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
@@ -2877,11 +2872,21 @@ static int rt5640_resume(struct snd_soc_component *component)
#define RT5640_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5640_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5640_aif_dai_ops = {
.hw_params = rt5640_hw_params,
.set_fmt = rt5640_set_dai_fmt,
.set_sysclk = rt5640_set_dai_sysclk,
.set_pll = rt5640_set_dai_pll,
+ .auto_selectable_formats = &rt5640_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5640_dai[] = {
@@ -3002,6 +3007,11 @@ static int rt5640_i2c_probe(struct i2c_client *i2c)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5640);
+ /* Check if MCLK provided */
+ rt5640->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5640->mclk))
+ return PTR_ERR(rt5640->mclk);
+
rt5640->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
"realtek,ldo1-en",
GPIOD_OUT_HIGH);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 7313ea71c958..15d21097ed0c 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -2209,7 +2209,7 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
0, rt5645_out_l_mix, ARRAY_SIZE(rt5645_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5645_PWR_MIXER, RT5645_PWR_OM_R_BIT,
0, rt5645_out_r_mix, ARRAY_SIZE(rt5645_out_r_mix)),
- /* Ouput Volume */
+ /* Output Volume */
SND_SOC_DAPM_SWITCH("SPKVOL L", RT5645_PWR_VOL, RT5645_PWR_SV_L_BIT, 0,
&spk_l_vol_control),
SND_SOC_DAPM_SWITCH("SPKVOL R", RT5645_PWR_VOL, RT5645_PWR_SV_R_BIT, 0,
@@ -3487,10 +3487,8 @@ static int rt5645_probe(struct snd_soc_component *component)
if (rt5645->pdata.long_name)
component->card->long_name = rt5645->pdata.long_name;
- rt5645->eq_param = devm_kcalloc(component->dev,
- RT5645_HWEQ_NUM, sizeof(struct rt5645_eq_param_s),
- GFP_KERNEL);
-
+ rt5645->eq_param = kcalloc(RT5645_HWEQ_NUM,
+ sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
if (!rt5645->eq_param)
return -ENOMEM;
@@ -3503,7 +3501,12 @@ static int rt5645_probe(struct snd_soc_component *component)
static void rt5645_remove(struct snd_soc_component *component)
{
+ struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+
rt5645_reset(component);
+
+ kfree(rt5645->eq_param);
+ rt5645->eq_param = NULL;
}
#ifdef CONFIG_PM
@@ -3541,12 +3544,22 @@ static int rt5645_resume(struct snd_soc_component *component)
#define RT5645_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5645_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5645_aif_dai_ops = {
.hw_params = rt5645_hw_params,
.set_fmt = rt5645_set_dai_fmt,
.set_sysclk = rt5645_set_dai_sysclk,
.set_tdm_slot = rt5645_set_tdm_slot,
.set_pll = rt5645_set_dai_pll,
+ .auto_selectable_formats = &rt5645_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5645_dai[] = {
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 5c729135f71b..b911757376f5 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1024,7 +1024,7 @@ static const struct snd_soc_dapm_widget rt5651_dapm_widgets[] = {
0, rt5651_out_l_mix, ARRAY_SIZE(rt5651_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5651_PWR_MIXER, RT5651_PWR_OM_R_BIT,
0, rt5651_out_r_mix, ARRAY_SIZE(rt5651_out_r_mix)),
- /* Ouput Volume */
+ /* Output Volume */
SND_SOC_DAPM_SWITCH("OUTVOL L", RT5651_PWR_VOL,
RT5651_PWR_OV_L_BIT, 0, &outvol_l_control),
SND_SOC_DAPM_SWITCH("OUTVOL R", RT5651_PWR_VOL,
@@ -2102,11 +2102,21 @@ static int rt5651_resume(struct snd_soc_component *component)
#define RT5651_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5651_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5651_aif_dai_ops = {
.hw_params = rt5651_hw_params,
.set_fmt = rt5651_set_dai_fmt,
.set_sysclk = rt5651_set_dai_sysclk,
.set_pll = rt5651_set_dai_pll,
+ .auto_selectable_formats = &rt5651_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5651_dai[] = {
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 3097ee6d4e89..389be7cbfe05 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -3703,9 +3703,7 @@ static int rt5659_resume(struct snd_soc_component *component)
struct rt5659_priv *rt5659 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5659->regmap, false);
- regcache_sync(rt5659->regmap);
-
- return 0;
+ return regcache_sync(rt5659->regmap);
}
#else
#define rt5659_suspend NULL
@@ -3716,11 +3714,21 @@ static int rt5659_resume(struct snd_soc_component *component)
#define RT5659_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5659_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5659_aif_dai_ops = {
.hw_params = rt5659_hw_params,
.set_fmt = rt5659_set_dai_fmt,
.set_tdm_slot = rt5659_set_tdm_slot,
.set_bclk_ratio = rt5659_set_bclk_ratio,
+ .auto_selectable_formats = &rt5659_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5659_dai[] = {
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index edf2e3e346e4..2a3ecac12c07 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -1148,9 +1148,7 @@ static int rt5660_resume(struct snd_soc_component *component)
msleep(350);
regcache_cache_only(rt5660->regmap, false);
- regcache_sync(rt5660->regmap);
-
- return 0;
+ return regcache_sync(rt5660->regmap);
}
#else
#define rt5660_suspend NULL
@@ -1161,11 +1159,21 @@ static int rt5660_resume(struct snd_soc_component *component)
#define RT5660_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5660_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5660_aif_dai_ops = {
.hw_params = rt5660_hw_params,
.set_fmt = rt5660_set_dai_fmt,
.set_sysclk = rt5660_set_dai_sysclk,
.set_pll = rt5660_set_dai_pll,
+ .auto_selectable_formats = &rt5660_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5660_dai[] = {
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index 262d3bba1f3d..ec46e6180cc0 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3222,6 +3222,14 @@ static int rt5663_resume(struct snd_soc_component *component)
#define RT5663_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5663_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5663_aif_dai_ops = {
.hw_params = rt5663_hw_params,
.set_fmt = rt5663_set_dai_fmt,
@@ -3229,6 +3237,8 @@ static const struct snd_soc_dai_ops rt5663_aif_dai_ops = {
.set_pll = rt5663_set_dai_pll,
.set_tdm_slot = rt5663_set_tdm_slot,
.set_bclk_ratio = rt5663_set_bclk_ratio,
+ .auto_selectable_formats = &rt5663_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5663_dai[] = {
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 1d987472ba42..62b21092e352 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -4388,9 +4388,7 @@ static int rt5665_resume(struct snd_soc_component *component)
struct rt5665_priv *rt5665 = snd_soc_component_get_drvdata(component);
regcache_cache_only(rt5665->regmap, false);
- regcache_sync(rt5665->regmap);
-
- return 0;
+ return regcache_sync(rt5665->regmap);
}
#else
#define rt5665_suspend NULL
@@ -4401,11 +4399,21 @@ static int rt5665_resume(struct snd_soc_component *component)
#define RT5665_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5665_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5665_aif_dai_ops = {
.hw_params = rt5665_hw_params,
.set_fmt = rt5665_set_dai_fmt,
.set_tdm_slot = rt5665_set_tdm_slot,
.set_bclk_ratio = rt5665_set_bclk_ratio,
+ .auto_selectable_formats = &rt5665_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5665_dai[] = {
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index 4154bad27610..cd6245d6b91a 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2254,16 +2254,38 @@ static int rt5668_resume(struct snd_soc_component *component)
#define RT5668_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5668_selectable_formats_aif1 =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
+static const u64 rt5668_selectable_formats_aif2 =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5668_aif1_dai_ops = {
.hw_params = rt5668_hw_params,
.set_fmt = rt5668_set_dai_fmt,
.set_tdm_slot = rt5668_set_tdm_slot,
+ .auto_selectable_formats = &rt5668_selectable_formats_aif1,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops rt5668_aif2_dai_ops = {
.hw_params = rt5668_hw_params,
.set_fmt = rt5668_set_dai_fmt,
.set_bclk_ratio = rt5668_set_bclk_ratio,
+ .auto_selectable_formats = &rt5668_selectable_formats_aif2,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5668_dai[] = {
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 1d120b5dc2be..62d4e7cea5af 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -1901,7 +1901,7 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
0, rt5670_out_l_mix, ARRAY_SIZE(rt5670_out_l_mix)),
SND_SOC_DAPM_MIXER("OUT MIXR", RT5670_PWR_MIXER, RT5670_PWR_OM_R_BIT,
0, rt5670_out_r_mix, ARRAY_SIZE(rt5670_out_r_mix)),
- /* Ouput Volume */
+ /* Output Volume */
SND_SOC_DAPM_MIXER("HPOVOL MIXL", RT5670_PWR_VOL,
RT5670_PWR_HV_L_BIT, 0,
rt5670_hpvoll_mix, ARRAY_SIZE(rt5670_hpvoll_mix)),
@@ -2791,12 +2791,22 @@ static int rt5670_resume(struct snd_soc_component *component)
#define RT5670_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5670_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5670_aif_dai_ops = {
.hw_params = rt5670_hw_params,
.set_fmt = rt5670_set_dai_fmt,
.set_tdm_slot = rt5670_set_tdm_slot,
.set_pll = rt5670_set_dai_pll,
.set_bclk_ratio = rt5670_set_bclk_ratio,
+ .auto_selectable_formats = &rt5670_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt5670_dai[] = {
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
index ebc527115ea5..0b2166082437 100644
--- a/sound/soc/codecs/rt5677-spi.c
+++ b/sound/soc/codecs/rt5677-spi.c
@@ -58,6 +58,8 @@
static struct spi_device *g_spi;
static DEFINE_MUTEX(spi_mutex);
+/* Serializes access to the DSP context against the exported hotword callback */
+static DEFINE_MUTEX(dsp_lock);
struct rt5677_dsp {
struct device *dev;
@@ -380,8 +382,7 @@ static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
{
struct rt5677_dsp *rt5677_dsp;
- rt5677_dsp = devm_kzalloc(component->dev, sizeof(*rt5677_dsp),
- GFP_KERNEL);
+ rt5677_dsp = kzalloc_obj(*rt5677_dsp);
if (!rt5677_dsp)
return -ENOMEM;
rt5677_dsp->dev = &g_spi->dev;
@@ -392,9 +393,23 @@ static int rt5677_spi_pcm_probe(struct snd_soc_component *component)
return 0;
}
+static void rt5677_spi_pcm_remove(struct snd_soc_component *component)
+{
+ struct rt5677_dsp *rt5677_dsp =
+ snd_soc_component_get_drvdata(component);
+
+ scoped_guard(mutex, &dsp_lock)
+ snd_soc_component_set_drvdata(component, NULL);
+
+ cancel_delayed_work_sync(&rt5677_dsp->copy_work);
+ mutex_destroy(&rt5677_dsp->dma_lock);
+ kfree(rt5677_dsp);
+}
+
static const struct snd_soc_component_driver rt5677_spi_dai_component = {
.name = DRV_NAME,
.probe = rt5677_spi_pcm_probe,
+ .remove = rt5677_spi_pcm_remove,
.open = rt5677_spi_pcm_open,
.close = rt5677_spi_pcm_close,
.hw_params = rt5677_spi_hw_params,
@@ -579,6 +594,8 @@ void rt5677_spi_hotword_detected(void)
if (!g_spi)
return;
+ guard(mutex)(&dsp_lock);
+
rt5677_dsp = dev_get_drvdata(&g_spi->dev);
if (!rt5677_dsp) {
dev_err(&g_spi->dev, "Can't get rt5677_dsp\n");
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 4757017cc83d..7b80922236e5 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5031,12 +5031,22 @@ static int rt5677_write(void *context, unsigned int reg, unsigned int val)
#define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+static const u64 rt5677_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5677_aif_dai_ops = {
.hw_params = rt5677_hw_params,
.set_fmt = rt5677_set_dai_fmt,
.set_sysclk = rt5677_set_dai_sysclk,
.set_pll = rt5677_set_dai_pll,
.set_tdm_slot = rt5677_set_tdm_slot,
+ .auto_selectable_formats = &rt5677_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops rt5677_dsp_dai_ops = {
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 6f7e68c20d13..1aceb08b280d 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -3043,11 +3043,31 @@ static int rt5682_resume(struct snd_soc_component *component)
#define rt5682_resume NULL
#endif
+static const u64 rt5682_selectable_formats_aif1 =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
+static const u64 rt5682_selectable_formats_aif2 =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
const struct snd_soc_dai_ops rt5682_aif1_dai_ops = {
.hw_params = rt5682_hw_params,
.set_fmt = rt5682_set_dai_fmt,
.set_tdm_slot = rt5682_set_tdm_slot,
.set_bclk_ratio = rt5682_set_bclk1_ratio,
+ .auto_selectable_formats = &rt5682_selectable_formats_aif1,
+ .num_auto_selectable_formats = 1,
};
EXPORT_SYMBOL_GPL(rt5682_aif1_dai_ops);
@@ -3055,6 +3075,8 @@ const struct snd_soc_dai_ops rt5682_aif2_dai_ops = {
.hw_params = rt5682_hw_params,
.set_fmt = rt5682_set_dai_fmt,
.set_bclk_ratio = rt5682_set_bclk2_ratio,
+ .auto_selectable_formats = &rt5682_selectable_formats_aif2,
+ .num_auto_selectable_formats = 1,
};
EXPORT_SYMBOL_GPL(rt5682_aif2_dai_ops);
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index 34997cfb465b..0f80e4c32f57 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -2846,11 +2846,6 @@ static int rt5682s_dai_probe_clks(struct snd_soc_component *component)
struct rt5682s_priv *rt5682s = snd_soc_component_get_drvdata(component);
int ret;
- /* Check if MCLK provided */
- rt5682s->mclk = devm_clk_get_optional(component->dev, "mclk");
- if (IS_ERR(rt5682s->mclk))
- return PTR_ERR(rt5682s->mclk);
-
/* Register CCF DAI clock control */
ret = rt5682s_register_dai_clks(component);
if (ret)
@@ -2926,17 +2921,39 @@ static int rt5682s_resume(struct snd_soc_component *component)
#define rt5682s_resume NULL
#endif
+static const u64 rt5682s_selectable_formats_aif1 =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
+static const u64 rt5682s_selectable_formats_aif2 =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rt5682s_aif1_dai_ops = {
.hw_params = rt5682s_hw_params,
.set_fmt = rt5682s_set_dai_fmt,
.set_tdm_slot = rt5682s_set_tdm_slot,
.set_bclk_ratio = rt5682s_set_bclk1_ratio,
+ .auto_selectable_formats = &rt5682s_selectable_formats_aif1,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops rt5682s_aif2_dai_ops = {
.hw_params = rt5682s_hw_params,
.set_fmt = rt5682s_set_dai_fmt,
.set_bclk_ratio = rt5682s_set_bclk2_ratio,
+ .auto_selectable_formats = &rt5682s_selectable_formats_aif2,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_component_driver rt5682s_soc_component_dev = {
@@ -3130,6 +3147,13 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c)
return ret;
}
+#ifdef CONFIG_COMMON_CLK
+ /* Check if MCLK provided */
+ rt5682s->mclk = devm_clk_get_optional(&i2c->dev, "mclk");
+ if (IS_ERR(rt5682s->mclk))
+ return PTR_ERR(rt5682s->mclk);
+#endif
+
for (i = 0; i < ARRAY_SIZE(rt5682s->supplies); i++)
rt5682s->supplies[i].supply = rt5682s_supply_names[i];
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index a9f3aa4e143a..882390890738 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -246,7 +246,7 @@ static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
struct rt712_sdca_dmic_kctrl_priv *p =
(struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
unsigned int regvalue, ctl, i;
@@ -277,7 +277,7 @@ static int rt712_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
struct rt712_sdca_dmic_kctrl_priv *p =
(struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
- struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
unsigned int gain_val[4];
unsigned int i, adc_vol_flag = 0, changed = 0;
unsigned int regvalue[4];
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
index edba0367d9ce..f2f91dd087bf 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.c
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -395,6 +395,7 @@ static void rt712_sdca_sdw_remove(struct sdw_slave *slave)
mutex_destroy(&rt712->calibrate_mutex);
mutex_destroy(&rt712->disable_irq_lock);
+ mutex_destroy(&rt712->gc_lock);
}
static const struct sdw_device_id rt712_sdca_id[] = {
diff --git a/sound/soc/codecs/rt712-sdca-sdw.h b/sound/soc/codecs/rt712-sdca-sdw.h
index 7ad25cc27f62..162d62234b68 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.h
+++ b/sound/soc/codecs/rt712-sdca-sdw.h
@@ -46,6 +46,9 @@ static const struct reg_default rt712_sdca_mbq_defaults[] = {
{ 0x5b00029, 0x3fff },
{ 0x5b0002a, 0xf000 },
{ 0x6100000, 0x04e4 },
+ { 0x6100009, 0x0000 },
+ { 0x610000a, 0x0000 },
+ { 0x610000b, 0x0020 },
{ 0x610000e, 0x0007 },
{ 0x6100045, 0x0860 },
{ 0x6100046, 0x0029 },
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
index 38052cb19790..a98537e23c21 100644
--- a/sound/soc/codecs/rt712-sdca.c
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -113,6 +113,138 @@ static void rt712_sdca_clk_patch2(struct rt712_sdca_priv *rt712)
rt712_sdca_index_write(rt712, RT712_VENDOR_REG, 0x65, 0x0000);
}
+static int rt712_sdca_gpio_request(struct gpio_chip *chip, unsigned int offset)
+{
+ struct rt712_sdca_priv *rt712 = gpiochip_get_data(chip);
+ struct device *dev = &rt712->slave->dev;
+ unsigned int gpio_pin = offset + 3;
+ int ret;
+
+ dev_dbg(&rt712->slave->dev, "%s: gpio_pin=%d\n", __func__, gpio_pin);
+
+ mutex_lock(&rt712->gc_lock);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0 && ret != -EACCES)
+ goto io_error;
+
+ /*
+ * Only support GPIO3 and GPIO4 for now
+ */
+ switch (gpio_pin) {
+ case 3:
+ break;
+ case 4:
+ ret = rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_CONFIG_CTL0, 0x000c, 0x0004);
+ if (ret < 0)
+ goto io_error;
+ break;
+ default:
+ ret = -EINVAL;
+ goto io_error;
+ }
+
+ ret = rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_GPIO_EN_CTL, (1 << gpio_pin), (1 << gpio_pin));
+ if (ret < 0)
+ goto io_error;
+ ret = rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_GPIO_DIRECTION_CTL, (1 << gpio_pin), (1 << gpio_pin));
+
+io_error:
+ mutex_unlock(&rt712->gc_lock);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int rt712_sdca_gpio_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct rt712_sdca_priv *rt712 = gpiochip_get_data(chip);
+ struct device *dev = &rt712->slave->dev;
+ unsigned int gpio_pin = offset + 3;
+ int ret;
+
+ dev_dbg(&rt712->slave->dev, "%s: gpio_pin=%d, value=%d\n", __func__, gpio_pin, value);
+
+ mutex_lock(&rt712->gc_lock);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0 && ret != -EACCES)
+ goto io_error;
+
+ ret = rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_GPIO_SET_CTL, (1 << gpio_pin), (!!value << gpio_pin));
+
+io_error:
+ mutex_unlock(&rt712->gc_lock);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static int rt712_sdca_gpio_direction_out(struct gpio_chip *chip,
+ unsigned offset, int value)
+{
+ struct rt712_sdca_priv *rt712 = gpiochip_get_data(chip);
+ struct device *dev = &rt712->slave->dev;
+ unsigned int gpio_pin = offset + 3;
+ int ret;
+
+ dev_dbg(&rt712->slave->dev, "%s: gpio_pin=%d, value=%d\n", __func__, gpio_pin, value);
+
+ mutex_lock(&rt712->gc_lock);
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0 && ret != -EACCES)
+ goto io_error;
+
+ switch (gpio_pin) {
+ case 3:
+ case 4:
+ ret = rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_GPIO_DIRECTION_CTL, (1 << gpio_pin), (1 << gpio_pin));
+ if (ret < 0)
+ goto io_error;
+ ret = rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_GPIO_SET_CTL, (1 << gpio_pin), (!!value << gpio_pin));
+ break;
+ default:
+ ret = -EINVAL;
+ goto io_error;
+ }
+
+io_error:
+ mutex_unlock(&rt712->gc_lock);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ return ret;
+}
+
+static const struct gpio_chip rt712_sdca_template_chip = {
+ .label = "rt712-sdca",
+ .owner = THIS_MODULE,
+ .request = rt712_sdca_gpio_request,
+ .direction_output = rt712_sdca_gpio_direction_out,
+ .set = rt712_sdca_gpio_set,
+ .ngpio = 2,
+ .can_sleep = true,
+ .base = -1,
+};
+
+static void rt712_sdca_gpio_init(struct rt712_sdca_priv *rt712)
+{
+ int ret;
+
+ mutex_init(&rt712->gc_lock);
+ rt712->gpio_chip = rt712_sdca_template_chip;
+ rt712->gpio_chip.parent = &rt712->slave->dev;
+ rt712->gpio_chip.fwnode = dev_fwnode(&rt712->slave->dev);
+
+ ret = devm_gpiochip_add_data(&rt712->slave->dev, &rt712->gpio_chip, rt712);
+ if (ret != 0)
+ dev_err(&rt712->slave->dev, "Failed to add GPIOs: %d\n", ret);
+}
+
static int rt712_sdca_calibration(struct rt712_sdca_priv *rt712)
{
unsigned int val, loop_rc = 0, loop_dc = 0;
@@ -1724,6 +1856,10 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap,
INIT_DELAYED_WORK(&rt712->jack_detect_work, rt712_sdca_jack_detect_handler);
INIT_DELAYED_WORK(&rt712->jack_btn_check_work, rt712_sdca_btn_check_handler);
+ /* initialize GPIOs */
+ if (IS_ENABLED(CONFIG_GPIOLIB))
+ rt712_sdca_gpio_init(rt712);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
index 6229fe341bb5..7e6ee21c8a17 100644
--- a/sound/soc/codecs/rt712-sdca.h
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -14,6 +14,7 @@
#include <linux/soundwire/sdw_type.h>
#include <sound/soc.h>
#include <linux/workqueue.h>
+#include <linux/gpio/driver.h>
struct rt712_sdca_priv {
struct regmap *regmap;
@@ -45,6 +46,8 @@ struct rt712_sdca_priv {
bool fu05_dapm_mute;
bool fu05_mixer_l_mute;
bool fu05_mixer_r_mute;
+ struct gpio_chip gpio_chip;
+ struct mutex gc_lock;
};
struct rt712_dmic_kctrl_priv {
@@ -114,6 +117,9 @@ struct rt712_dmic_kctrl_priv {
#define RT712_HDA_LEGACY_MUX_CTL0 0x00
#define RT712_HDA_LEGACY_CONFIG_CTL0 0x06
#define RT712_HDA_LEGACY_RESET_CTL 0x08
+#define RT712_HDA_GPIO_EN_CTL 0x09
+#define RT712_HDA_GPIO_DIRECTION_CTL 0x0a
+#define RT712_HDA_GPIO_SET_CTL 0x0b
#define RT712_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e
#define RT712_DMIC_ENT_FLOAT_CTL 0x10
#define RT712_DMIC_GAIN_ENT_FLOAT_CTL0 0x11
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
index 910583162d3e..a8c7b42898db 100644
--- a/sound/soc/codecs/rt721-sdca-sdw.c
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -25,16 +25,22 @@ static bool rt721_sdca_readable_register(struct device *dev, unsigned int reg)
case 0x2f50:
case 0x2f51:
case 0x2f58 ... 0x2f5d:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
RT721_SDCA_CTL_XUV, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
RT721_SDCA_CTL_SELECTED_MODE, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
RT721_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0):
case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0):
return true;
default:
return false;
@@ -46,14 +52,20 @@ static bool rt721_sdca_volatile_register(struct device *dev, unsigned int reg)
switch (reg) {
case 0x2f01:
case 0x2f51:
+ case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_GE49,
RT721_SDCA_CTL_DETECTED_MODE, 0):
case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_XUV,
RT721_SDCA_CTL_XUV, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0):
case SDW_SDCA_CTL(FUNC_NUM_HID, RT721_SDCA_ENT_HID01,
RT721_SDCA_CTL_HIDTX_CURRENT_OWNER, 0) ... SDW_SDCA_CTL(FUNC_NUM_HID,
RT721_SDCA_ENT_HID01, RT721_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0):
case RT721_BUF_ADDR_HID1 ... RT721_BUF_ADDR_HID2:
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0):
return true;
default:
return false;
@@ -79,6 +91,7 @@ static bool rt721_sdca_mbq_readable_register(struct device *dev, unsigned int re
case 0x5810037:
case 0x5810038:
case 0x5810039:
+ case 0x5810100:
case 0x5b10018:
case 0x5b10019:
case 0x5f00045:
@@ -155,6 +168,7 @@ static bool rt721_sdca_mbq_volatile_register(struct device *dev, unsigned int re
case 0x5810037:
case 0x5810038:
case 0x5810039:
+ case 0x5810100:
case 0x5b10018:
case 0x5b10019:
case 0x6100006:
@@ -436,6 +450,7 @@ static void rt721_sdca_sdw_remove(struct sdw_slave *slave)
static const struct sdw_device_id rt721_sdca_id[] = {
SDW_SLAVE_ENTRY_EXT(0x025d, 0x721, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x718, 0x3, 0x1, 0),
{},
};
MODULE_DEVICE_TABLE(sdw, rt721_sdca_id);
diff --git a/sound/soc/codecs/rt721-sdca.c b/sound/soc/codecs/rt721-sdca.c
index 738644018fb9..ef003c93f533 100644
--- a/sound/soc/codecs/rt721-sdca.c
+++ b/sound/soc/codecs/rt721-sdca.c
@@ -142,151 +142,312 @@ io_error:
static void rt721_sdca_dmic_preset(struct rt721_sdca_priv *rt721)
{
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_MISC_POWER_CTL31, 0x8000);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
- RT721_VREF1_HV_CTRL1, 0xe000);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_MISC_POWER_CTL31, 0x8007);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL9, 0x2a2a);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL10, 0x2a00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL6, 0x2a2a);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL5, 0x2626);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL8, 0x1e00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL7, 0x1515);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_CH_FLOAT_CTL3, 0x0304);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_CH_FLOAT_CTL4, 0x0304);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_HDA_LEGACY_CTL1, 0x0000);
- regmap_write(rt721->regmap,
- SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
- RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
- regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
- RT721_RC_CALIB_CTRL0, 0x0b00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
- RT721_RC_CALIB_CTRL0, 0x0b40);
- regmap_write(rt721->regmap, 0x2f5c, 0x25);
+ unsigned int mic_func_status;
+ struct device *dev = &rt721->slave->dev;
+
+ regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT0, RT721_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) || (!rt721->first_hw_init)) {
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL9, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL10, 0x2a00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL6, 0x2a2a);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL5, 0x2626);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL8, 0x1e00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL7, 0x1515);
+ if (rt721->wf_id == RT721_S)
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL3, 0x0304);
+ else
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL5, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL4, 0x0304);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_HDA_LEGACY_CTL1, 0x0000);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_IT26,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ regmap_write(rt721->mbq_regmap, 0x5910009, 0x2e01);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ if (rt721->wf_id == RT721_S) {
+ regmap_write(rt721->regmap, 0x2f5c, 0x25);
+ } else {
+ regmap_write(rt721->regmap, 0x2f5c, 0x01);
+ regmap_write(rt721->regmap, 0x2f52, 0x00);
+ }
+ /* clear flag */
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0), FUNCTION_NEEDS_INITIALIZATION);
+ }
}
static void rt721_sdca_amp_preset(struct rt721_sdca_priv *rt721)
{
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_MISC_POWER_CTL31, 0x8000);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
- RT721_VREF1_HV_CTRL1, 0xe000);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_MISC_POWER_CTL31, 0x8007);
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
- regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_CH_FLOAT_CTL6, 0x5561);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_REG,
- RT721_GPIO_PAD_CTRL5, 0x8003);
- regmap_write(rt721->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
- RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
- regmap_write(rt721->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
- RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
- regmap_write(rt721->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
- RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
- regmap_write(rt721->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
- RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
- regmap_write(rt721->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
- RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
- regmap_write(rt721->regmap, 0x2f5d, 0x1);
+ unsigned int amp_func_status;
+ struct device *dev = &rt721->slave->dev;
+
+ regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT0, RT721_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) || (!rt721->first_hw_init)) {
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ if (rt721->wf_id == RT721_S) {
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6420);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x6421);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0xe421);
+ }
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_CH_FLOAT_CTL6, 0x5561);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_REG,
+ RT721_GPIO_PAD_CTRL5, 0x8003);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_OT23,
+ RT721_SDCA_CTL_VENDOR_DEF, 0), 0x04);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_PDE23,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_01), 0x00);
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_FU55,
+ RT721_SDCA_CTL_FU_MUTE, CH_02), 0x00);
+ regmap_write(rt721->regmap, 0x2f5d, 0x1);
+ if (rt721->wf_id == RT721_U)
+ regmap_write(rt721->regmap, 0x2f54, 0x00);
+ /* clear flag */
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT0, RT721_SDCA_CTL_FUNC_STATUS, 0),
+ FUNCTION_NEEDS_INITIALIZATION);
+ }
}
static void rt721_sdca_jack_preset(struct rt721_sdca_priv *rt721)
{
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_MISC_POWER_CTL31, 0x8000);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
- RT721_VREF1_HV_CTRL1, 0xe000);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_MISC_POWER_CTL31, 0x8007);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_GE_REL_CTRL1, 0x8011);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_UMP_HID_CTRL3, 0xcf00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_UMP_HID_CTRL4, 0x000f);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_UMP_HID_CTRL1, 0x1100);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_UMP_HID_CTRL5, 0x0c12);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_JD_CTRL,
- RT721_JD_1PIN_GAT_CTRL2, 0xc002);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
- RT721_RC_CALIB_CTRL0, 0x0b00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
- RT721_RC_CALIB_CTRL0, 0x0b40);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_UAJ_TOP_TCON14, 0x3333);
- regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
- regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
- RT721_HP_AMP_2CH_CAL1, 0x0140);
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
- regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
- RT721_HP_AMP_2CH_CAL18, 0x5522);
- regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
- regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
- RT721_CBJ_A0_GAT_CTRL1, 0x2205);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
- RT721_HP_AMP_2CH_CAL4, 0xa105);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_UAJ_TOP_TCON14, 0x3b33);
- regmap_write(rt721->mbq_regmap, 0x310400, 0x3043);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_UAJ_TOP_TCON14, 0x3f33);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_UAJ_TOP_TCON13, 0x6048);
- regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
- regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
- regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
- regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
- regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
- RT721_UAJ_TOP_TCON17, 0x0008);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
- RT721_DAC_2CH_CTRL3, 0x55ff);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
- RT721_DAC_2CH_CTRL4, 0xcc00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
- RT721_MBIAS_LV_CTRL2, 0x6677);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
- RT721_VREF2_LV_CTRL1, 0x7600);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL2, 0x1234);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL3, 0x3512);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL1, 0x4040);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_ENT_FLOAT_CTL4, 0x1201);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_BOOST_CTRL,
- RT721_BST_4CH_TOP_GATING_CTRL1, 0x002a);
- regmap_write(rt721->regmap, 0x2f58, 0x07);
-
- regmap_write(rt721->regmap, 0x2f51, 0x00);
- rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
- RT721_MISC_CTL, 0x0004);
+ unsigned int jack_func_status;
+ struct device *dev = &rt721->slave->dev;
+
+ regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT0, RT721_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) || (!rt721->first_hw_init)) {
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x8011);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL3, 0xcf00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL4, 0x000f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL1, 0x1100);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL5, 0x0c12);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_JD_CTRL,
+ RT721_JD_1PIN_GAT_CTRL2, 0xc002);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3333);
+ regmap_write(rt721->mbq_regmap, 0x5810035, 0x0036);
+ regmap_write(rt721->mbq_regmap, 0x5810030, 0xee00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL1, 0x0140);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x0021);
+ regmap_write(rt721->mbq_regmap, 0x5810000, 0x8021);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL18, 0x5522);
+ regmap_write(rt721->mbq_regmap, 0x5b10007, 0x2000);
+ regmap_write(rt721->mbq_regmap, 0x5B10017, 0x1b0f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
+ RT721_CBJ_A0_GAT_CTRL1, 0x2205);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL4, 0xa105);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3b33);
+ regmap_write(rt721->mbq_regmap, 0x310400, 0x3043);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3f33);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON13, 0x6048);
+ regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
+ regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
+ regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON17, 0x0008);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL3, 0x55ff);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_DAC_CTRL,
+ RT721_DAC_2CH_CTRL4, 0xcc00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_MBIAS_LV_CTRL2, 0x6677);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF2_LV_CTRL1, 0x7600);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL2, 0x1234);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL3, 0x3512);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL1, 0x4040);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL4, 0x1201);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_BOOST_CTRL,
+ RT721_BST_4CH_TOP_GATING_CTRL1, 0x002a);
+ regmap_write(rt721->regmap, 0x2f58, 0x07);
+ regmap_write(rt721->regmap, 0x2f51, 0x00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_MISC_CTL, 0x0004);
+ /* clear flag */
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0), FUNCTION_NEEDS_INITIALIZATION);
+ }
+}
+
+static void rt721u_sdca_jack_preset(struct rt721_sdca_priv *rt721)
+{
+ unsigned int jack_func_status, calibration_status, i;
+ int ret;
+ struct device *dev = &rt721->slave->dev;
+
+ regmap_read(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT0, RT721_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 & BIT(5)) || (!rt721->first_hw_init)) {
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_ANA_POW_PART,
+ RT721_VREF1_HV_CTRL1, 0xe000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_MISC_POWER_CTL31, 0x8007);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ 0x10, 0xffb7);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ 0x20, 0xffb7);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ 0x30, 0xffb7);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON13, 0x6048);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_GE_REL_CTRL1, 0x8011);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL3, 0xcf00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL4, 0x000f);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL1, 0x1100);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_UMP_HID_CTRL5, 0x0c12);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_JD_CTRL,
+ RT721_JD_1PIN_GAT_CTRL2, 0xc004);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_RC_CALIB_CTRL,
+ RT721_RC_CALIB_CTRL0, 0x0b40);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x333b);
+ regmap_write(rt721->mbq_regmap, 0x5810135, 0x8036);
+ regmap_write(rt721->mbq_regmap, 0x5810130, 0xee03);
+ regmap_write(rt721->mbq_regmap, 0x5810133, 0x6000);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL1, 0x4140);
+ regmap_write(rt721->mbq_regmap, 0x5810100, 0x0006);
+ regmap_write(rt721->mbq_regmap, 0x5810100, 0x8006);
+ for (i = 0; i < 5; i++) {
+ ret = regmap_read(rt721->mbq_regmap, 0x5810100,
+ &calibration_status);
+ if (ret < 0)
+ dev_dbg(dev, "calibration failed!, ret=%d\n",
+ ret);
+ if (!(calibration_status & 0x8000))
+ break;
+ }
+ if (rt721->hw_vid == RT721_U_VB)
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
+ RT721_CBJ_A0_GAT_CTRL1, 0x2205);
+ else
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CBJ_CTRL,
+ RT721_CBJ_A0_GAT_CTRL1, 0x2e05);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3b3b);
+ regmap_write(rt721->mbq_regmap, 0x310400, 0x3043);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON14, 0x3f33);
+ regmap_write(rt721->mbq_regmap, 0x310401, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310402, 0x1b00);
+ regmap_write(rt721->mbq_regmap, 0x310300, 0x000f);
+ regmap_write(rt721->mbq_regmap, 0x310301, 0x3000);
+ regmap_write(rt721->mbq_regmap, 0x310302, 0x1b00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON17, 0x0008);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL2, 0x1234);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL3, 0x3512);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL1, 0x4040);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_HDA_SDCA_FLOAT,
+ RT721_ENT_FLOAT_CTL4, 0x1201);
+ regmap_write(rt721->regmap, 0x2f58, 0x01);
+ regmap_write(rt721->regmap, 0x2f59, 0x01);
+ regmap_write(rt721->regmap, 0x2f51, 0x00);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_VENDOR_ANA_CTL,
+ RT721_UAJ_TOP_TCON13, 0x6040);
+ regmap_write(rt721->mbq_regmap, 0x910001, 0x3256);
+ regmap_write(rt721->mbq_regmap, 0x910002, 0x0000);
+ regmap_write(rt721->mbq_regmap, 0x910003, 0x0000);
+ regmap_write(rt721->mbq_regmap, 0x900004, 0x7778);
+ regmap_write(rt721->mbq_regmap, 0x910202, 0x02e4);
+ regmap_write(rt721->mbq_regmap, 0x800002, 0xcc04);
+ rt_sdca_index_write(rt721->mbq_regmap, RT721_CAP_PORT_CTRL,
+ RT721_HP_AMP_2CH_CAL4, 0xa105);
+ rt_sdca_index_write(rt721->mbq_regmap, 0x0b, 0x01, 0x0000);
+ /* clear flag */
+ regmap_write(rt721->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT0,
+ RT721_SDCA_CTL_FUNC_STATUS, 0), FUNCTION_NEEDS_INITIALIZATION);
+ }
}
static void rt721_sdca_jack_init(struct rt721_sdca_priv *rt721)
@@ -692,19 +853,47 @@ static int rt721_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv_s, -6525, 75, 0);
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv_u, -9225, 75, 0);
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0);
static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0);
static const DECLARE_TLV_DB_SCALE(mic2_boost_vol_tlv, -200, 200, 0);
-static const struct snd_kcontrol_new rt721_sdca_controls[] = {
+static const struct snd_kcontrol_new rt721s_sdca_controls[] = {
/* Headphone playback settings */
SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
RT721_SDCA_CTL_FU_VOLUME, CH_L),
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
- rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv_s),
+ /* AMP playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv_s),
+};
+
+static const struct snd_kcontrol_new rt721u_sdca_controls[] = {
+ /* Headphone playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_USER_FU05,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x7b, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv_u),
+ /* AMP playback settings */
+ SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_L),
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
+ RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x7b, 0,
+ rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv_u),
+};
+
+static const struct snd_kcontrol_new rt721_sdca_controls[] = {
/* Headset mic capture settings */
SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0,
rt721_sdca_fu0f_capture_get, rt721_sdca_fu0f_capture_put),
@@ -720,13 +909,6 @@ static const struct snd_kcontrol_new rt721_sdca_controls[] = {
SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT721_SDCA_ENT_PLATFORM_FU44,
RT721_SDCA_CTL_FU_CH_GAIN, CH_R), 1, 0x15, 0,
rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, mic2_boost_vol_tlv),
- /* AMP playback settings */
- SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
- RT721_SDCA_CTL_FU_VOLUME, CH_L),
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT721_SDCA_ENT_USER_FU06,
- RT721_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0,
- rt721_sdca_set_gain_get, rt721_sdca_set_gain_put, out_vol_tlv),
/* DMIC capture settings */
RT_SDCA_FU_CTRL("FU1E Capture Switch",
SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT721_SDCA_ENT_USER_FU1E,
@@ -1235,6 +1417,15 @@ static int rt721_sdca_probe(struct snd_soc_component *component)
if (ret < 0 && ret != -EACCES)
return ret;
+ if (rt721->wf_id == RT721_S)
+ snd_soc_add_component_controls(component,
+ rt721s_sdca_controls,
+ ARRAY_SIZE(rt721s_sdca_controls));
+ else
+ snd_soc_add_component_controls(component,
+ rt721u_sdca_controls,
+ ARRAY_SIZE(rt721u_sdca_controls));
+
return 0;
}
@@ -1492,6 +1683,7 @@ int rt721_sdca_init(struct device *dev, struct regmap *regmap,
rt721->fu0f_mixer_l_mute = rt721->fu0f_mixer_r_mute = true;
rt721->fu1e_mixer_mute[0] = rt721->fu1e_mixer_mute[1] =
rt721->fu1e_mixer_mute[2] = rt721->fu1e_mixer_mute[3] = true;
+ rt721->wf_id = -1;
return devm_snd_soc_register_component(dev,
&soc_sdca_dev_rt721, rt721_sdca_dai, ARRAY_SIZE(rt721_sdca_dai));
@@ -1509,6 +1701,7 @@ static void rt721_sdca_reset(struct rt721_sdca_priv *rt721)
int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
{
struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
+ int val;
rt721->disable_irq = false;
@@ -1540,12 +1733,23 @@ int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave)
pm_runtime_get_noresume(&slave->dev);
+ if (rt721->wf_id < 0) {
+ rt_sdca_index_read(rt721->mbq_regmap, RT721_VENDOR_REG,
+ RT721_JD_PRODUCT_NUM, &val);
+ rt721->wf_id = (val >> 4) & 0xf;
+ rt721->hw_vid = val & 0xf;
+ dev_dbg(&slave->dev, "wf_id = %d hw_vid = %d\n", rt721->wf_id, rt721->hw_vid);
+ }
+
if (!rt721->first_hw_init)
rt721_sdca_reset(rt721);
rt721_sdca_dmic_preset(rt721);
rt721_sdca_amp_preset(rt721);
- rt721_sdca_jack_preset(rt721);
+ if (rt721->wf_id == RT721_S)
+ rt721_sdca_jack_preset(rt721);
+ else
+ rt721u_sdca_jack_preset(rt721);
if (rt721->hs_jack && (!rt721->first_hw_init))
rt721_sdca_jack_init(rt721);
diff --git a/sound/soc/codecs/rt721-sdca.h b/sound/soc/codecs/rt721-sdca.h
index 24ce188562ba..16cf40cd88de 100644
--- a/sound/soc/codecs/rt721-sdca.h
+++ b/sound/soc/codecs/rt721-sdca.h
@@ -40,6 +40,8 @@ struct rt721_sdca_priv {
/* For DMIC */
bool fu1e_dapm_mute;
bool fu1e_mixer_mute[4];
+ int wf_id;
+ int hw_vid;
};
struct rt721_sdca_dmic_kctrl_priv {
@@ -214,6 +216,7 @@ struct rt721_sdca_dmic_kctrl_priv {
#define RT721_SDCA_ENT_XU03 0x03
#define RT721_SDCA_ENT_XU0D 0x0d
#define RT721_SDCA_ENT_FU55 0x55
+#define RT721_SDCA_ENT0 0x00
/* RT721 SDCA control */
#define RT721_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
@@ -229,6 +232,7 @@ struct rt721_sdca_dmic_kctrl_priv {
#define RT721_SDCA_CTL_VENDOR_DEF 0x30
#define RT721_SDCA_CTL_XUV 0x34
#define RT721_SDCA_CTL_FU_CH_GAIN 0x0b
+#define RT721_SDCA_CTL_FUNC_STATUS 0x10
/* RT721 SDCA channel */
#define CH_L 0x01
@@ -261,6 +265,9 @@ struct rt721_sdca_dmic_kctrl_priv {
/* RT721 HID ID */
#define RT721_SDCA_HID_ID 0x11
+/* Function_Status */
+#define FUNCTION_NEEDS_INITIALIZATION BIT(5)
+
enum {
RT721_AIF1, /* For headset mic and headphone */
RT721_AIF2, /* For speaker */
@@ -268,6 +275,16 @@ enum {
RT721_AIFS,
};
+enum rt721_wf_id {
+ RT721_S,
+ RT721_U,
+};
+
+enum rt721u_hw_ver {
+ RT721_U_VB = 1,
+ RT721_U_VD = 3,
+};
+
int rt721_sdca_io_init(struct device *dev, struct sdw_slave *slave);
int rt721_sdca_init(struct device *dev, struct regmap *regmap,
struct regmap *mbq_regmap, struct sdw_slave *slave);
diff --git a/sound/soc/codecs/rt766-sdca.c b/sound/soc/codecs/rt766-sdca.c
index 5a1c1e10e6d7..28b4118e05c9 100644
--- a/sound/soc/codecs/rt766-sdca.c
+++ b/sound/soc/codecs/rt766-sdca.c
@@ -1297,7 +1297,7 @@ static int rt766_func_initialize(struct rt766_sdca_priv *rt766, struct sdca_func
regmap_read(rt766->regmap, func_status_reg, &func_status);
dev_dbg(dev, "%s, %s func_status=0x%x\n", __func__, func_data->desc->name, func_status);
- if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION) || (!rt766->first_hw_init)) {
+ if ((func_status & SDCA_CTL_ENTITY_0_FUNCTION_NEEDS_INITIALIZATION)) {
ret = sdca_regmap_write_init(dev, rt766->regmap, func_data);
if (ret) {
dev_err(dev, "%s initialization table update failed\n", func_data->desc->name);
diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c
index e51cd4a31894..1445e823c4ff 100644
--- a/sound/soc/codecs/rt9120.c
+++ b/sound/soc/codecs/rt9120.c
@@ -289,9 +289,18 @@ static int rt9120_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 rt9120_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops rt9120_dai_ops = {
.set_fmt = rt9120_set_fmt,
.hw_params = rt9120_hw_params,
+ .auto_selectable_formats = &rt9120_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt9120_dai = {
diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c
index 07eaf275c9e9..251bad450ec8 100644
--- a/sound/soc/codecs/rt9123.c
+++ b/sound/soc/codecs/rt9123.c
@@ -299,10 +299,23 @@ static int rt9123_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+/*
+ * NOTE
+ *
+ * auto_select doesn't include _DSP_A / _DSP_B, because it has limitaion for TDM
+ * (= rt9123_dai_hw_params()). It can be selected via Card directly if needed.
+ */
+static const u64 rt9123_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
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,
+ .auto_selectable_formats = &rt9123_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rt9123_dai_driver = {
diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c
index 2a041894bc0c..cc1c68752fde 100644
--- a/sound/soc/codecs/rtq9124.c
+++ b/sound/soc/codecs/rtq9124.c
@@ -286,10 +286,19 @@ static int rtq9124_dai_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 rtq9124_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
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,
+ .auto_selectable_formats = &rtq9124_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rtq9124_dai_driver = {
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
index 573200e5062f..9567e90c0b62 100644
--- a/sound/soc/codecs/rtq9128.c
+++ b/sound/soc/codecs/rtq9128.c
@@ -727,11 +727,20 @@ static int rtq9128_dai_mute_stream(struct snd_soc_dai *dai, int mute, int stream
return ret < 0 ? ret : 0;
}
+static const u64 rtq9128_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops rtq9128_dai_ops = {
.set_fmt = rtq9128_dai_set_fmt,
.set_tdm_slot = rtq9128_dai_set_tdm_slot,
.hw_params = rtq9128_dai_hw_params,
.mute_stream = rtq9128_dai_mute_stream,
+ .auto_selectable_formats = &rtq9128_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 35df1a8c4c8c..97bea75ed24f 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1158,12 +1158,23 @@ static int sgtl5000_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 sgtl5000_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops sgtl5000_ops = {
.hw_params = sgtl5000_pcm_hw_params,
.mute_stream = sgtl5000_mute_stream,
.set_fmt = sgtl5000_set_dai_fmt,
.set_sysclk = sgtl5000_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &sgtl5000_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver sgtl5000_dai = {
diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c
index d87141ba8438..9b904450ab8a 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -199,9 +199,27 @@ out:
return err;
}
+static const u64 si476x_selectable_formats[] = {
+ /* Hi Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* Low Priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops si476x_dai_ops = {
.hw_params = si476x_codec_hw_params,
.set_fmt = si476x_codec_set_dai_fmt,
+ .auto_selectable_formats = si476x_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(si476x_selectable_formats),
};
static struct snd_soc_dai_driver si476x_dai = {
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
index 15758eb13f8c..0bff32559714 100644
--- a/sound/soc/codecs/sma1303.c
+++ b/sound/soc/codecs/sma1303.c
@@ -1412,12 +1412,25 @@ static int sma1303_dai_set_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
+static const u64 sma1303_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops sma1303_dai_ops_amp = {
.set_sysclk = sma1303_dai_set_sysclk_amp,
.set_fmt = sma1303_dai_set_fmt_amp,
.hw_params = sma1303_dai_hw_params_amp,
.mute_stream = sma1303_dai_mute,
.set_tdm_slot = sma1303_dai_set_tdm_slot,
+ .auto_selectable_formats = &sma1303_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define SMA1303_RATES SNDRV_PCM_RATE_8000_192000
diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c
index adb369a29b9d..57f34f2c5efb 100644
--- a/sound/soc/codecs/sma1307.c
+++ b/sound/soc/codecs/sma1307.c
@@ -1552,12 +1552,25 @@ static int sma1307_dai_mute_stream(struct snd_soc_dai *dai, int mute,
return 0;
}
+static const u64 sma1307_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops sma1307_dai_ops_amp = {
.hw_params = sma1307_dai_hw_params_amp,
.set_fmt = sma1307_dai_set_fmt_amp,
.set_sysclk = sma1307_dai_set_sysclk_amp,
.set_tdm_slot = sma1307_dai_set_tdm_slot,
.mute_stream = sma1307_dai_mute_stream,
+ .auto_selectable_formats = &sma1307_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define SMA1307_RATES_PLAYBACK SNDRV_PCM_RATE_8000_96000
@@ -1701,7 +1714,9 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil
__func__, setting_file, ERR_PTR(ret));
sma1307->set.status = false;
return;
- } else if ((fw->size) < SMA1307_SETTING_HEADER_SIZE) {
+ } else if (fw->size % sizeof(int) ||
+ fw->size < (SMA1307_SETTING_HEADER_SIZE +
+ SMA1307_SETTING_DEFAULT_SIZE) * sizeof(int)) {
dev_err(sma1307->dev, "%s: Invalid file\n", __func__);
sma1307->set.status = false;
return;
@@ -1720,6 +1735,10 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil
sma1307->set.checksum = data[sma1307->set.header_size - 2];
sma1307->set.num_mode = data[sma1307->set.header_size - 1];
num_mode = sma1307->set.num_mode;
+ if (num_mode < 0 || num_mode > ARRAY_SIZE(sma1307->set.mode_set)) {
+ sma1307->set.status = false;
+ return;
+ }
sma1307->set.header = devm_kmalloc_array(sma1307->dev,
sma1307->set.header_size,
sizeof(int),
@@ -1755,7 +1774,11 @@ static void sma1307_setting_loaded(struct sma1307_priv *sma1307, const char *fil
/* MODE */
offset = sma1307->set.header_size + sma1307->set.def_size;
- sma1307->set.mode_size = DIV_ROUND_CLOSEST(size - offset, num_mode + 1);
+ if ((size - offset) % (num_mode + 1)) {
+ sma1307->set.status = false;
+ return;
+ }
+ sma1307->set.mode_size = (size - offset) / (num_mode + 1);
for (int i = 0; i < num_mode; i++) {
sma1307->set.mode_set[i]
= devm_kzalloc(sma1307->dev,
diff --git a/sound/soc/codecs/sn624x-sdca-sdw.c b/sound/soc/codecs/sn624x-sdca-sdw.c
new file mode 100644
index 000000000000..c9e690482e8e
--- /dev/null
+++ b/sound/soc/codecs/sn624x-sdca-sdw.c
@@ -0,0 +1,1888 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// sn624x-sdw-sdca.c -- SN624X SDCA ALSA SoC SoundWire audio driver
+//
+// Copyright(c) 2025 Senary Semiconductor Corp.
+//
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/find.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+
+#include <sound/jack.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/sdca_regmap.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "sn624x-sdca.h"
+
+/* Resume wait after bus re-enumeration (rt722-style probe timeout) */
+#define SN624X_PROBE_TIMEOUT_MS 5000U
+#define SN624X_PWR_D0 SDCA_PDE_PS0
+#define SN624X_PWR_D3 SDCA_PDE_PS3
+#define SN624X_MUTE_ON 1
+/*
+ * Budget for ACTUAL_PS poll via sdca_asoc_pde_poll_actual_ps() (100 polls).
+ * Matches previous ~2s hand-rolled loop for slow PDE transitions.
+ */
+#define SN624X_PDE_PS_POLL_BUDGET_US 2000000U
+
+/*
+ * Prefer ACPI mipi-sdca-function-initialization-table via
+ * sdca_regmap_write_init(). If absent or DisCo parse is incomplete,
+ * a temporary vendor-window OEM fallback is used until firmware tables land.
+ *
+ * Jack detection is poll-driven (UAJ pulse + GE35); SDCA INTMASK bits stay
+ * masked so interrupt storms cannot schedule extra work.
+ */
+
+struct sn624x_sdca_priv;
+
+static void sn624x_sdca_sdca_irq_mask_all(struct sn624x_sdca_priv *sn624x);
+static int sn624x_uaj_ge35_apply_detected_mode(struct device *dev,
+ struct sn624x_sdca_priv *sn624x);
+#define SN624X_DBG(dev, fmt, ...) dev_dbg(dev, "sn624x: " fmt, ##__VA_ARGS__)
+
+/*
+ * SDCA control addresses via SDW_SDCA_CTL(fun, ent, ctl, ch).
+ * Pass ch=0 for single-value controls (power/rate/status); use CH_L/CH_R
+ * only for multi-channel FU mute/volume. CH_ENABLE_* are not SDCA Control
+ * Prefix addresses — keep as raw windows.
+ */
+/* Speaker amp (function 4) — PDE/FU power/mute/vol; rate on CS01 */
+#define SN624X_REG_PWR_STATE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_SPEAKER_AMP, SN624X_SDCA_ENT_PDE03, \
+ SDCA_CTL_PDE_ACTUAL_PS, 0)
+#define SN624X_REG_PWR_STATE_WRITE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_SPEAKER_AMP, SN624X_SDCA_ENT_PDE03, \
+ SDCA_CTL_PDE_REQUESTED_PS, 0)
+#define SN624X_REG_RATE_SEL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_SPEAKER_AMP, SN624X_SDCA_ENT_CS01, \
+ SN624X_SDCA_CTL_SAMPLE_FREQ_INDEX, 0)
+#define SN624X_REG_CH_ENABLE_A 0x00000220u
+#define SN624X_REG_CH_ENABLE_B 0x00000230u
+#define SN624X_REG_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_SPEAKER_AMP, SN624X_SDCA_ENT_USER_FU10, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_L)
+#define SN624X_REG_CHR_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_SPEAKER_AMP, SN624X_SDCA_ENT_USER_FU10, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_R)
+#define SN624X_REG_MUTE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_SPEAKER_AMP, SN624X_SDCA_ENT_USER_FU10, \
+ SN624X_SDCA_CTL_FU_MUTE, SN624X_SDCA_CH_L)
+#define SN624X_REG_RIGHT_MUTE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_SPEAKER_AMP, SN624X_SDCA_ENT_USER_FU10, \
+ SN624X_SDCA_CTL_FU_MUTE, SN624X_SDCA_CH_R)
+
+/* Jack playback (function 1) */
+#define SN624X_REG_JACK_OUT_PWR_STATE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_PDE03, \
+ SDCA_CTL_PDE_ACTUAL_PS, 0)
+#define SN624X_REG_JACK_OUT_PWR_STATE_WRITE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_PDE03, \
+ SDCA_CTL_PDE_REQUESTED_PS, 0)
+#define SN624X_REG_JACK_OUT_RATE_SEL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_CS01, \
+ SN624X_SDCA_CTL_SAMPLE_FREQ_INDEX, 0)
+#define SN624X_REG_JACK_OUT_CH_ENABLE_A 0x00000120u
+#define SN624X_REG_JACK_OUT_CH_ENABLE_B 0x00000130u
+#define SN624X_REG_JACK_OUT_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_USER_FU14, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_L)
+#define SN624X_REG_JACK_OUT_CHR_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_USER_FU14, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_R)
+#define SN624X_REG_JACK_OUT_MUTE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_USER_FU14, \
+ SN624X_SDCA_CTL_FU_MUTE, SN624X_SDCA_CH_L)
+#define SN624X_REG_JACK_OUT_CLUSTER_INDEX \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_CRU05, \
+ SN624X_SDCA_CTL_CLUSTER_INDEX, 0)
+
+/* Jack headset capture (function 1) */
+#define SN624X_REG_JACK_CAP_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_USER_FU1E, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_L)
+#define SN624X_REG_JACK_CAP_CHR_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_USER_FU1E, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_R)
+#define SN624X_REG_JACK_CAP_MUTE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_USER_FU1E, \
+ SN624X_SDCA_CTL_FU_MUTE, SN624X_SDCA_CH_L)
+#define SN624X_REG_JACK_CAP_PWR_STATE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_PDE04, \
+ SDCA_CTL_PDE_ACTUAL_PS, 0)
+#define SN624X_REG_JACK_CAP_PWR_STATE_WRITE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_PDE04, \
+ SDCA_CTL_PDE_REQUESTED_PS, 0)
+#define SN624X_REG_JACK_CAP_RATE_SEL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_CS02, \
+ SN624X_SDCA_CTL_SAMPLE_FREQ_INDEX, 0)
+#define SN624X_REG_JACK_CAP_CH_ENABLE_A 0x00000420u /* low bits: 0x3=both */
+#define SN624X_REG_JACK_CAP_CH_ENABLE_B 0x00000430u
+
+#define SN624X_REG_JACK_FUN_STATUS_CTL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_ENTITY0, \
+ SN624X_SDCA_CTL_FUNCTION_STATUS, 0)
+#define SN624X_JACK_FUN_NEEDS_INIT BIT(5)
+#define SN624X_JACK_FUN_HAS_BEEN_RESET BIT(6)
+
+/* DMIC capture (function 2) */
+#define SN624X_REG_DMIC_CAP_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_USER_FU14, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_L)
+#define SN624X_REG_DMIC_CAP_CHR_VOL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_USER_FU14, \
+ SN624X_SDCA_CTL_FU_VOLUME, SN624X_SDCA_CH_R)
+/* Q7.8 channel volume: integer in bits 15:8, fraction in bits 7:0 */
+#define SN624X_VOL_Q78(int_part, frac_part) \
+ ((((unsigned int)(int_part) & 0xff) << 8) | ((unsigned int)(frac_part) & 0xff))
+#define SN624X_REG_DMIC_CAP_MUTE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_USER_FU14, \
+ SN624X_SDCA_CTL_FU_MUTE, SN624X_SDCA_CH_L)
+#define SN624X_REG_DMIC_CAP_CHR_MUTE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_USER_FU14, \
+ SN624X_SDCA_CTL_FU_MUTE, SN624X_SDCA_CH_R)
+#define SN624X_REG_DMIC_CAP_RATE_SEL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_CS02, \
+ SN624X_SDCA_CTL_SAMPLE_FREQ_INDEX, 0)
+/* OEM sheet used ctl=0; SDCA Actual PS is 0x10 — poll the standard selector */
+#define SN624X_REG_DMIC_CAP_PWR_STATE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_PDE03, \
+ SDCA_CTL_PDE_ACTUAL_PS, 0)
+#define SN624X_REG_DMIC_CAP_PWR_STATE_WRITE \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_PDE03, \
+ SDCA_CTL_PDE_REQUESTED_PS, 0)
+#define SN624X_REG_DMIC_CAP_CH_ENABLE_A 0x00000520u /* low bits: 0x3=both */
+#define SN624X_REG_DMIC_CAP_CH_ENABLE_B 0x00000530u
+
+#define SN624X_REG_DMIC_FUN_STATUS_CTL \
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_MIC_ARRAY, SN624X_SDCA_ENT_ENTITY0, \
+ SN624X_SDCA_CTL_FUNCTION_STATUS, 0)
+
+/*
+ * Write REQUESTED_PS then poll ACTUAL_PS with the shared SDCA helper
+ * (sdca_asoc_pde_poll_actual_ps). Caller must not hand-roll ACTUAL_PS waits.
+ */
+static int sn624x_pde_request_ps(struct device *dev, struct regmap *regmap,
+ unsigned int fun, unsigned int ent,
+ unsigned int ps);
+
+/*
+ * DAPM PDE supplies (rt711/rt712 style): write REQUESTED_PS then poll ACTUAL_PS.
+ * Mute-before-D3 is handled by FU widget PRE_PMD, which runs before SUPPLY PRE_PMD
+ * when the FU lists the PDE as a dependency.
+ */
+static int sn624x_dapm_pde_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event,
+ unsigned int fun, unsigned int ent)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sn624x_sdca_priv *sn624x =
+ snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ int ret;
+
+ if (!sn624x || !sn624x->regmap)
+ return -ENODEV;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = sn624x_pde_request_ps(dev, sn624x->regmap, fun, ent,
+ SN624X_PWR_D0);
+ if (ret < 0)
+ dev_warn(dev,
+ "sn624x: DAPM %s PDE fun=%u ent=0x%x -> PS0 failed (%d)\n",
+ w->name, fun, ent, ret);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = sn624x_pde_request_ps(dev, sn624x->regmap, fun, ent,
+ SN624X_PWR_D3);
+ if (ret < 0)
+ dev_warn(dev,
+ "sn624x: DAPM %s PDE fun=%u ent=0x%x -> PS3 failed (%d)\n",
+ w->name, fun, ent, ret);
+ break;
+ default:
+ return 0;
+ }
+ return ret;
+}
+
+static int sn624x_pde_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return sn624x_dapm_pde_event(w, kcontrol, event,
+ SN624X_FUNC_NUM_JACK_CODEC,
+ SN624X_SDCA_ENT_PDE03);
+}
+
+static int sn624x_pde_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return sn624x_dapm_pde_event(w, kcontrol, event,
+ SN624X_FUNC_NUM_SPEAKER_AMP,
+ SN624X_SDCA_ENT_PDE03);
+}
+
+static int sn624x_pde_mic2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return sn624x_dapm_pde_event(w, kcontrol, event,
+ SN624X_FUNC_NUM_JACK_CODEC,
+ SN624X_SDCA_ENT_PDE04);
+}
+
+static int sn624x_pde_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return sn624x_dapm_pde_event(w, kcontrol, event,
+ SN624X_FUNC_NUM_MIC_ARRAY,
+ SN624X_SDCA_ENT_PDE03);
+}
+
+static int sn624x_fu_mute_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event,
+ unsigned int mute_l, unsigned int mute_r,
+ unsigned int vol_l, unsigned int vol_r,
+ unsigned int vol_val)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_to_component(w->dapm);
+ struct sn624x_sdca_priv *sn624x =
+ snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ int ret;
+
+ if (!sn624x || !sn624x->regmap)
+ return -ENODEV;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (vol_l) {
+ ret = regmap_write(sn624x->regmap, vol_l, vol_val);
+ if (ret < 0)
+ dev_warn(dev, "sn624x: DAPM %s vol(L) failed (%d)\n",
+ w->name, ret);
+ }
+ if (vol_r) {
+ ret = regmap_write(sn624x->regmap, vol_r, vol_val);
+ if (ret < 0)
+ dev_warn(dev, "sn624x: DAPM %s vol(R) failed (%d)\n",
+ w->name, ret);
+ }
+ ret = regmap_write(sn624x->regmap, mute_l, 0);
+ if (ret < 0)
+ dev_warn(dev, "sn624x: DAPM %s unmute(L) failed (%d)\n",
+ w->name, ret);
+ if (mute_r) {
+ ret = regmap_write(sn624x->regmap, mute_r, 0);
+ if (ret < 0)
+ dev_warn(dev, "sn624x: DAPM %s unmute(R) failed (%d)\n",
+ w->name, ret);
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Mute and settle before PDE SUPPLY PRE_PMD takes the domain to PS3 */
+ ret = regmap_write(sn624x->regmap, mute_l, SN624X_MUTE_ON);
+ if (ret < 0)
+ dev_warn(dev, "sn624x: DAPM %s mute(L) failed (%d)\n",
+ w->name, ret);
+ if (mute_r) {
+ ret = regmap_write(sn624x->regmap, mute_r, SN624X_MUTE_ON);
+ if (ret < 0)
+ dev_warn(dev, "sn624x: DAPM %s mute(R) failed (%d)\n",
+ w->name, ret);
+ }
+ usleep_range(SN624X_ROUTE_AFTER_MUTE_US_MIN,
+ SN624X_ROUTE_AFTER_MUTE_US_MAX);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int sn624x_fu_hp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return sn624x_fu_mute_event(w, kcontrol, event,
+ SN624X_REG_JACK_OUT_MUTE, 0,
+ SN624X_REG_JACK_OUT_VOL,
+ SN624X_REG_JACK_OUT_CHR_VOL, 0);
+}
+
+static int sn624x_fu_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return sn624x_fu_mute_event(w, kcontrol, event,
+ SN624X_REG_MUTE, SN624X_REG_RIGHT_MUTE,
+ SN624X_REG_VOL, SN624X_REG_CHR_VOL, 0);
+}
+
+static int sn624x_fu_mic2_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 sn624x_sdca_priv *sn624x =
+ snd_soc_component_get_drvdata(component);
+ int ret;
+
+ if (event == SND_SOC_DAPM_POST_PMU && sn624x)
+ sn624x_uaj_ge35_apply_detected_mode(component->dev, sn624x);
+
+ ret = sn624x_fu_mute_event(w, kcontrol, event,
+ SN624X_REG_JACK_CAP_MUTE, 0,
+ SN624X_REG_JACK_CAP_VOL,
+ SN624X_REG_JACK_CAP_CHR_VOL,
+ SN624X_VOL_Q78(6, 0));
+ return ret;
+}
+
+static int sn624x_fu_dmic_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ return sn624x_fu_mute_event(w, kcontrol, event,
+ SN624X_REG_DMIC_CAP_MUTE,
+ SN624X_REG_DMIC_CAP_CHR_MUTE,
+ SN624X_REG_DMIC_CAP_VOL,
+ SN624X_REG_DMIC_CAP_CHR_VOL,
+ SN624X_VOL_Q78(6, 0));
+}
+
+static int sn624x_pde_request_ps(struct device *dev, struct regmap *regmap,
+ unsigned int fun, unsigned int ent,
+ unsigned int ps)
+{
+ static const struct sdca_pde_delay delays[] = {
+ { .from_ps = SDCA_PDE_PS0, .to_ps = SDCA_PDE_PS3,
+ .us = SN624X_PDE_PS_POLL_BUDGET_US },
+ { .from_ps = SDCA_PDE_PS3, .to_ps = SDCA_PDE_PS0,
+ .us = SN624X_PDE_PS_POLL_BUDGET_US },
+ { .from_ps = SDCA_PDE_PS1, .to_ps = SDCA_PDE_PS0,
+ .us = SN624X_PDE_PS_POLL_BUDGET_US },
+ { .from_ps = SDCA_PDE_PS2, .to_ps = SDCA_PDE_PS0,
+ .us = SN624X_PDE_PS_POLL_BUDGET_US },
+ { .from_ps = SDCA_PDE_PS0, .to_ps = SDCA_PDE_PS0,
+ .us = SN624X_PDE_PS_POLL_BUDGET_US },
+ { .from_ps = SDCA_PDE_PS3, .to_ps = SDCA_PDE_PS3,
+ .us = SN624X_PDE_PS_POLL_BUDGET_US },
+ };
+ unsigned int req = SDW_SDCA_CTL(fun, ent, SDCA_CTL_PDE_REQUESTED_PS, 0);
+ unsigned int act = SDW_SDCA_CTL(fun, ent, SDCA_CTL_PDE_ACTUAL_PS, 0);
+ unsigned int from_ps = SDCA_PDE_PS3;
+ int ret;
+
+ ret = regmap_read(regmap, act, &from_ps);
+ if (ret < 0) {
+ dev_warn(dev,
+ "sn624x: PDE fun=%u ent=0x%x ACTUAL_PS read failed (%d)\n",
+ fun, ent, ret);
+ from_ps = (ps == SN624X_PWR_D0) ? SN624X_PWR_D3 : SN624X_PWR_D0;
+ } else {
+ from_ps &= 0xffu;
+ }
+
+ ret = regmap_write(regmap, req, ps);
+ if (ret < 0) {
+ dev_warn(dev,
+ "sn624x: PDE fun=%u ent=0x%x REQUESTED_PS=%u write failed (%d)\n",
+ fun, ent, ps, ret);
+ return ret;
+ }
+
+ ret = sdca_asoc_pde_poll_actual_ps(regmap, fun, ent,
+ from_ps, ps,
+ delays, ARRAY_SIZE(delays));
+ if (ret < 0)
+ dev_warn(dev,
+ "sn624x: PDE fun=%u ent=0x%x ACTUAL_PS poll failed (%d; want %u)\n",
+ fun, ent, ret, ps);
+ return ret;
+}
+
+static void sn624x_jack_type_from_det_mode(struct sn624x_sdca_priv *sn624x,
+ unsigned int det_mode)
+{
+ switch (det_mode) {
+ case 0x00:
+ sn624x->jack_type = 0;
+ break;
+ case 0x03:
+ sn624x->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x05:
+ sn624x->jack_type = SND_JACK_HEADSET;
+ break;
+ default:
+ SN624X_DBG(&sn624x->slave->dev,
+ "unknown GE35 detected_mode 0x%x\n", det_mode);
+ sn624x->jack_type = 0;
+ break;
+ }
+}
+
+static int sn624x_uaj_ge35_select_mode(struct device *dev,
+ struct sn624x_sdca_priv *sn624x,
+ unsigned int mode)
+{
+ int ret;
+
+ ret = regmap_write(sn624x->regmap,
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC,
+ SN624X_SDCA_ENT_GE35,
+ SN624X_SDCA_CTL_SELECTED_MODE, 0),
+ mode);
+ if (ret < 0)
+ dev_warn(dev,
+ "sn624x: jack_cap: GE35 SELECTED_MODE=0x%x failed (%d)\n",
+ mode, ret);
+ else
+ dev_dbg(dev,
+ "sn624x: jack_cap: GE35 SELECTED_MODE=0x%x\n",
+ mode);
+ return ret;
+}
+
+/*
+ * Follow DETECTED_MODE → SELECTED_MODE (rt711/rt722 pattern). Never force
+ * headset: mic path is only valid when the jack hardware reports one.
+ * Also updates sn624x->jack_type from DETECTED_MODE.
+ */
+static int sn624x_uaj_ge35_apply_detected_mode(struct device *dev,
+ struct sn624x_sdca_priv *sn624x)
+{
+ unsigned int det_mode = 0;
+ int ret;
+
+ ret = regmap_read(sn624x->regmap,
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC,
+ SN624X_SDCA_ENT_GE35,
+ SN624X_SDCA_CTL_DETECTED_MODE, 0),
+ &det_mode);
+ if (ret < 0) {
+ dev_warn(dev,
+ "sn624x: jack_cap: GE35 DETECTED_MODE read failed (%d)\n",
+ ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "sn624x: jack_cap: GE35 DETECTED_MODE=0x%x\n", det_mode);
+ sn624x_jack_type_from_det_mode(sn624x, det_mode);
+
+ if (!det_mode) {
+ dev_dbg(dev,
+ "sn624x: jack_cap: no jack detected — skip SELECTED_MODE\n");
+ return 0;
+ }
+
+ return sn624x_uaj_ge35_select_mode(dev, sn624x, det_mode);
+}
+
+static const char *sn624x_status_str(enum sdw_slave_status status)
+{
+ switch (status) {
+ case SDW_SLAVE_UNATTACHED:
+ return "UNATTACHED";
+ case SDW_SLAVE_ATTACHED:
+ return "ATTACHED";
+ case SDW_SLAVE_ALERT:
+ return "ALERT";
+ case SDW_SLAVE_RESERVED:
+ return "RESERVED";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static int sn624x_sdca_mbq_size(struct device *dev, unsigned int reg)
+{
+ if (!SDW_SDCA_VALID_CTL(reg))
+ return 1;
+
+ /*
+ * FU_VOLUME and GE35 DETECTED_MODE share control selector 0x02.
+ * Only list known FU volume addresses as 16-bit MBQ — matching
+ * DETECTED_MODE by csel alone makes GE35 use SDW_SDCA_MBQ_CTL and
+ * fails with -ENODATA (-61), which breaks jack plug/unplug.
+ */
+ switch (reg) {
+ case SN624X_REG_VOL:
+ case SN624X_REG_CHR_VOL:
+ case SN624X_REG_JACK_OUT_VOL:
+ case SN624X_REG_JACK_OUT_CHR_VOL:
+ case SN624X_REG_JACK_CAP_VOL:
+ case SN624X_REG_JACK_CAP_CHR_VOL:
+ case SN624X_REG_DMIC_CAP_VOL:
+ case SN624X_REG_DMIC_CAP_CHR_VOL:
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static bool sn624x_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ if (!SDW_SDCA_VALID_CTL(reg))
+ return true;
+
+ switch (reg) {
+ case SN624X_REG_PWR_STATE:
+ case SN624X_REG_JACK_OUT_PWR_STATE:
+ case SN624X_REG_JACK_CAP_PWR_STATE:
+ case SN624X_REG_DMIC_CAP_PWR_STATE:
+ case SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_SDCA_ENT_GE35,
+ SN624X_SDCA_CTL_DETECTED_MODE, 0):
+ return true;
+ default:
+ if (SDW_SDCA_CTL_CSEL(reg) == SDCA_CTL_PDE_ACTUAL_PS)
+ return true;
+ return false;
+ }
+}
+
+static const struct regmap_sdw_mbq_cfg sn624x_sdca_mbq_cfg = {
+ .mbq_size = sn624x_sdca_mbq_size,
+};
+
+static const struct regmap_config sn624x_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+ .volatile_reg = sn624x_sdca_volatile_register,
+ .max_register = 0xffffffff,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+/*
+ * Prefer ACPI mipi-sdca-function-initialization-table (sdca_regmap_write_init).
+ * sdca_parse_function() loads that table before entity parsing, so a later
+ * DisCo parse failure can still leave fn->num_init_table usable.
+ */
+static int sn624x_write_sdca_init_table(struct device *dev,
+ struct sn624x_sdca_priv *sn624x,
+ struct sdca_function_data *fn,
+ const char *label)
+{
+ int ret;
+
+ if (!fn || !fn->num_init_table)
+ return -ENOENT;
+
+ ret = sdca_regmap_write_init(dev, sn624x->regmap, fn);
+ if (ret < 0) {
+ dev_warn(dev,
+ "sn624x: %s function-initialization-table write failed (%d)\n",
+ label, ret);
+ return ret;
+ }
+
+ dev_info(dev,
+ "sn624x: applied %s function-initialization-table (%d writes)\n",
+ label, fn->num_init_table);
+ return 0;
+}
+
+/*
+ * Temporary vendor-window fallback when ACPI has no init table.
+ * Validated on current platforms; remove once DisCo init tables are complete.
+ */
+static int sn624x_jack_oem_fallback_init(struct device *dev,
+ struct sn624x_sdca_priv *sn624x)
+{
+ static const struct reg_sequence jack_oem_table[] = {
+ REG_SEQ0(SN624X_UAJ_CTL_IT33_MIC_BIAS, 0x06),
+ REG_SEQ0(SN624X_UAJ_CTL_IT31_MIC_BIAS, 0x06),
+ REG_SEQ0(SN624X_UAJ_CTL_FU41_CH1_MUTE, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU41_CH2_MUTE, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU31_CH0_MUTE, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU32_CH0_MUTE, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU33_CH0_MUTE, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU36_CH1_MUTE, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU36_CH2_MUTE, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU31_CH0_GAIN_HIGH, 0x12),
+ REG_SEQ0(SN624X_UAJ_CTL_FU31_CH0_GAIN_LOW, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU32_CH0_GAIN_HIGH, 0x0C),
+ REG_SEQ0(SN624X_UAJ_CTL_FU32_CH0_GAIN_LOW, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_FU33_CH0_GAIN_HIGH, 0x12),
+ REG_SEQ0(SN624X_UAJ_CTL_FU33_CH0_GAIN_LOW, 0x00),
+ REG_SEQ0(SN624X_UAJ_CTL_INPUT_DELAY_TIME, 0x0F),
+ REG_SEQ0(SN624X_UAJ_CTL_PORTA_CHARGE_PUMP, 0x0B),
+ };
+ int ret;
+
+ dev_warn_once(dev,
+ "sn624x: using temporary jack OEM init fallback (no ACPI init table)\n");
+ ret = regmap_multi_reg_write_bypassed(sn624x->regmap, jack_oem_table,
+ ARRAY_SIZE(jack_oem_table));
+ if (ret < 0) {
+ dev_warn(dev, "sn624x: jack OEM fallback failed (%d)\n", ret);
+ return ret;
+ }
+ usleep_range(1000, 1500);
+ return 0;
+}
+
+static int sn624x_dmic_oem_fallback_init(struct device *dev,
+ struct sn624x_sdca_priv *sn624x)
+{
+ static const struct reg_sequence dmic_oem_table[] = {
+ REG_SEQ0(0x00002041, 0x00),
+ REG_SEQ0(SN624X_UAJ_DMIC_PPU11, 0x30),
+ REG_SEQ0(0x00002105, 0x66),
+ REG_SEQ0(0x00002107, 0x26),
+ REG_SEQ0(0x00002109, 0x62),
+ REG_SEQ0(0x40800a09, 0x00),
+ REG_SEQ0(0x40800a0a, 0x00),
+ REG_SEQ0(0x40802a11, 0x00),
+ REG_SEQ0(0x40800a11, 0x00),
+ REG_SEQ0(0x40802a12, 0x00),
+ REG_SEQ0(0x40800a12, 0x00),
+ REG_SEQ0(0x40802a13, 0x00),
+ REG_SEQ0(0x40800a13, 0x00),
+ REG_SEQ0(0x40802a14, 0x00),
+ REG_SEQ0(0x40800a14, 0x00),
+ REG_SEQ0(0x408029d8, 0x0c),
+ REG_SEQ0(0x408009d8, 0x00),
+ REG_SEQ0(0x408029d9, 0x0c),
+ REG_SEQ0(0x408009d9, 0x00),
+ REG_SEQ0(0x00002140, 0x18),
+ REG_SEQ0(0x00002045, 0x01),
+ };
+ int ret;
+
+ dev_warn_once(dev,
+ "sn624x: using temporary dmic OEM init fallback (no ACPI init table)\n");
+ ret = regmap_multi_reg_write_bypassed(sn624x->regmap, dmic_oem_table,
+ ARRAY_SIZE(dmic_oem_table));
+ if (ret < 0) {
+ dev_warn(dev, "sn624x: dmic OEM fallback failed (%d)\n", ret);
+ return ret;
+ }
+ usleep_range(1000, 1500);
+ return 0;
+}
+
+static void sn624x_uaj_apply_io_defaults(struct sn624x_sdca_priv *sn624x)
+{
+ struct device *dev = &sn624x->slave->dev;
+ int ret;
+
+ ret = sn624x_write_sdca_init_table(dev, sn624x, sn624x->jack_func,
+ "jack");
+ if (ret)
+ sn624x_jack_oem_fallback_init(dev, sn624x);
+}
+
+static int sn624x_parse_sdca_functions(struct sn624x_sdca_priv *sn624x)
+{
+ struct sdw_slave *slave = sn624x->slave;
+ struct device *dev = &slave->dev;
+ int i, ret;
+
+ if (!slave->sdca_data.num_functions) {
+ dev_dbg(dev, "sn624x: no SDCA function descriptors from DisCo\n");
+ return 0;
+ }
+
+ for (i = 0; i < slave->sdca_data.num_functions; i++) {
+ struct sdca_function_desc *desc = &slave->sdca_data.function[i];
+ struct sdca_function_data *fn;
+ bool is_jack = false, is_mic = false, is_amp = false;
+
+ switch (desc->type) {
+ case SDCA_FUNCTION_TYPE_UAJ:
+ case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
+ case SDCA_FUNCTION_TYPE_RJ:
+ is_jack = true;
+ break;
+ case SDCA_FUNCTION_TYPE_SMART_MIC:
+ case SDCA_FUNCTION_TYPE_SIMPLE_MIC:
+ is_mic = true;
+ break;
+ case SDCA_FUNCTION_TYPE_SMART_AMP:
+ case SDCA_FUNCTION_TYPE_SIMPLE_AMP:
+ case SDCA_FUNCTION_TYPE_SPEAKER_MIC:
+ is_amp = true;
+ break;
+ default:
+ break;
+ }
+
+ if (desc->adr == SN624X_FUNC_NUM_JACK_CODEC)
+ is_jack = true;
+ else if (desc->adr == SN624X_FUNC_NUM_MIC_ARRAY)
+ is_mic = true;
+ else if (desc->adr == SN624X_FUNC_NUM_SPEAKER_AMP)
+ is_amp = true;
+
+ if (!is_jack && !is_mic && !is_amp)
+ continue;
+
+ fn = devm_kzalloc(dev, sizeof(*fn), GFP_KERNEL);
+ if (!fn)
+ return -ENOMEM;
+
+ fn->desc = desc;
+ ret = sdca_parse_function(dev, fn);
+ if (ret) {
+ /*
+ * sdca_parse_function() reads the ACPI
+ * mipi-sdca-function-initialization-table first,
+ * then parses entities/controls. On some platforms
+ * DisCo entity data is incomplete so the later
+ * steps fail (-EINVAL), but fn->init_table is
+ * already filled. Keep that partial function so
+ * io_init can still apply the ACPI table; if the
+ * table was never loaded, drop this function and
+ * let the OEM register fallback run instead.
+ */
+ dev_warn(dev,
+ "sn624x: sdca_parse_function(%s adr=%u) failed (%d)%s\n",
+ desc->name ? desc->name : "?", desc->adr, ret,
+ fn->num_init_table ?
+ ", keeping ACPI init table" : "");
+ if (!fn->num_init_table)
+ continue;
+ } else {
+ dev_dbg(dev,
+ "sn624x: parsed SDCA function %s type=%u adr=%u init_writes=%d\n",
+ desc->name ? desc->name : "?", desc->type,
+ desc->adr, fn->num_init_table);
+ }
+
+ if (is_jack && !sn624x->jack_func)
+ sn624x->jack_func = fn;
+ else if (is_mic && !sn624x->mic_func)
+ sn624x->mic_func = fn;
+ else if (is_amp && !sn624x->amp_func)
+ sn624x->amp_func = fn;
+ }
+
+ return 0;
+}
+
+/*
+ * Poll-driven jack detect: pulse UAJ jack-detect, then apply GE35
+ * DETECTED_MODE → SELECTED_MODE via sn624x_uaj_ge35_apply_detected_mode().
+ */
+static int sn624x_sdca_headset_detect(struct sn624x_sdca_priv *sn624x)
+{
+ struct device *dev = &sn624x->slave->dev;
+ int ret;
+
+ SN624X_DBG(dev, "headset_detect: pulse UAJ jack-detect, read GE35\n");
+ ret = regmap_write(sn624x->regmap,
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC,
+ SN624X_UAJ_ENT_BLOCK,
+ SN624X_UAJ_CTL_JACK_DETECT, 0),
+ 0x08);
+ if (ret < 0)
+ goto io_error;
+
+ usleep_range(5000, 5500);
+
+ ret = sn624x_uaj_ge35_apply_detected_mode(dev, sn624x);
+
+ /* Clear the detect pulse even if GE35 apply failed. */
+ regmap_write(sn624x->regmap,
+ SDW_SDCA_CTL(SN624X_FUNC_NUM_JACK_CODEC, SN624X_UAJ_ENT_BLOCK,
+ SN624X_UAJ_CTL_JACK_DETECT, 0),
+ 0x00);
+
+ if (ret < 0)
+ goto io_error;
+
+ SN624X_DBG(dev, "headset_detect: jack_type=0x%x\n", sn624x->jack_type);
+ return 0;
+
+io_error:
+ if (ret == -ENODATA)
+ SN624X_DBG(dev, "%s: UAJ/GE35 access ignored (-ENODATA)\n",
+ __func__);
+ else
+ dev_err_ratelimited(dev, "%s failed (%d)\n", __func__, ret);
+ return ret;
+}
+
+static void sn624x_sdca_sdca_irq_mask_all(struct sn624x_sdca_priv *sn624x)
+{
+ if (!sn624x->slave)
+ return;
+
+ sdw_write_no_pm(sn624x->slave, SDW_SCP_SDCA_INTMASK1, 0);
+ sdw_write_no_pm(sn624x->slave, SDW_SCP_SDCA_INTMASK2, 0);
+ sdw_write_no_pm(sn624x->slave, SDW_SCP_SDCA_INTMASK3, 0);
+ sdw_write_no_pm(sn624x->slave, SDW_SCP_SDCA_INTMASK4, 0);
+ SN624X_DBG(&sn624x->slave->dev,
+ "SDCA INTMASK1-4 = 0 (all SDCA sources masked)\n");
+}
+
+static void sn624x_sdca_jack_irq_unmask(struct sn624x_sdca_priv *sn624x)
+{
+ /* Jack detection is poll-driven; SDCA jack IRQs remain masked. */
+ (void)sn624x;
+}
+
+static void sn624x_sdca_jack_irq_mask(struct sn624x_sdca_priv *sn624x)
+{
+ if (!sn624x->slave)
+ return;
+
+ sn624x_sdca_sdca_irq_mask_all(sn624x);
+}
+
+/*
+ * Hold a runtime PM reference for the lifetime of jack detection so the
+ * device cannot autosuspend while the poll worker needs register access.
+ */
+static int sn624x_jack_rpm_get(struct sn624x_sdca_priv *sn624x)
+{
+ struct device *dev;
+ int ret;
+
+ if (!sn624x)
+ return -EINVAL;
+ if (sn624x->jack_rpm)
+ return 0;
+ if (!sn624x->component)
+ return -ENODEV;
+
+ dev = sn624x->component->dev;
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(dev, "sn624x: jack rpm get failed (%d)\n", ret);
+ return ret;
+ }
+ /* pm_runtime not enabled yet (before first ATTACHED io_init) */
+ return 0;
+ }
+
+ sn624x->jack_rpm = true;
+ return 0;
+}
+
+static void sn624x_jack_rpm_put(struct sn624x_sdca_priv *sn624x)
+{
+ if (!sn624x || !sn624x->jack_rpm || !sn624x->component)
+ return;
+
+ pm_runtime_mark_last_busy(sn624x->component->dev);
+ pm_runtime_put_autosuspend(sn624x->component->dev);
+ sn624x->jack_rpm = false;
+}
+
+static void sn624x_jack_schedule_poll(struct sn624x_sdca_priv *sn624x)
+{
+ if (!sn624x || !sn624x->hs_jack || !sn624x->slave)
+ return;
+
+ mod_delayed_work(system_power_efficient_wq, &sn624x->jack_detect_work,
+ msecs_to_jiffies(SN624X_JACK_POLL_MS));
+}
+
+static void sn624x_sdca_jack_poll_and_report(struct sn624x_sdca_priv *sn624x)
+{
+ struct device *dev;
+
+ if (!sn624x->hs_jack || !sn624x->slave)
+ return;
+
+ dev = &sn624x->slave->dev;
+ if (sn624x_sdca_headset_detect(sn624x) < 0)
+ return;
+
+ snd_soc_jack_report(sn624x->hs_jack, sn624x->jack_type,
+ SND_JACK_HEADSET | SND_JACK_HEADPHONE);
+ sn624x->jack_type_last = sn624x->jack_type;
+ dev_dbg(dev, "sn624x: jack polled: detected_mode -> jack_type=0x%x\n",
+ sn624x->jack_type);
+}
+
+static void sn624x_sdca_jack_detect_handler(struct work_struct *work)
+{
+ struct sn624x_sdca_priv *sn624x =
+ container_of(work, struct sn624x_sdca_priv, jack_detect_work.work);
+ struct device *dev;
+
+ if (!sn624x->slave) {
+ sn624x_jack_schedule_poll(sn624x);
+ return;
+ }
+
+ dev = &sn624x->slave->dev;
+
+ if (!sn624x->hs_jack) {
+ SN624X_DBG(dev, "jack_work: skip (no hs_jack)\n");
+ goto out_reschedule;
+ }
+
+ /* Registers are only valid after ATTACHED → io_init (hw_init). */
+ if (!sn624x->hw_init) {
+ SN624X_DBG(dev, "jack_work: skip (hw_init not ready)\n");
+ goto out_reschedule;
+ }
+
+ /* Poll GE35 every interval; do not rely on SDCA_0 IRQ or PCM activity */
+ sn624x_sdca_headset_detect(sn624x);
+
+ if (sn624x->jack_type != sn624x->jack_type_last) {
+ dev_dbg(dev,
+ "sn624x: jack event: %s (jack_type=0x%x; HP=0x%x HS=0x%x)\n",
+ sn624x->jack_type ? "plug" : "unplug",
+ sn624x->jack_type, SND_JACK_HEADPHONE, SND_JACK_HEADSET);
+ sn624x->jack_type_last = sn624x->jack_type;
+
+ snd_soc_jack_report(sn624x->hs_jack, sn624x->jack_type,
+ SND_JACK_HEADSET | SND_JACK_HEADPHONE);
+ }
+
+out_reschedule:
+ sn624x_jack_schedule_poll(sn624x);
+}
+
+static void sn624x_sdca_jack_init(struct sn624x_sdca_priv *sn624x)
+{
+ /* No machine set_jack() yet — not "jack unsupported". */
+ if (!sn624x->hs_jack)
+ return;
+
+ sn624x_sdca_jack_irq_unmask(sn624x);
+ sn624x_sdca_jack_poll_and_report(sn624x);
+ sn624x_jack_schedule_poll(sn624x);
+
+ dev_dbg(&sn624x->slave->dev,
+ "sn624x: jack_init: poll scheduled every %ums\n", SN624X_JACK_POLL_MS);
+}
+
+static int sn624x_sdca_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct sn624x_sdca_priv *sn624x = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ dev_dbg(component->dev,
+ "sn624x: set_jack: hs_jack=%p first_hw_init=%d hw_init=%d\n",
+ hs_jack, sn624x->first_hw_init, sn624x->hw_init);
+
+ if (!hs_jack) {
+ cancel_delayed_work_sync(&sn624x->jack_detect_work);
+ sn624x->hs_jack = NULL;
+ sn624x_jack_rpm_put(sn624x);
+ return 0;
+ }
+
+ /*
+ * Component registration happens before runtime PM is enabled (RPM is
+ * enabled from io_init on first ATTACHED). Do not touch runtime PM
+ * until then, or get/put counts can go out of sync.
+ */
+ if (!sn624x->first_hw_init) {
+ sn624x->hs_jack = hs_jack;
+ sn624x->jack_type_last = -1;
+ sn624x_jack_schedule_poll(sn624x);
+ return 0;
+ }
+
+ /*
+ * Take the runtime PM reference before publishing hs_jack, so a
+ * failed get cannot leave jack detection marked as enabled.
+ */
+ ret = sn624x_jack_rpm_get(sn624x);
+ if (ret < 0)
+ return ret;
+
+ sn624x->hs_jack = hs_jack;
+ sn624x->jack_type_last = -1;
+ sn624x_sdca_jack_init(sn624x);
+ return 0;
+}
+
+/* Prefer ACPI init table; OEM vendor-window fallback only if absent. */
+static int sn624x_jack_apply_defaults(struct device *dev,
+ struct sn624x_sdca_priv *sn624x)
+{
+ int ret;
+
+ if (!sn624x || !sn624x->regmap || !sn624x->slave)
+ return -ENODEV;
+
+ ret = sn624x_write_sdca_init_table(dev, sn624x, sn624x->jack_func,
+ "jack");
+ if (ret == 0) {
+ usleep_range(1000, 1500);
+ return 0;
+ }
+
+ return sn624x_jack_oem_fallback_init(dev, sn624x);
+}
+
+static int sn624x_dmic_apply_defaults(struct device *dev,
+ struct sn624x_sdca_priv *sn624x)
+{
+ int ret;
+
+ if (!sn624x || !sn624x->regmap || !sn624x->slave)
+ return -ENODEV;
+
+ ret = sn624x_write_sdca_init_table(dev, sn624x, sn624x->mic_func,
+ "dmic");
+ if (ret == 0) {
+ usleep_range(1000, 1500);
+ return 0;
+ }
+
+ return sn624x_dmic_oem_fallback_init(dev, sn624x);
+}
+
+static int sn624x_sdca_jack_function_init(struct device *dev,
+ struct sn624x_sdca_priv *sn624x)
+{
+ unsigned int jack_func_status;
+ const struct reg_sequence clear_status =
+ REG_SEQ0(SN624X_REG_JACK_FUN_STATUS_CTL, 0x61);
+ int ret;
+
+ ret = regmap_read_bypassed(sn624x->regmap,
+ SN624X_REG_JACK_FUN_STATUS_CTL,
+ &jack_func_status);
+ if (ret < 0)
+ return ret;
+
+ if (!(jack_func_status & (SN624X_JACK_FUN_NEEDS_INIT |
+ SN624X_JACK_FUN_HAS_BEEN_RESET)))
+ return 0;
+
+ ret = sn624x_jack_apply_defaults(dev, sn624x);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_multi_reg_write_bypassed(sn624x->regmap, &clear_status, 1);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 1500);
+ return 0;
+}
+
+static int sn624x_sdca_dmic_function_init(struct device *dev,
+ struct sn624x_sdca_priv *sn624x)
+{
+ unsigned int dmic_func_status;
+ const struct reg_sequence clear_status =
+ REG_SEQ0(SN624X_REG_DMIC_FUN_STATUS_CTL, 0x61);
+ int ret;
+
+ ret = regmap_read_bypassed(sn624x->regmap,
+ SN624X_REG_DMIC_FUN_STATUS_CTL,
+ &dmic_func_status);
+ if (ret < 0)
+ return ret;
+
+ if (!(dmic_func_status & (SN624X_JACK_FUN_NEEDS_INIT |
+ SN624X_JACK_FUN_HAS_BEEN_RESET)))
+ return 0;
+
+ ret = sn624x_dmic_apply_defaults(dev, sn624x);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_multi_reg_write_bypassed(sn624x->regmap, &clear_status, 1);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(1000, 1500);
+ return 0;
+}
+
+static int sn624x_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(dev);
+ int ret;
+
+ if (sn624x->hw_init) {
+ SN624X_DBG(dev, "io_init: skip (hw_init already true)\n");
+ return 0;
+ }
+
+ SN624X_DBG(dev, "io_init: start (first hardware bring-up)\n");
+ sn624x->disable_irq = false;
+
+ sn624x_sdca_sdca_irq_mask_all(sn624x);
+
+ regcache_cache_only(sn624x->regmap, false);
+ if (!sn624x->first_hw_init) {
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ * (same pattern as rt722).
+ */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_enable(dev);
+ }
+
+ pm_runtime_get_noresume(dev);
+
+ sn624x_uaj_apply_io_defaults(sn624x);
+
+ /*
+ * set_jack() may have run before RPM was enabled (-EACCES). Take the
+ * long-lived jack PM ref now that runtime PM is active.
+ */
+ if (sn624x->hs_jack) {
+ sn624x_jack_rpm_get(sn624x);
+ sn624x_sdca_jack_init(sn624x);
+ }
+
+ ret = sn624x_sdca_jack_function_init(dev, sn624x);
+ if (ret < 0) {
+ dev_err(dev, "sn624x: jack function init failed (%d)\n", ret);
+ goto err;
+ }
+
+ ret = sn624x_sdca_dmic_function_init(dev, sn624x);
+ if (ret < 0) {
+ dev_err(dev, "sn624x: dmic function init failed (%d)\n", ret);
+ goto err;
+ }
+
+ /*
+ * Re-attach after bus reset: force a full cache write-back on the
+ * following resume/sync path (same idea as rt722).
+ */
+ if (sn624x->first_hw_init)
+ regcache_mark_dirty(sn624x->regmap);
+
+ sn624x->hw_init = true;
+ sn624x->first_hw_init = true;
+
+ pm_runtime_put_autosuspend(dev);
+
+ SN624X_DBG(dev, "io_init: complete (hw_init set), jack %s\n",
+ sn624x->hs_jack ? "registered" : "not registered yet");
+ return 0;
+
+err:
+ pm_runtime_put_noidle(dev);
+ return ret;
+}
+
+/*
+ * Bus lifecycle (same as rt722): enumerate → assign dev_num → ATTACHED →
+ * io_init(). Do not poll enumeration/init around each register access; after
+ * unattach, resume uses sdw_slave_wait_for_init() then regcache_sync.
+ */
+static int sn624x_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(&slave->dev);
+ bool need_io_init;
+ int ret;
+
+ SN624X_DBG(&slave->dev, "update_status: %s hw_init=%d dev_num=%u\n",
+ sn624x_status_str(status), sn624x->hw_init, slave->dev_num);
+
+ if (status == SDW_SLAVE_UNATTACHED) {
+ cancel_delayed_work_sync(&sn624x->jack_detect_work);
+ sn624x->hw_init = false;
+ SN624X_DBG(&slave->dev,
+ "slave UNATTACHED: cleared hw_init, jack work cancelled\n");
+ }
+
+ if (status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ sn624x_sdca_sdca_irq_mask_all(sn624x);
+
+ need_io_init = !sn624x->hw_init;
+ if (need_io_init) {
+ dev_notice(&slave->dev, "sn624x: update_status ATTACHED -> io_init()\n");
+ ret = sn624x_sdca_io_init(&slave->dev, slave);
+ if (ret < 0)
+ return ret;
+ } else {
+ dev_dbg(&slave->dev,
+ "sn624x: update_status ATTACHED: io_init already done\n");
+ }
+
+ /* Re-ATTACHED without io_init: restore jack detect after bus is up. */
+ if (sn624x->hs_jack && sn624x->hw_init && !need_io_init) {
+ sn624x_sdca_jack_irq_unmask(sn624x);
+ sn624x_sdca_jack_poll_and_report(sn624x);
+ sn624x_jack_schedule_poll(sn624x);
+ }
+
+ return 0;
+}
+
+static int sn624x_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ struct sdw_dpn_prop *dpn;
+ unsigned long addr;
+ unsigned int bit;
+ int nval;
+ int i, j;
+
+ sdw_slave_read_lane_mapping(slave);
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->paging_support = true;
+ prop->use_domain_irq = true;
+ prop->wake_capable = true;
+
+ prop->source_ports = BIT(SN624X_PORT_JACK_CAPTURE) |
+ BIT(SN624X_PORT_DMIC_CAPTURE);
+ prop->sink_ports = BIT(SN624X_PORT_JACK_PLAYBACK) |
+ BIT(SN624X_PORT_SPEAKER_PLAYBACK);
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+ prop->clk_stop_timeout = 900;
+ prop->simple_clk_stop_capable = true;
+ prop->lane_control_support = true;
+ SN624X_DBG(&slave->dev,
+ "read_prop: src_ports=0x%x sink_ports=0x%x lane irq paging\n",
+ prop->source_ports, prop->sink_ports);
+
+ return 0;
+}
+
+/*
+ * Clear SDCA INT1/2 only. DP0_INT standard fields (PORT_READY, etc.) are
+ * owned by the SoundWire bus core; cascade clears when SDCA status is clear.
+ * Pending SDCA IRQs can block clock-stop prepare (CLK_STP_NF -> -ETIMEDOUT).
+ */
+static int sn624x_sdca_flush_pending_irqs(struct sn624x_sdca_priv *sn624x,
+ struct device *dev)
+{
+ int ret, stat = 0;
+ int count = 0;
+ const int retry = 3;
+ unsigned int scp_sdca_stat1, scp_sdca_stat2 = 0;
+
+ if (!sn624x || !sn624x->slave)
+ return 0;
+
+ ret = sdw_read_no_pm(sn624x->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ return ret;
+ sn624x->scp_sdca_stat1 = ret;
+
+ ret = sdw_read_no_pm(sn624x->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ return ret;
+ sn624x->scp_sdca_stat2 = ret;
+
+ do {
+ ret = sdw_read_no_pm(sn624x->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ return ret;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) {
+ ret = sdw_write_no_pm(sn624x->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INTMASK_SDCA_0);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sdw_read_no_pm(sn624x->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ return ret;
+ if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) {
+ ret = sdw_write_no_pm(sn624x->slave, SDW_SCP_SDCA_INT2,
+ SDW_SCP_SDCA_INTMASK_SDCA_8);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = sdw_read_no_pm(sn624x->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ return ret;
+ scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0;
+
+ ret = sdw_read_no_pm(sn624x->slave, SDW_SCP_SDCA_INT2);
+ if (ret < 0)
+ return ret;
+ scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8;
+
+ stat = scp_sdca_stat1 || scp_sdca_stat2;
+ count++;
+ } while (stat != 0 && count < retry);
+
+ if (stat && dev)
+ dev_warn(dev,
+ "sn624x: SDCA IRQ flush incomplete (stat=%u)\n", stat);
+
+ return stat ? -EBUSY : 0;
+}
+
+static int sn624x_sdca_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(&slave->dev);
+ int ret;
+
+ if (!sn624x)
+ return 0;
+
+ /*
+ * Bus expects the codec to clear SCP SDCA INT1/2 when cascade is set.
+ * Leaving them set can stall alert handling.
+ */
+
+ SN624X_DBG(&slave->dev,
+ "irq: control_port=%#x sdca_cascade=%u disable_irq=%d\n",
+ status->control_port, status->sdca_cascade, sn624x->disable_irq);
+
+ cancel_delayed_work_sync(&sn624x->jack_detect_work);
+
+ mutex_lock(&sn624x->disable_irq_lock);
+
+ ret = sn624x_sdca_flush_pending_irqs(sn624x, &slave->dev);
+ if (ret < 0 && ret != -EBUSY)
+ goto io_error;
+
+ SN624X_DBG(&slave->dev, "irq: INT1=0x%x INT2=0x%x\n",
+ sn624x->scp_sdca_stat1, sn624x->scp_sdca_stat2);
+
+ /*
+ * Always restart the jack poll after IRQ handling so a cancel above
+ * cannot stop detection. SDCA INTMASK stays masked; any residual SCP
+ * IRQ still triggers an early poll. The poll itself does the UAJ
+ * pulse + GE35 read.
+ */
+ if (!sn624x->disable_irq)
+ sn624x_jack_schedule_poll(sn624x);
+
+ mutex_unlock(&sn624x->disable_irq_lock);
+ return 0;
+
+io_error:
+ mutex_unlock(&sn624x->disable_irq_lock);
+ dev_err_ratelimited(&slave->dev, "%s: IO error (%d)\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops sn624x_sdca_slave_ops = {
+ .read_prop = sn624x_sdca_read_prop,
+ .interrupt_callback = sn624x_sdca_interrupt_callback,
+ .update_status = sn624x_sdca_update_status,
+};
+
+static int sn624x_sdca_probe(struct snd_soc_component *component)
+{
+ struct sn624x_sdca_priv *sn624x = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ sn624x->component = component;
+
+ if (!sn624x->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static int sn624x_sdca_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 sn624x_sdca_priv *sn624x = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {};
+ struct sdw_port_config port_config = {};
+ struct sdw_stream_runtime *sdw_stream;
+ unsigned int sampling_rate;
+ int port;
+ int ret;
+
+ dev_dbg(dai->dev,
+ "sn624x: hw_params: entered dai=%s id=%d stream=%s\n",
+ dai->name, dai->id, snd_pcm_stream_str(substream));
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!sdw_stream) {
+ dev_warn(dai->dev,
+ "sn624x: hw_params: no SDW stream (set_stream not run yet?)\n");
+ return -EINVAL;
+ }
+ if (!sn624x->slave) {
+ dev_warn(dai->dev, "sn624x: hw_params: slave NULL\n");
+ return -EINVAL;
+ }
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES) {
+ dev_err(dai->dev,
+ "sn624x: hw_params: pm_runtime_resume failed (%d)\n", ret);
+ return ret;
+ }
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (dai->id == SN624X_DAI_JACK)
+ port = SN624X_PORT_JACK_PLAYBACK;
+ else if (dai->id == SN624X_DAI_SPEAKER)
+ port = SN624X_PORT_SPEAKER_PLAYBACK;
+ else
+ return -EINVAL;
+ } else {
+ if (dai->id == SN624X_DAI_JACK)
+ port = SN624X_PORT_JACK_CAPTURE;
+ else if (dai->id == SN624X_DAI_DMIC)
+ port = SN624X_PORT_DMIC_CAPTURE;
+ else
+ return -EINVAL;
+ }
+ port_config.num = port;
+
+ ret = sdw_stream_add_slave(sn624x->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "%s: sdw_stream_add_slave port %d failed: %d\n",
+ __func__, port, ret);
+ return ret;
+ }
+
+ /* SDCA SampleFreqIndex (same encoding as other SDCA codecs) */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = SN624X_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = SN624X_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = SN624X_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = SN624X_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(dai->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ if (dai->id == SN624X_DAI_JACK) {
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ ret = regmap_write(sn624x->regmap,
+ SN624X_REG_JACK_OUT_RATE_SEL,
+ sampling_rate);
+ else
+ ret = regmap_write(sn624x->regmap,
+ SN624X_REG_JACK_CAP_RATE_SEL,
+ sampling_rate);
+ } else if (dai->id == SN624X_DAI_SPEAKER) {
+ ret = regmap_write(sn624x->regmap, SN624X_REG_RATE_SEL,
+ sampling_rate);
+ } else if (dai->id == SN624X_DAI_DMIC) {
+ ret = regmap_write(sn624x->regmap, SN624X_REG_DMIC_CAP_RATE_SEL,
+ sampling_rate);
+ } else {
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int sn624x_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sn624x_sdca_priv *sn624x = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!sn624x->slave)
+ return -EINVAL;
+
+ return sdw_stream_remove_slave(sn624x->slave, sdw_stream);
+}
+
+static int sn624x_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+ return 0;
+}
+
+static void sn624x_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static const struct snd_soc_dapm_widget sn624x_sdca_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("RX", "Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("TX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("SPK RX", "Speaker Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DMIC TX", "DMic Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("PDE HP", SND_SOC_NOPM, 0, 0,
+ sn624x_pde_hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE SPK", SND_SOC_NOPM, 0, 0,
+ sn624x_pde_spk_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE MIC2", SND_SOC_NOPM, 0, 0,
+ sn624x_pde_mic2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY("PDE DMIC", SND_SOC_NOPM, 0, 0,
+ sn624x_pde_dmic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_DAC_E("FU HP", NULL, SND_SOC_NOPM, 0, 0,
+ sn624x_fu_hp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC_E("FU SPK", NULL, SND_SOC_NOPM, 0, 0,
+ sn624x_fu_spk_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU MIC2", NULL, SND_SOC_NOPM, 0, 0,
+ sn624x_fu_mic2_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_ADC_E("FU DMIC", NULL, SND_SOC_NOPM, 0, 0,
+ sn624x_fu_dmic_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC"),
+};
+
+static const struct snd_soc_dapm_route sn624x_sdca_audio_map[] = {
+ { "FU HP", NULL, "RX" },
+ { "FU HP", NULL, "PDE HP" },
+ { "HP", NULL, "FU HP" },
+
+ { "FU SPK", NULL, "SPK RX" },
+ { "FU SPK", NULL, "PDE SPK" },
+ { "SPK", NULL, "FU SPK" },
+
+ { "FU MIC2", NULL, "MIC2" },
+ { "FU MIC2", NULL, "PDE MIC2" },
+ /* UAJ headset capture also needs jack PDE03 (shared with HP) */
+ { "FU MIC2", NULL, "PDE HP" },
+ { "TX", NULL, "FU MIC2" },
+
+ { "FU DMIC", NULL, "DMIC" },
+ { "FU DMIC", NULL, "PDE DMIC" },
+ { "DMIC TX", NULL, "FU DMIC" },
+};
+
+static const struct snd_soc_dai_ops sn624x_sdca_ops = {
+ .hw_params = sn624x_sdca_pcm_hw_params,
+ .hw_free = sn624x_sdca_pcm_hw_free,
+ .set_stream = sn624x_sdca_set_sdw_stream,
+ .shutdown = sn624x_sdca_shutdown,
+};
+
+static struct snd_soc_dai_driver sn624x_sdca_dai[] = {
+ {
+ .name = "sn624x-sdca-aif",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SN624X_STEREO_RATES,
+ .formats = SN624X_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SN624X_STEREO_RATES,
+ .formats = SN624X_FORMATS,
+ },
+ .ops = &sn624x_sdca_ops,
+ },
+ {
+ .name = "sn624x-sdca-aif2",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SN624X_STEREO_RATES,
+ .formats = SN624X_FORMATS,
+ },
+ .ops = &sn624x_sdca_ops,
+ },
+ {
+ .name = "sn624x-sdca-aif3",
+ .id = 2,
+ .capture = {
+ .stream_name = "DMic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SN624X_STEREO_RATES,
+ .formats = SN624X_FORMATS,
+ },
+ .ops = &sn624x_sdca_ops,
+ },
+};
+
+static const struct snd_soc_component_driver soc_sdca_dev_sn624x = {
+ .probe = sn624x_sdca_probe,
+ .dapm_widgets = sn624x_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sn624x_sdca_dapm_widgets),
+ .dapm_routes = sn624x_sdca_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(sn624x_sdca_audio_map),
+ .set_jack = sn624x_sdca_set_jack_detect,
+ .endianness = 1,
+};
+
+int sn624x_sdca_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct sn624x_sdca_priv *sn624x;
+ int ret;
+
+ sn624x = devm_kzalloc(dev, sizeof(*sn624x), GFP_KERNEL);
+ if (!sn624x)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, sn624x);
+ sn624x->slave = slave;
+ sn624x->regmap = regmap;
+
+ ret = sn624x_parse_sdca_functions(sn624x);
+ if (ret < 0)
+ return ret;
+
+ mutex_init(&sn624x->disable_irq_lock);
+ INIT_DELAYED_WORK(&sn624x->jack_detect_work, sn624x_sdca_jack_detect_handler);
+
+ regcache_cache_only(sn624x->regmap, true);
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_sn624x, sn624x_sdca_dai,
+ ARRAY_SIZE(sn624x_sdca_dai));
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Do not enable runtime PM here. SoundWire slaves are only powered /
+ * accessible after ATTACHED; enable RPM from io_init() then.
+ */
+ SN624X_DBG(dev, "snd_soc_register_component OK (DAI sn624x-sdca-aif)\n");
+ return 0;
+}
+
+static int sn624x_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+ int ret;
+
+ SN624X_DBG(&slave->dev, "sdw_probe: dev_num=%u status=%s\n",
+ slave->dev_num, sn624x_status_str(slave->status));
+
+ regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave,
+ &sn624x_sdca_regmap,
+ &sn624x_sdca_mbq_cfg);
+ if (IS_ERR(regmap)) {
+ dev_err(&slave->dev, "sn624x: devm_regmap_init_sdw_mbq_cfg failed (%ld)\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ ret = sn624x_sdca_init(&slave->dev, regmap, slave);
+ if (ret < 0)
+ dev_err(&slave->dev, "sn624x: sn624x_sdca_init failed (%d)\n", ret);
+ else {
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(&slave->dev);
+
+ if (sn624x)
+ sn624x_sdca_sdca_irq_mask_all(sn624x);
+ SN624X_DBG(&slave->dev, "SoundWire probe finished OK\n");
+ }
+
+ return ret;
+}
+
+static void sn624x_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(&slave->dev);
+
+ SN624X_DBG(&slave->dev, "SoundWire driver remove\n");
+ cancel_delayed_work_sync(&sn624x->jack_detect_work);
+ sn624x_jack_rpm_put(sn624x);
+ if (sn624x->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+ mutex_destroy(&sn624x->disable_irq_lock);
+}
+
+static const struct sdw_device_id sn624x_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x0496, 0x6242, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0496, 0x6244, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x0496, 0x6247, 0x3, 0x1, 0),
+ { }
+};
+MODULE_DEVICE_TABLE(sdw, sn624x_sdca_id);
+
+static int sn624x_sdca_dev_suspend(struct device *dev)
+{
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(dev);
+
+ if (!sn624x->first_hw_init)
+ return 0;
+
+ regcache_cache_only(sn624x->regmap, true);
+ /*
+ * Suspend / clock-stop may reset the peripheral. Mark the cache dirty
+ * so resume's regcache_sync restores software state to hardware.
+ */
+ regcache_mark_dirty(sn624x->regmap);
+
+ return 0;
+}
+
+static int sn624x_sdca_dev_system_suspend(struct device *dev)
+{
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(dev);
+
+ if (!sn624x->first_hw_init)
+ return 0;
+
+ /* Mask first so IRQ cannot re-schedule work between cancel and disable. */
+ mutex_lock(&sn624x->disable_irq_lock);
+ sn624x->disable_irq = true;
+ sn624x_sdca_jack_irq_mask(sn624x);
+ mutex_unlock(&sn624x->disable_irq_lock);
+
+ cancel_delayed_work_sync(&sn624x->jack_detect_work);
+
+ return sn624x_sdca_dev_suspend(dev);
+}
+
+static int sn624x_sdca_regmap_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(dev);
+ int ret;
+
+ if (!sn624x->first_hw_init)
+ return 0;
+
+ SN624X_DBG(dev, "resume: unattach_request=%u\n", slave->unattach_request);
+ if (slave->unattach_request) {
+ ret = sdw_slave_wait_for_init(slave, SN624X_PROBE_TIMEOUT_MS);
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
+ return ret;
+ }
+ }
+
+ regcache_cache_only(sn624x->regmap, false);
+ regcache_sync(sn624x->regmap);
+
+ return 0;
+}
+
+static int sn624x_sdca_dev_resume(struct device *dev)
+{
+ return sn624x_sdca_regmap_resume(dev);
+}
+
+static int sn624x_sdca_dev_system_resume(struct device *dev)
+{
+ struct sn624x_sdca_priv *sn624x = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sn624x_sdca_regmap_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&sn624x->disable_irq_lock);
+ if (sn624x->disable_irq) {
+ sn624x_sdca_jack_irq_unmask(sn624x);
+ sn624x->disable_irq = false;
+ }
+ mutex_unlock(&sn624x->disable_irq_lock);
+
+ if (sn624x->hs_jack && sn624x->hw_init) {
+ sn624x_sdca_jack_poll_and_report(sn624x);
+ sn624x_jack_schedule_poll(sn624x);
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops sn624x_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(sn624x_sdca_dev_system_suspend, sn624x_sdca_dev_system_resume)
+ RUNTIME_PM_OPS(sn624x_sdca_dev_suspend, sn624x_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver sn624x_sdca_sdw_driver = {
+ .driver = {
+ .name = "sn624x-sdca",
+ .pm = pm_ptr(&sn624x_sdca_pm),
+ },
+ .probe = sn624x_sdca_sdw_probe,
+ .remove = sn624x_sdca_sdw_remove,
+ .ops = &sn624x_sdca_slave_ops,
+ .id_table = sn624x_sdca_id,
+};
+module_sdw_driver(sn624x_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC SN624X SDCA SoundWire driver with UAJ jack support");
+MODULE_AUTHOR("Senary Semiconductor Corp.");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
+MODULE_SOFTDEP("pre: snd-soc-sdca");
diff --git a/sound/soc/codecs/sn624x-sdca.h b/sound/soc/codecs/sn624x-sdca.h
new file mode 100644
index 000000000000..6eb588ba2b3b
--- /dev/null
+++ b/sound/soc/codecs/sn624x-sdca.h
@@ -0,0 +1,152 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * sn624x-sdca.h -- SN624X SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2025 Senary Semiconductor Corp.
+ */
+
+#ifndef __SN624X_H__
+#define __SN624X_H__
+
+#include <linux/mutex.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/workqueue.h>
+#include <sound/sdca_function.h>
+#include <sound/soc.h>
+
+struct sn624x_sdca_priv {
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct sdw_slave *slave;
+ /* Parsed DisCo functions (init tables / defaults from ACPI) */
+ struct sdca_function_data *jack_func;
+ struct sdca_function_data *mic_func;
+ struct sdca_function_data *amp_func;
+ bool hw_init;
+ bool first_hw_init;
+ struct snd_soc_jack *hs_jack;
+ struct delayed_work jack_detect_work;
+ struct mutex disable_irq_lock;
+ bool disable_irq;
+ /* Long-lived runtime PM ref while jack detection is enabled */
+ bool jack_rpm;
+ int jack_type;
+ int jack_type_last;
+ unsigned int scp_sdca_stat1;
+ unsigned int scp_sdca_stat2;
+};
+
+/* SoundWire data port numbers (slave prop source/sink port masks) */
+#define SN624X_PORT_JACK_PLAYBACK 1
+#define SN624X_PORT_SPEAKER_PLAYBACK 2
+#define SN624X_PORT_JACK_CAPTURE 4
+#define SN624X_PORT_DMIC_CAPTURE 5
+
+/* Order matches sn624x_sdca_dai[] .id */
+enum sn624x_dai_id {
+ SN624X_DAI_JACK = 0,
+ SN624X_DAI_SPEAKER = 1,
+ SN624X_DAI_DMIC = 2,
+};
+
+/*
+ * SDCA Function Numbers (from platform ACPI / device topology).
+ * Address with SDW_SDCA_CTL(fun, ent, ctl, ch) from sdw_registers.h.
+ */
+#define SN624X_FUNC_NUM_JACK_CODEC 0x01
+#define SN624X_FUNC_NUM_MIC_ARRAY 0x02
+#define SN624X_FUNC_NUM_SPEAKER_AMP 0x04
+
+/* Entity instance IDs (from platform ACPI / SN624X topology) */
+#define SN624X_SDCA_ENT_ENTITY0 0x00
+#define SN624X_SDCA_ENT_CS01 0x01
+#define SN624X_SDCA_ENT_CS02 0x02
+#define SN624X_SDCA_ENT_PDE03 0x03
+#define SN624X_SDCA_ENT_PDE04 0x04
+#define SN624X_SDCA_ENT_CRU05 0x05
+#define SN624X_SDCA_ENT_USER_FU10 0x10
+#define SN624X_SDCA_ENT_USER_FU14 0x14
+#define SN624X_SDCA_ENT_USER_FU1E 0x1e
+#define SN624X_UAJ_ENT_BLOCK 0x20
+#define SN624X_SDCA_ENT_GE35 0x1F
+/* UAJ power domains from ssdt26.dsl AF01 entity list (PDE 34 / PDE 47) */
+#define SN624X_UAJ_ENT_PDE47 SN624X_SDCA_ENT_PDE03
+#define SN624X_UAJ_ENT_PDE34 SN624X_SDCA_ENT_PDE04
+
+/* Control selectors (SDCA; same values as sound/sdca_function.h) */
+/* PDE power state selectors — use standard SDCA defines from sdca_function.h */
+#define SN624X_SDCA_CTL_FU_MUTE 0x01
+#define SN624X_SDCA_CTL_FU_VOLUME 0x02
+#define SN624X_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define SN624X_SDCA_CTL_CLUSTER_INDEX 0x10
+#define SN624X_SDCA_CTL_FUNCTION_STATUS 0x10 /* Entity 0 */
+#define SN624X_SDCA_CTL_SELECTED_MODE 0x01
+#define SN624X_SDCA_CTL_DETECTED_MODE 0x02
+
+/*
+ * Control Number / channel for multi-channel FU controls (mute/volume).
+ * Single-value controls (PDE power, rate, function status, …) pass 0
+ * directly to SDW_SDCA_CTL — no CH_0 alias.
+ */
+#define SN624X_SDCA_CH_L 0x01
+#define SN624X_SDCA_CH_R 0x02
+
+/* GE35 Selected_Mode / Detected_Mode (SDCA Table 153, UAJ §5.5) */
+#define SN624X_GE35_MODE_HEADPHONE 0x03
+#define SN624X_GE35_MODE_HEADSET 0x05
+
+/* Vendor UAJ SCP windows */
+#define SN624X_UAJ_CTL_INPUT_DELAY 0x30
+#define SN624X_UAJ_CTL_CHARGE_PUMP 0x31
+#define SN624X_UAJ_CTL_JACK_DETECT 0x32
+
+/*
+ * Vendor SCP addresses used only when ACPI
+ * mipi-sdca-function-initialization-table is missing (temporary fallback).
+ * Remove once platform DisCo ships a complete init table.
+ */
+#define SN624X_UAJ_CTL_IT33_MIC_BIAS 0x40400418
+#define SN624X_UAJ_CTL_IT31_MIC_BIAS 0x40400318
+#define SN624X_UAJ_CTL_FU41_CH1_MUTE 0x40400A09
+#define SN624X_UAJ_CTL_FU41_CH2_MUTE 0x40400A0A
+#define SN624X_UAJ_CTL_FU31_CH0_MUTE 0x40400C08
+#define SN624X_UAJ_CTL_FU32_CH0_MUTE 0x40400C88
+#define SN624X_UAJ_CTL_FU33_CH0_MUTE 0x40400D08
+#define SN624X_UAJ_CTL_FU36_CH1_MUTE 0x40400F09
+#define SN624X_UAJ_CTL_FU36_CH2_MUTE 0x40400F0A
+#define SN624X_UAJ_CTL_FU31_CH0_GAIN_HIGH 0x40402C58
+#define SN624X_UAJ_CTL_FU31_CH0_GAIN_LOW 0x40400C58
+#define SN624X_UAJ_CTL_FU32_CH0_GAIN_HIGH 0x40402CD8
+#define SN624X_UAJ_CTL_FU32_CH0_GAIN_LOW 0x40400CD8
+#define SN624X_UAJ_CTL_FU33_CH0_GAIN_HIGH 0x40402D58
+#define SN624X_UAJ_CTL_FU33_CH0_GAIN_LOW 0x40400D58
+#define SN624X_UAJ_CTL_INPUT_DELAY_TIME 0x00002124
+#define SN624X_UAJ_CTL_PORTA_CHARGE_PUMP 0x00002086
+#define SN624X_UAJ_DMIC_PPU11 0x40880380
+
+/* Jack poll interval (ms) */
+#define SN624X_JACK_POLL_MS 1000
+
+/* µs settle times for headphone <-> speaker route switch during active PCM */
+#define SN624X_ROUTE_AFTER_MUTE_US_MIN 5000
+#define SN624X_ROUTE_AFTER_MUTE_US_MAX 8000
+#define SN624X_ROUTE_AFTER_D3_US_MIN 10000
+#define SN624X_ROUTE_AFTER_D3_US_MAX 15000
+#define SN624X_ROUTE_BEFORE_ACTIVE_US_MIN 10000
+#define SN624X_ROUTE_BEFORE_ACTIVE_US_MAX 15000
+
+/* SDCA SampleFreqIndex (same encoding as other SDCA codecs, e.g. rt722) */
+#define SN624X_SDCA_RATE_44100HZ 0x08
+#define SN624X_SDCA_RATE_48000HZ 0x09
+#define SN624X_SDCA_RATE_96000HZ 0x0b
+#define SN624X_SDCA_RATE_192000HZ 0x0d
+
+#define SN624X_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define SN624X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+int sn624x_sdca_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave);
+
+#endif /* __SN624X_H__ */
diff --git a/sound/soc/codecs/src4xxx.c b/sound/soc/codecs/src4xxx.c
index 5a3489475225..6e187c930372 100644
--- a/sound/soc/codecs/src4xxx.c
+++ b/sound/soc/codecs/src4xxx.c
@@ -326,10 +326,18 @@ static int src4xxx_hw_params(struct snd_pcm_substream *substream,
return 0;
};
+static const u64 src4xxx_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops src4xxx_dai_ops = {
.hw_params = src4xxx_hw_params,
.set_sysclk = src4xxx_set_mclk_hz,
.set_fmt = src4xxx_set_dai_fmt,
+ .auto_selectable_formats = &src4xxx_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define SRC4XXX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 5192569ba6a8..32e81b31479d 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -620,12 +620,25 @@ static int ssm2518_startup(struct snd_pcm_substream *substream,
#define SSM2518_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32)
+static const u64 ssm2518_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops ssm2518_dai_ops = {
.startup = ssm2518_startup,
.hw_params = ssm2518_hw_params,
.mute_stream = ssm2518_mute,
.set_fmt = ssm2518_set_dai_fmt,
.set_tdm_slot = ssm2518_set_tdm_slot,
+ .auto_selectable_formats = &ssm2518_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index fccd2eacd7a6..92df44f86915 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -523,12 +523,25 @@ static int ssm2602_set_bias_level(struct snd_soc_component *component,
#define SSM2602_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 ssm2602_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops ssm2602_dai_ops = {
.startup = ssm2602_startup,
.hw_params = ssm2602_hw_params,
.mute_stream = ssm2602_mute,
.set_sysclk = ssm2602_set_dai_sysclk,
.set_fmt = ssm2602_set_dai_fmt,
+ .auto_selectable_formats = &ssm2602_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c
index 8c6665677a17..f71509237fca 100644
--- a/sound/soc/codecs/ssm3515.c
+++ b/sound/soc/codecs/ssm3515.c
@@ -356,12 +356,22 @@ static int ssm3515_hw_free(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 ssm3515_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops ssm3515_dai_ops = {
.mute_stream = ssm3515_mute,
.hw_params = ssm3515_hw_params,
.set_fmt = ssm3515_set_fmt,
.set_tdm_slot = ssm3515_set_tdm_slot,
.hw_free = ssm3515_hw_free,
+ .auto_selectable_formats = &ssm3515_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ssm3515_dai_driver = {
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 8415dd163edd..181047b38d3d 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -389,11 +389,24 @@ static int ssm4567_set_bias_level(struct snd_soc_component *component,
return ret;
}
+static const u64 ssm4567_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_PDM |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops ssm4567_dai_ops = {
.hw_params = ssm4567_hw_params,
.mute_stream = ssm4567_mute,
.set_fmt = ssm4567_set_dai_fmt,
.set_tdm_slot = ssm4567_set_tdm_slot,
+ .auto_selectable_formats = &ssm4567_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index d6de739212f9..2865a07be395 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -342,27 +342,48 @@ static int sta32x_sync_coef_shadow(struct snd_soc_component *component)
{
struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
unsigned int cfud;
- int i;
+ int i, ret;
/* preserve reserved bits in STA32X_CFUD */
- regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+ ret = regmap_read(sta32x->regmap, STA32X_CFUD, &cfud);
+ if (ret)
+ return ret;
+
cfud &= 0xf0;
for (i = 0; i < STA32X_COEF_COUNT; i++) {
- regmap_write(sta32x->regmap, STA32X_CFADDR2, i);
- regmap_write(sta32x->regmap, STA32X_B1CF1,
- (sta32x->coef_shadow[i] >> 16) & 0xff);
- regmap_write(sta32x->regmap, STA32X_B1CF2,
- (sta32x->coef_shadow[i] >> 8) & 0xff);
- regmap_write(sta32x->regmap, STA32X_B1CF3,
- (sta32x->coef_shadow[i]) & 0xff);
+ ret = regmap_write(sta32x->regmap, STA32X_CFADDR2, i);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(sta32x->regmap, STA32X_B1CF1,
+ (sta32x->coef_shadow[i] >> 16) & 0xff);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(sta32x->regmap, STA32X_B1CF2,
+ (sta32x->coef_shadow[i] >> 8) & 0xff);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(sta32x->regmap, STA32X_B1CF3,
+ sta32x->coef_shadow[i] & 0xff);
+ if (ret)
+ return ret;
+
/*
* chip documentation does not say if the bits are
* self-clearing, so do it explicitly
*/
- regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
- regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
+ ret = regmap_write(sta32x->regmap, STA32X_CFUD, cfud);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(sta32x->regmap, STA32X_CFUD, cfud | 0x01);
+ if (ret)
+ return ret;
}
+
return 0;
}
@@ -370,15 +391,24 @@ static int sta32x_cache_sync(struct snd_soc_component *component)
{
struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
unsigned int mute;
- int rc;
+ int restore_ret, ret;
/* mute during register sync */
- regmap_read(sta32x->regmap, STA32X_MMUTE, &mute);
- regmap_write(sta32x->regmap, STA32X_MMUTE, mute | STA32X_MMUTE_MMUTE);
- sta32x_sync_coef_shadow(component);
- rc = regcache_sync(sta32x->regmap);
- regmap_write(sta32x->regmap, STA32X_MMUTE, mute);
- return rc;
+ ret = regmap_read(sta32x->regmap, STA32X_MMUTE, &mute);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(sta32x->regmap, STA32X_MMUTE,
+ mute | STA32X_MMUTE_MMUTE);
+ if (ret)
+ return ret;
+
+ ret = sta32x_sync_coef_shadow(component);
+ if (!ret)
+ ret = regcache_sync(sta32x->regmap);
+
+ restore_ret = regmap_write(sta32x->regmap, STA32X_MMUTE, mute);
+ return ret ?: restore_ret;
}
/* work around ESD issue where sta32x resets and loses all configuration */
@@ -794,9 +824,9 @@ static int sta32x_startup_sequence(struct sta32x_priv *sta32x)
static int sta32x_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
- int ret;
struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int cleanup_ret, ret;
dev_dbg(component->dev, "level = %d\n", level);
switch (level) {
@@ -820,15 +850,28 @@ static int sta32x_set_bias_level(struct snd_soc_component *component,
return ret;
}
- sta32x_startup_sequence(sta32x);
- sta32x_cache_sync(component);
+ ret = sta32x_startup_sequence(sta32x);
+ if (!ret)
+ ret = sta32x_cache_sync(component);
+ if (ret)
+ goto restore_failed;
+
+ ret = regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+ 0);
+ if (ret)
+ goto restore_failed;
+
sta32x_watchdog_start(sta32x);
+ break;
}
/* Power down */
- regmap_update_bits(sta32x->regmap, STA32X_CONFF,
- STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
- 0);
+ ret = regmap_update_bits(sta32x->regmap, STA32X_CONFF,
+ STA32X_CONFF_PWDN | STA32X_CONFF_EAPD,
+ 0);
+ if (ret)
+ return ret;
break;
@@ -846,12 +889,31 @@ static int sta32x_set_bias_level(struct snd_soc_component *component,
break;
}
return 0;
+
+restore_failed:
+ gpiod_set_value(sta32x->gpiod_nreset, 0);
+ regcache_mark_dirty(sta32x->regmap);
+ cleanup_ret = regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies),
+ sta32x->supplies);
+ if (cleanup_ret)
+ dev_warn(component->dev, "Failed to disable supplies: %d\n",
+ cleanup_ret);
+ return ret;
}
+static const u64 sta32x_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF;
+
static const struct snd_soc_dai_ops sta32x_dai_ops = {
.hw_params = sta32x_hw_params,
.set_sysclk = sta32x_set_dai_sysclk,
.set_fmt = sta32x_set_dai_fmt,
+ .auto_selectable_formats = &sta32x_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver sta32x_dai = {
@@ -1035,7 +1097,7 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
{
struct device_node *np = dev->of_node;
struct sta32x_platform_data *pdata;
- u16 tmp;
+ u32 tmp;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
@@ -1060,8 +1122,8 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
of_property_read_bool(np, "st,needs_esd_watchdog");
tmp = 140;
- of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
- pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
+ of_property_read_u32(np, "st,drop-compensation-ns", &tmp);
+ pdata->drop_compensation_ns = clamp_t(u32, tmp, 0, 300) / 20;
/* CONFE */
pdata->max_power_use_mpcc =
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 2ba35076732b..d2b6f73d5e10 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -882,10 +882,19 @@ static int sta350_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static const u64 sta350_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF;
+
static const struct snd_soc_dai_ops sta350_dai_ops = {
.hw_params = sta350_hw_params,
.set_sysclk = sta350_set_dai_sysclk,
.set_fmt = sta350_set_dai_fmt,
+ .auto_selectable_formats = &sta350_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver sta350_dai = {
@@ -1087,7 +1096,7 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
struct device_node *np = dev->of_node;
struct sta350_platform_data *pdata;
const char *ffx_power_mode;
- u16 tmp;
+ u32 tmp;
u8 tmp8;
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
@@ -1127,8 +1136,8 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
}
tmp = 140;
- of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
- pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
+ of_property_read_u32(np, "st,drop-compensation-ns", &tmp);
+ pdata->drop_compensation_ns = clamp_t(u32, tmp, 0, 300) / 20;
pdata->oc_warning_adjustment =
of_property_read_bool(np, "st,overcurrent-warning-adjustment");
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 7d57999f9cb1..0faf439ffee9 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -286,10 +286,17 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
return 0;
}
+static const u64 sta529_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops sta529_dai_ops = {
.hw_params = sta529_hw_params,
.set_fmt = sta529_set_dai_fmt,
.mute_stream = sta529_mute,
+ .auto_selectable_formats = &sta529_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index be2c9a0fa529..beb5654ac4b4 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -518,6 +518,18 @@ static const struct dev_pm_ops tas2552_pm = {
RUNTIME_PM_OPS(tas2552_runtime_suspend, tas2552_runtime_resume, NULL)
};
+static const u64 tas2552_selectable_formats[] = {
+ /* Hi Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF,
+ /* Low Priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
.hw_params = tas2552_hw_params,
.prepare = tas2552_prepare,
@@ -525,6 +537,8 @@ static const struct snd_soc_dai_ops tas2552_speaker_dai_ops = {
.set_fmt = tas2552_set_dai_fmt,
.set_tdm_slot = tas2552_set_dai_tdm_slot,
.mute_stream = tas2552_mute,
+ .auto_selectable_formats = tas2552_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(tas2552_selectable_formats),
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index 0b8644ad74f1..1fe17b0895fc 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -604,11 +604,21 @@ static const struct snd_soc_component_driver soc_component_dev_tas2562 = {
.endianness = 1,
};
+static const u64 tas2562_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops tas2562_speaker_dai_ops = {
.hw_params = tas2562_hw_params,
.set_fmt = tas2562_set_dai_fmt,
.set_tdm_slot = tas2562_set_dai_tdm_slot,
.mute_stream = tas2562_mute,
+ .auto_selectable_formats = &tas2562_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index f9c65c9f6d0a..6654100ac91d 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -602,6 +602,16 @@ static int tas2764_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
return tas2764_write_sdout_idle_mask(tas2764, tas2764->idle_slot_config.tx_mask);
}
+static const u64 tas2764_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops tas2764_dai_ops = {
.mute_stream = tas2764_mute,
.hw_params = tas2764_hw_params,
@@ -609,6 +619,8 @@ static const struct snd_soc_dai_ops tas2764_dai_ops = {
.set_bclk_ratio = tas2764_set_bclk_ratio,
.set_tdm_slot = tas2764_set_dai_tdm_slot,
.set_tdm_idle = tas2764_set_dai_tdm_idle,
+ .auto_selectable_formats = &tas2764_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -960,7 +972,7 @@ static int tas2764_parse_dt(struct device *dev, struct tas2764_priv *tas2764)
}
}
- tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
+ tas2764->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_LOW);
if (IS_ERR(tas2764->sdz_gpio)) {
if (PTR_ERR(tas2764->sdz_gpio) == -EPROBE_DEFER)
return -EPROBE_DEFER;
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index e79754edac5c..882e19857697 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -566,12 +566,24 @@ static int tas2770_set_dai_tdm_idle(struct snd_soc_dai *dai,
return 0;
}
+static const u64 tas2770_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops tas2770_dai_ops = {
.mute_stream = tas2770_mute,
.hw_params = tas2770_hw_params,
.set_fmt = tas2770_set_fmt,
.set_tdm_slot = tas2770_set_dai_tdm_slot,
.set_tdm_idle = tas2770_set_dai_tdm_idle,
+ .auto_selectable_formats = &tas2770_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -886,7 +898,7 @@ static int tas2770_parse_dt(struct device *dev, struct tas2770_priv *tas2770)
if (rc)
tas2770->pdm_slot = -1;
- tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_HIGH);
+ tas2770->sdz_gpio = devm_gpiod_get_optional(dev, "shutdown", GPIOD_OUT_LOW);
if (IS_ERR(tas2770->sdz_gpio)) {
if (PTR_ERR(tas2770->sdz_gpio) == -EPROBE_DEFER)
return -EPROBE_DEFER;
diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c
index f1fce4305fa1..991d0c0efee2 100644
--- a/sound/soc/codecs/tas2780.c
+++ b/sound/soc/codecs/tas2780.c
@@ -441,11 +441,21 @@ err:
return ret;
}
+static const u64 tas2780_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops tas2780_dai_ops = {
.mute_stream = tas2780_mute,
.hw_params = tas2780_hw_params,
.set_fmt = tas2780_set_fmt,
.set_tdm_slot = tas2780_set_dai_tdm_slot,
+ .auto_selectable_formats = &tas2780_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tas2781-comlib-i2c.c b/sound/soc/codecs/tas2781-comlib-i2c.c
index 79b5f5b04c74..dd86fc90a7d0 100644
--- a/sound/soc/codecs/tas2781-comlib-i2c.c
+++ b/sound/soc/codecs/tas2781-comlib-i2c.c
@@ -2,7 +2,7 @@
//
// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers based on I2C
//
-// Copyright 2025 Texas Instruments, Inc.
+// Copyright 2025 - 2026 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
@@ -131,7 +131,10 @@ int tasdevice_dev_update_bits(
ret = regmap_update_bits(map, TASDEVICE_PGRG(reg),
mask, value);
if (ret < 0)
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ dev_err(tas_priv->dev, "%s, Bx%02xPx%02xRx%02x E=%d\n",
+ __func__, TASDEVICE_BOOK_ID(reg),
+ TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), ret);
} else {
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
chn);
diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c
index 4cec9f8a00af..e4c8a04a9187 100644
--- a/sound/soc/codecs/tas2781-comlib.c
+++ b/sound/soc/codecs/tas2781-comlib.c
@@ -2,7 +2,7 @@
//
// TAS2563/TAS2781 Common functions for HDA and ASoC Audio drivers
//
-// Copyright 2023 - 2025 Texas Instruments, Inc.
+// Copyright 2023 - 2026 Texas Instruments, Inc.
//
// Author: Shenghao Ding <shenghao-ding@ti.com>
@@ -34,7 +34,10 @@ int tasdevice_dev_read(struct tasdevice_priv *tas_priv,
ret = regmap_read(map, TASDEVICE_PGRG(reg), val);
if (ret < 0)
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ dev_err(tas_priv->dev, "%s, Bx%02xPx%02xRx%02x E=%d\n",
+ __func__, TASDEVICE_BOOK_ID(reg),
+ TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), ret);
} else {
ret = -EINVAL;
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
@@ -62,7 +65,10 @@ int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv,
ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len);
if (ret < 0)
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ dev_err(tas_priv->dev, "%s, Bx%02xPx%02xRx%02x E=%d\n",
+ __func__, TASDEVICE_BOOK_ID(reg),
+ TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), ret);
} else
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
chn);
@@ -88,7 +94,10 @@ int tasdevice_dev_write(struct tasdevice_priv *tas_priv,
ret = regmap_write(map, TASDEVICE_PGRG(reg),
value);
if (ret < 0)
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ dev_err(tas_priv->dev, "%s, Bx%02xPx%02xRx%02x E=%d\n",
+ __func__, TASDEVICE_BOOK_ID(reg),
+ TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), ret);
} else {
ret = -EINVAL;
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
@@ -118,7 +127,10 @@ int tasdevice_dev_bulk_write(
ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg),
data, len);
if (ret < 0)
- dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret);
+ dev_err(tas_priv->dev, "%s, Bx%02xPx%02xRx%02x E=%d\n",
+ __func__, TASDEVICE_BOOK_ID(reg),
+ TASDEVICE_PAGE_ID(reg),
+ TASDEVICE_PAGE_REG(reg), ret);
} else {
ret = -EINVAL;
dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__,
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 2b9efccf1a0b..e8ceeb027e1b 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -110,7 +110,7 @@ struct tas2781_cali_specific {
};
static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = {
- 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4, 1, 2
+ 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4, 1, 2, 1, 2, 3, 4,
};
/* fixed m68k compiling issue: mapping table can save code field */
@@ -199,15 +199,22 @@ static struct tasdevice_config_info *tasdevice_add_config(
dev_err(tas_priv->dev, "add conf: Out of boundary\n");
goto out;
}
- /* If in the RCA bin file are several profiles with the
- * keyword "init", init_profile_id only store the last
- * init profile id.
+ /*
+ * If in the RCA bin file are several profiles with the
+ * keyword "init"/"calib", init_profile_id only store the last
+ * init profile id, and same as calibration_profile_id.
*/
if (strnstr(&config_data[config_offset], "init", 64)) {
tas_priv->rcabin.init_profile_id =
tas_priv->rcabin.ncfgs - 1;
dev_dbg(tas_priv->dev, "%s: init profile id = %d\n",
__func__, tas_priv->rcabin.init_profile_id);
+ } else if (strnstr(&config_data[config_offset], "calib", 64)) {
+ tas_priv->rcabin.calibration_profile_id =
+ tas_priv->rcabin.ncfgs - 1;
+ dev_dbg(tas_priv->dev, "%s: calib profile id = %d\n",
+ __func__,
+ tas_priv->rcabin.calibration_profile_id);
}
config_offset += 64;
}
@@ -314,6 +321,7 @@ int tasdevice_rca_parser(void *context, const struct firmware *fmw)
rca = &(tas_priv->rcabin);
/* Initialize to none */
rca->init_profile_id = -1;
+ rca->calibration_profile_id = -1;
fw_hdr = &(rca->fw_hdr);
if (!fmw || !fmw->data) {
dev_err(tas_priv->dev, "Failed to read %s\n",
@@ -632,6 +640,17 @@ static int fw_parse_configuration_data_kernel(
goto out;
}
memcpy(config->name, &data[offset], 64);
+
+ /*
+ * If in the coef bin file are several configs with the
+ * keyword "calib", calibration_config_id only store the last
+ * calibration profile id.
+ */
+ if (strnstr(config->name, "calib", 64)) {
+ tas_fmw->calibration_config_id = i;
+ dev_dbg(tas_priv->dev, "%s: calib cofig = %d\n",
+ __func__, tas_fmw->calibration_config_id);
+ }
/*skip extra 16 bytes*/
offset += 80;
@@ -1412,6 +1431,17 @@ static int fw_parse_configuration_data(
memcpy(config->name, &data[offset], 64);
offset += 64;
+ /*
+ * If in the coef bin file are several configs with the
+ * keyword "calib", calibration_config_id only store the last
+ * calibration profile id.
+ */
+ if (strnstr(config->name, "calib", 64)) {
+ tas_fmw->calibration_config_id = i;
+ dev_dbg(tas_priv->dev, "%s: calib cofig id = %d\n",
+ __func__, tas_fmw->calibration_config_id);
+ }
+
n = tasdevice_fw_strnlen(fmw, offset);
if (n < 0) {
dev_err(tas_priv->dev, "Description err\n");
@@ -1959,6 +1989,8 @@ static void dspbin_type_check(struct tasdevice_priv *tas_priv,
else
tas_priv->dspbin_typ = TASDEV_ALPHA;
}
+ if (tas_priv->chip_id == TAS2573)
+ return;
if ((tas_priv->dspbin_typ != TASDEV_BASIC) &&
(ppcver < PPC3_VERSION_TAS5825_BASE))
tas_priv->fw_parse_fct_param_address =
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index 6f349f88f194..995bfd52f5fc 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -613,6 +613,39 @@ static void tas2563_calib_stop_put(struct tasdevice_priv *tas_priv)
}
}
+static void tas2573_calib_stop_put(struct tasdevice_priv *tas_priv)
+{
+ int cal_prof_id = tas_priv->rcabin.calibration_profile_id;
+
+ if (cal_prof_id >= 0)
+ tasdevice_select_cfg_blk(tas_priv, cal_prof_id,
+ TASDEVICE_BIN_BLK_PRE_SHUTDOWN);
+}
+
+static int tas2573_calib_start_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(comp);
+ int cal_prof_id = tas_priv->rcabin.calibration_profile_id;
+ int cal_conf_id = tas_priv->fmw->calibration_config_id;
+
+ guard(mutex)(&tas_priv->codec_lock);
+ if (tas_priv->chip_id != TAS2573) {
+ WARN_ON_ONCE(1);
+ return -ENODEV;
+ }
+
+ if (cal_prof_id >= 0)
+ tasdevice_select_cfg_blk(tas_priv, cal_prof_id,
+ TASDEVICE_BIN_BLK_PRE_POWER_UP);
+
+ tasdevice_select_tuningprm_cfg(tas_priv, tas_priv->cur_prog,
+ cal_conf_id, cal_prof_id);
+
+ return 1;
+}
+
static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -621,10 +654,22 @@ static int tasdev_calib_stop_put(struct snd_kcontrol *kcontrol,
int i;
guard(mutex)(&priv->codec_lock);
- if (priv->chip_id == TAS2563)
+
+ switch (priv->chip_id) {
+ case TAS2563:
tas2563_calib_stop_put(priv);
- else
+ break;
+ case TAS2573:
+ tas2573_calib_stop_put(priv);
+ break;
+ case TAS2781:
tas2781_calib_stop_put(priv);
+ break;
+ default:
+ dev_err(priv->dev, "%s: Chip(%d) unsupports calibration\n",
+ __func__, priv->chip_id);
+ return -1;
+ }
/*
* Set reloading-firmware flag after calibration, the flag will work
@@ -983,6 +1028,13 @@ static const struct snd_kcontrol_new tas2563_cali_controls[] = {
tasdev_nop_get, tas2563_calib_start_put),
};
+static const struct snd_kcontrol_new tas2573_cali_controls[] = {
+ SOC_SINGLE_EXT("Calibration Start", SND_SOC_NOPM, 0, 1, 0,
+ tasdev_nop_get, tas2573_calib_start_put),
+ SOC_SINGLE_EXT("Calibration Stop", SND_SOC_NOPM, 0, 1, 0,
+ tasdev_nop_get, tasdev_calib_stop_put),
+};
+
static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -1493,6 +1545,182 @@ static int create_tas2781_cali_start_ktrl(struct tasdevice_priv
return 0;
}
+static int tas2573_re_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned char *dst = ucontrol->value.bytes.data;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned int i = 0;
+ unsigned int j;
+ int rc;
+
+ guard(mutex)(&priv->codec_lock);
+ dst[i++] = bytes_ext->max;
+ dst[i++] = 'r';
+
+ dst[i++] = TASDEVICE_BOOK_ID(TAS2573_RE_OUT);
+ dst[i++] = TASDEVICE_PAGE_ID(TAS2573_RE_OUT);
+ dst[i++] = TASDEVICE_PAGE_REG(TAS2573_RE_OUT);
+
+ dst[i++] = 'D';
+
+ for (j = 0; j < priv->ndev; j++) {
+ dst[i++] = j;
+
+ rc = tasdevice_dev_bulk_read(priv, j, TAS2573_RE_OUT,
+ &dst[i], 4);
+ if (rc < 0)
+ dev_err(priv->dev, "chn %d bulk_rd err = %d\n", j, rc);
+
+ i += 4;
+ }
+
+ return 0;
+}
+
+static int tas2573_calib_status_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tasdevice_priv *priv = snd_soc_component_get_drvdata(comp);
+ unsigned char *dst = ucontrol->value.bytes.data;
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *) kcontrol->private_value;
+ unsigned int fct_status_regs[] = {
+ TAS2573_FCT_INT_LATCH,
+ TAS2573_FCT_STATUS_CTRL,
+ TAS2573_FCT_STATUS_BINNING,
+ TAS2573_SILENCE_DETECTED,
+ TAS2573_OPEN_CIRCUIT,
+ TAS2573_SHORTCKT,
+ TAS2573_FCT_OUTPUT_R0,
+ TAS2573_FCT_OUTPUT_R0_LOW,
+ TAS2573_FCT_OUTPUT_INV_R0,
+ TAS2573_FCT_OUTPUT_POWERTOT,
+ TAS2573_FCT_OUTPUT_F0,
+ TAS2573_RE_OUT,
+ };
+ unsigned int j, k, val;
+ unsigned int i = 0;
+ int rc;
+
+ guard(mutex)(&priv->codec_lock);
+ dst[i++] = bytes_ext->max;
+ dst[i++] = 'r';
+
+ for (j = 0; j < ARRAY_SIZE(fct_status_regs); j++) {
+ dst[i++] = TASDEVICE_BOOK_ID(fct_status_regs[j]);
+ dst[i++] = TASDEVICE_PAGE_ID(fct_status_regs[j]);
+ dst[i++] = TASDEVICE_PAGE_REG(fct_status_regs[j]);
+ }
+ dst[i++] = 'D';
+
+ for (j = 0; j < priv->ndev; j++) {
+ dst[i++] = j;
+ dst[i++] = 0;
+ dst[i++] = 0;
+ dst[i++] = 0;
+
+ rc = tasdevice_dev_read(priv, j, fct_status_regs[0], &val);
+ if (rc < 0)
+ dev_err(priv->dev,
+ "chn %d fct_status_regs[0] rd err = %d\n",
+ j, rc);
+ else
+ dst[i++] = val;
+
+ for (k = 1; k < ARRAY_SIZE(fct_status_regs); k++, i += 4) {
+ rc = tasdevice_dev_bulk_read(priv, j,
+ fct_status_regs[k], &dst[i], 4);
+ if (rc < 0) {
+ dev_err(priv->dev,
+ "chn %d regs[%u] bulk_rd err = %d\n",
+ j, k, rc);
+ }
+ }
+
+ }
+
+ return 0;
+}
+
+static int create_tas2573_cali_status_ktrl(struct tasdevice_priv
+ *priv, struct snd_kcontrol_new *cali_ctrl)
+{
+ struct soc_bytes_ext *ext_cali_start;
+ char *cali_start_name;
+
+ ext_cali_start = devm_kzalloc(priv->dev,
+ sizeof(*ext_cali_start), GFP_KERNEL);
+ if (!ext_cali_start)
+ return -ENOMEM;
+
+ cali_start_name = devm_kstrdup(priv->dev,
+ "Calibration Status", GFP_KERNEL);
+ if (!cali_start_name)
+ return -ENOMEM;
+ /*
+ * package structure for tas2573 fct status:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register for fct status (total 36 bytes)
+ * Data Start Flag (1 byte, constant 'D')
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Sine gain for Device #i (48 bytes)
+ * }
+ */
+ ext_cali_start->max = 39 + priv->ndev * 49;
+ cali_ctrl->name = cali_start_name;
+ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrl->info = snd_soc_bytes_info_ext;
+ cali_ctrl->put = NULL;
+ cali_ctrl->get = tas2573_calib_status_get;
+ cali_ctrl->private_value = (unsigned long)ext_cali_start;
+
+ return 0;
+}
+
+static int create_tas2573_cali_re_ktrl(struct tasdevice_priv
+ *priv, struct snd_kcontrol_new *cali_ctrl)
+{
+ struct soc_bytes_ext *ext_cali_start;
+ char *cali_start_name;
+
+ ext_cali_start = devm_kzalloc(priv->dev,
+ sizeof(*ext_cali_start), GFP_KERNEL);
+ if (!ext_cali_start)
+ return -ENOMEM;
+
+ cali_start_name = devm_kstrdup(priv->dev,
+ "Real-time Speaker Impedance", GFP_KERNEL);
+ if (!cali_start_name)
+ return -ENOMEM;
+ /*
+ * package structure for tas2573 real-time spk impedance:
+ * Pkg len (1 byte)
+ * Reg id (1 byte, constant 'r')
+ * book, page, register for fct status (total 3 bytes)
+ * Data Start Flag (1 byte, constant 'D')
+ * for (i = 0; i < Device-Sum; i++) {
+ * Device #i index_info (1 byte)
+ * Sine gain for Device #i (4 bytes)
+ * }
+ */
+ ext_cali_start->max = 6 + priv->ndev * 5;
+ cali_ctrl->name = cali_start_name;
+ cali_ctrl->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+ cali_ctrl->info = snd_soc_bytes_info_ext;
+ cali_ctrl->put = NULL;
+ cali_ctrl->get = tas2573_re_get;
+ cali_ctrl->private_value = (unsigned long)ext_cali_start;
+
+ return 0;
+}
+
static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
{
struct calidata *cali_data = &priv->cali_data;
@@ -1504,17 +1732,43 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
char *cali_name;
int rc, i;
- rc = snd_soc_add_component_controls(priv->codec,
- tasdevice_cali_controls, ARRAY_SIZE(tasdevice_cali_controls));
- if (rc < 0) {
- dev_err(priv->dev, "%s: Add cali controls err rc = %d",
- __func__, rc);
- return rc;
+ switch (priv->chip_id) {
+ case TAS2563: {
+ rc = snd_soc_add_component_controls(priv->codec,
+ tasdevice_cali_controls,
+ ARRAY_SIZE(tasdevice_cali_controls));
+ if (rc < 0) {
+ dev_err(priv->dev, "%s: Add cali controls err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+ cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
+ nctrls = ARRAY_SIZE(tas2563_cali_controls);
+ for (i = 0; i < priv->ndev; i++) {
+ tasdev[i].cali_data_backup =
+ kmemdup(tas2563_cali_start_reg,
+ sizeof(tas2563_cali_start_reg), GFP_KERNEL);
+ if (!tasdev[i].cali_data_backup)
+ return -ENOMEM;
+ }
}
-
- if (priv->chip_id == TAS2781) {
+ break;
+ case TAS2573:
+ cali_ctrls = (struct snd_kcontrol_new *)tas2573_cali_controls;
+ nctrls = ARRAY_SIZE(tas2573_cali_controls);
+ break;
+ case TAS2781: {
struct fct_param_address *t = &(fmw->fct_par_addr);
+ rc = snd_soc_add_component_controls(priv->codec,
+ tasdevice_cali_controls,
+ ARRAY_SIZE(tasdevice_cali_controls));
+ if (rc < 0) {
+ dev_err(priv->dev, "%s: Add cali controls err rc = %d",
+ __func__, rc);
+ return rc;
+ }
+
cali_ctrls = (struct snd_kcontrol_new *)tas2781_cali_controls;
nctrls = ARRAY_SIZE(tas2781_cali_controls);
for (i = 0; i < priv->ndev; i++) {
@@ -1533,16 +1787,12 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
}
}
}
- } else {
- cali_ctrls = (struct snd_kcontrol_new *)tas2563_cali_controls;
- nctrls = ARRAY_SIZE(tas2563_cali_controls);
- for (i = 0; i < priv->ndev; i++) {
- tasdev[i].cali_data_backup =
- kmemdup(tas2563_cali_start_reg,
- sizeof(tas2563_cali_start_reg), GFP_KERNEL);
- if (!tasdev[i].cali_data_backup)
- return -ENOMEM;
- }
+ }
+ break;
+ default:
+ dev_err(priv->dev, "%s: Wrong chip id = %d", __func__,
+ priv->chip_id);
+ return -EINVAL;
}
rc = snd_soc_add_component_controls(priv->codec, cali_ctrls, nctrls);
@@ -1554,10 +1804,17 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
/* index for cali_ctrls */
i = 0;
- if (priv->chip_id == TAS2781)
+ switch (priv->chip_id) {
+ case TAS2573:
+ nctrls = 3;
+ break;
+ case TAS2781:
nctrls = 2;
- else
+ break;
+ default:
nctrls = 1;
+ break;
+ }
/*
* Alloc kcontrol via devm_kzalloc(), which don't manually
@@ -1610,12 +1867,24 @@ static int tasdevice_create_cali_ctrls(struct tasdevice_priv *priv)
* it, for the default value is 0, which means the first device.
*/
cali_data->data[0] = 0xff;
- if (priv->chip_id == TAS2781) {
+
+ switch (priv->chip_id) {
+ case TAS2573:
+ rc = create_tas2573_cali_status_ktrl(priv, &cali_ctrls[i]);
+ if (rc != 0)
+ return rc;
+ i++;
+ rc = create_tas2573_cali_re_ktrl(priv, &cali_ctrls[i]);
+ if (rc != 0)
+ return rc;
+ i++;
+ break;
+ case TAS2781:
rc = create_tas2781_cali_start_ktrl(priv, &cali_ctrls[i]);
if (rc != 0)
return rc;
i++;
-
+ break;
}
return snd_soc_add_component_controls(priv->codec, cali_ctrls,
@@ -1822,7 +2091,8 @@ static void tasdevice_fw_ready(const struct firmware *fmw,
tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK;
/* There is no calibration required for TAS58XX. */
- if (tas_priv->chip_id == TAS2563 || tas_priv->chip_id == TAS2781) {
+ if (tas_priv->chip_id == TAS2563 || tas_priv->chip_id == TAS2573 ||
+ tas_priv->chip_id == TAS2781) {
ret = tasdevice_create_cali_ctrls(tas_priv);
if (ret) {
dev_err(tas_priv->dev, "cali controls error\n");
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index 7fcf8214bf34..a57230e01f75 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -97,6 +97,7 @@ struct tas2783_prv {
u8 rca_binaryname[64];
u8 dev_name[32];
bool hw_init;
+ unsigned int fw_version;
/* wq for firmware download */
wait_queue_head_t fw_wait;
bool fw_dl_task_done;
@@ -185,35 +186,17 @@ static const struct reg_default tas2783_reg_default[] = {
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x01, 1), 0x1},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0), 0x1},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x02, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x02, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x02, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x02, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x1, 0), 0x3},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 1), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 2), 0x0},
@@ -222,40 +205,28 @@ static const struct reg_default tas2783_reg_default[] = {
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 5), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 6), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x01, 7), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x04, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 0), 0x1},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 1), 0x1},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x01, 2), 0x1},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 1), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 2), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x02, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x01, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x04, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x05, 0), 0x1},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x01, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x04, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x05, 0), 0x1},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0), 0x1},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x15, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x16, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS24, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS21, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS26, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS28, 0x10, 0), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PDE23, 0x10, 0), 0x3},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x10, 0), 0x1},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x11, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x12, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x11, 0), 0x0},
@@ -286,7 +257,6 @@ static const struct reg_default tas2783_reg_default[] = {
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 5), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 6), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x12, 7), 0x0},
- {SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_CS127, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x10, 0), 0x0},
{SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x11, 0), 0x0},
@@ -346,8 +316,10 @@ static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
case 0x300 ... 0x340: /* Data port 3. */
case 0x400 ... 0x440: /* Data port 4. */
case 0x500 ... 0x540: /* Data port 5. */
- case 0x800000 ... 0x803fff: /* Page 0 ~ 127. */
- case 0x807e80 ... 0x807eff: /* Page 253. */
+ case TASDEV_REG_SDW(0, 0, 0) ... TASDEV_REG_SDW(0x00, 0x01, 0x80):
+ case TASDEV_REG_SDW(0, 0xfd, 0) ... TASDEV_REG_SDW(0, 0xfd, 0x80):
+ case PRAM_ADDR_START ... PRAM_ADDR_END:
+ case YRAM_ADDR_START ... YRAM_ADDR_END:
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_UDMPU23,
TAS2783_SDCA_CTL_UDMPU_CLUSTER, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU21, TAS2783_SDCA_CTL_FU_MUTE,
@@ -364,7 +336,6 @@ static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x11, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_TG23, 0x10, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x01, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x08, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x0a, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x10, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x14, 0):
@@ -450,7 +421,6 @@ static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x0b, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x0b, 1):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x07, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x09, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x12, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x12, 0):
@@ -462,27 +432,8 @@ static int tas2783_sdca_mbq_size(struct device *dev, u32 reg)
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x11, 0):
return 2;
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU23, 0x10, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT21, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT26, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT28, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_IT29, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT23, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT24, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT25, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT28, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_OT127, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MU26, 0x06, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU127, 0x10, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_FU26, 0x10, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x06, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x12, 0):
case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_XU22, 0x13, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU21, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_MFPU26, 0x08, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_SAPU29, 0x05, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU21, 0x06, 0):
- case SDW_SDCA_CTL(1, TAS2783_SDCA_ENT_PPU26, 0x06, 0):
return 4;
default:
@@ -495,59 +446,52 @@ static bool tas2783_readable_register(struct device *dev, unsigned int reg)
return tas2783_sdca_mbq_size(dev, reg) > 0;
}
-static bool tas2783_writeable_register(struct device *dev, unsigned int reg)
+/*
+ * The read-only SDCA Controls the driver describes: the Clock Valid of every
+ * Clock Source, the actual power state of the Power Domain Entity, the
+ * protection status and the algorithm ready flags. None of them is a
+ * setting; every one is a reading of device state.
+ */
+static bool tas2783_read_only_control(unsigned int reg)
{
- /*
- * The Latency Control of every Entity, together with the Power Domain
- * actual state and the protection status, is read-only. They are
- * listed in tas2783_reg_default[] with a placeholder value, so without
- * this a regcache_sync() would try to write them back and the
- * peripheral would reject the transaction, aborting the sync.
- */
switch (reg) {
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU21, 0x10, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU23, 0x10, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU26, 0x10, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x06, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x07, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x08, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS24, 0x02, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS21, 0x02, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS25, 0x02, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS26, 0x02, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS28, 0x02, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PDE23, 0x10, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_UDMPU23, 0x06, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x05, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_SAPU29, 0x11, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU21, 0x06, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_PPU26, 0x06, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT21, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT29, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT26, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_IT28, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT24, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT23, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT25, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT28, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MU26, 0x06, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_OT127, 0x08, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_FU127, 0x10, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_CS127, 0x02, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x08, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU21, 0x04, 0):
- case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x08, 0):
case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_MFPU26, 0x04, 0):
- return false;
+ return true;
default:
- return tas2783_sdca_mbq_size(dev, reg) > 0;
+ return false;
}
}
+static bool tas2783_writeable_register(struct device *dev, unsigned int reg)
+{
+ if (tas2783_read_only_control(reg))
+ return false;
+
+ return tas2783_sdca_mbq_size(dev, reg) > 0;
+}
+
static bool tas2783_volatile_register(struct device *dev, u32 reg)
{
+ if (tas2783_read_only_control(reg))
+ return true;
+
switch (reg) {
+ /*
+ * The File Download Status is a handshake: the device raises its own
+ * bits on its own schedule and the host writes back a response, so it
+ * is writable, but it must never be answered from the cache.
+ */
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, TAS2783_SDCA_ENT_XU22, 0x14, 0):
case 0x000 ... 0x080: /* Data port 0. */
case 0x100 ... 0x140: /* Data port 1. */
case 0x200 ... 0x240: /* Data port 2. */
@@ -564,13 +508,19 @@ static bool tas2783_volatile_register(struct device *dev, u32 reg)
static const struct regmap_config tas_regmap = {
.reg_bits = 32,
- .val_bits = 8,
+ /*
+ * tas2783_sdca_mbq_size() declares registers of one, two and four
+ * bytes. val_bits has to cover the widest of those: the SoundWire MBQ
+ * bus rejects any register whose declared size exceeds val_bits, which
+ * would make every multi-byte register unreachable in both directions.
+ */
+ .val_bits = 32,
.readable_reg = tas2783_readable_register,
.writeable_reg = tas2783_writeable_register,
.volatile_reg = tas2783_volatile_register,
.reg_defaults = tas2783_reg_default,
.num_reg_defaults = ARRAY_SIZE(tas2783_reg_default),
- .max_register = 0x41008000 + TASDEV_REG_SDW(0xa1, 0x60, 0x7f),
+ .max_register = 0x41000000 + PRAM_ADDR_END,
.cache_type = REGCACHE_MAPLE,
.use_single_read = true,
.use_single_write = true,
@@ -667,9 +617,9 @@ static s32 tas2783_validate_calibdata(struct tas2783_prv *tas_dev,
static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32 *cali_data)
{
- u32 dev_count, offset, i, device_num;
+ u32 dev_count, offset, i, j, device_num;
u32 reg_value;
- u8 buf[4];
+ s32 ret;
dev_count = cali_data[1];
offset = 3;
@@ -683,12 +633,25 @@ static void tas2783_set_calib_params_to_device(struct tas2783_prv *tas_dev, u32
for (i = 0; i < ARRAY_SIZE(tas2783_cali_reg); i++) {
reg_value = cali_data[offset + i];
- buf[0] = reg_value >> 24;
- buf[1] = reg_value >> 16;
- buf[2] = reg_value >> 8;
- buf[3] = reg_value & 0xff;
- regmap_bulk_write(tas_dev->regmap, tas2783_cali_reg[i],
- buf, sizeof(u32));
+
+ /*
+ * A calibration value occupies four consecutive
+ * single-byte registers, most significant byte first.
+ * Write them one by one: these registers are declared
+ * as one byte wide by tas2783_sdca_mbq_size(), so a
+ * multi-byte regmap access would not match the layout.
+ */
+ for (j = 0; j < sizeof(u32); j++) {
+ ret = regmap_write(tas_dev->regmap,
+ tas2783_cali_reg[i] + j,
+ (reg_value >> (24 - 8 * j)) & 0xff);
+ if (ret) {
+ dev_err(tas_dev->dev,
+ "calib write to 0x%x failed, err=%d\n",
+ tas2783_cali_reg[i] + j, ret);
+ break;
+ }
+ }
}
break;
}
@@ -785,6 +748,7 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
const u8 *buf = NULL;
s32 img_sz, ret = 0, cur_file = 0;
s32 offset = 0;
+ u32 val[4], fw_version;
struct tas_fw_hdr *hdr __free(kfree) = kzalloc_obj(*hdr);
struct tas_fw_file *file __free(kfree) = kzalloc_obj(*file);
@@ -826,6 +790,11 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
}
mutex_lock(&tas_dev->pde_lock);
+ ret = regmap_bulk_read(tas_dev->regmap, TAS2783_FW_VERSION, &val, 4);
+ fw_version = (val[0] << 24) | (val[1] << 16) | (val[2] << 8) | val[3];
+ dev_dbg(tas_dev->dev, "Get Firmware version: %08x == %08x?, err=%d",
+ fw_version, tas_dev->fw_version, ret);
+
while (offset < (img_sz - FW_FL_HDR)) {
offset += tas_fw_get_next_file(&buf[offset], file);
dev_dbg(tas_dev->dev,
@@ -834,6 +803,13 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
file->version, file->length,
file->dest_addr, file->fw_data);
+ if (tas_dev->fw_version == fw_version &&
+ file->dest_addr >= PRAM_ADDR_START &&
+ (file->dest_addr + file->length) <= PRAM_ADDR_END) {
+ cur_file++;
+ dev_dbg(tas_dev->dev, "Ignore PRAM block");
+ continue;
+ }
ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
file->dest_addr,
file->length,
@@ -841,17 +817,34 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
if (ret < 0) {
dev_err(tas_dev->dev,
"FW download failed: %d", ret);
- break;
+ /*
+ * We do retry here for some special case of download
+ * failed after Power-On.
+ */
+ ret = sdw_nwrite_no_pm(tas_dev->sdw_peripheral,
+ file->dest_addr,
+ file->length,
+ file->fw_data);
+ if (ret < 0) {
+ dev_err(tas_dev->dev,
+ "FW download failed again: %d", ret);
+ break;
+ }
}
cur_file++;
}
mutex_unlock(&tas_dev->pde_lock);
+ regcache_drop_region(tas_dev->regmap, 0, UINT_MAX);
if (cur_file == 0) {
dev_err(tas_dev->dev, "fw with no files");
ret = -EINVAL;
} else {
tas2783_update_calibdata(tas_dev);
+ ret = regmap_bulk_read(tas_dev->regmap, TAS2783_FW_VERSION,
+ &val, 4);
+ tas_dev->fw_version = (val[0] << 24) | (val[1] << 16) |
+ (val[2] << 8) | val[3];
}
out:
@@ -1002,30 +995,6 @@ static s32 tas_sdw_hw_params(struct snd_pcm_substream *substream,
snd_sdw_params_to_config(substream, params,
&stream_config, &port_config);
- /*
- * The two mono amps each render one channel of the stereo stream:
- * snd_sdw_params_to_config() hands every codec the full mask for
- * playback, which leaves the pair in mirror mode and one channel
- * unreproduced. Claim a single channel instead, keyed off the
- * machine-assigned component prefix rather than the SoundWire
- * address, which is board-specific: soc_sdw_ti_amp.c names the amps
- * tas2783-1..4.
- *
- * Which side an amp then renders does not follow from the bit that
- * is set - sdw_compute_slave_ports() advances the payload offset by
- * the popcount of ch_mask and never looks at which bit it is - but
- * from the amp's position in the codec order of the DAI link, which
- * on these boards matches the prefix numbering.
- */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
- params_channels(params) == 2 && component->name_prefix) {
- const char *idx_str = strrchr(component->name_prefix, '-');
- unsigned long idx;
-
- if (idx_str && !kstrtoul(idx_str + 1, 10, &idx) && idx)
- port_config.ch_mask = (idx & 1) ? BIT(0) : BIT(1);
- }
-
/* port 1 for playback */
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
port_config.num = 1;
@@ -1114,7 +1083,7 @@ static const struct snd_soc_component_driver soc_codec_driver_tasdevice = {
.num_dapm_widgets = ARRAY_SIZE(tas_dapm_widgets),
.dapm_routes = tas_audio_map,
.num_dapm_routes = ARRAY_SIZE(tas_audio_map),
- .idle_bias_on = 1,
+ .idle_bias_on = 0,
.endianness = 1,
};
@@ -1274,6 +1243,8 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
ret = regmap_multi_reg_write(tas_dev->regmap, tas2783_init_seq,
ARRAY_SIZE(tas2783_init_seq));
+ /* Re-active AMP after resume. */
+ regmap_write(tas_dev->regmap, TASDEV_REG_SDW(0, 0, 2), 0);
if (ret)
dev_err(tas_dev->dev,
"init writes failed, err=%d", ret);
@@ -1453,6 +1424,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral,
tas_dev->dev = dev;
tas_dev->sdw_peripheral = peripheral;
tas_dev->hw_init = false;
+ tas_dev->fw_version = 0;
mutex_init(&tas_dev->calib_lock);
mutex_init(&tas_dev->pde_lock);
diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h
index d5996c73526c..2f034617e356 100644
--- a/sound/soc/codecs/tas2783.h
+++ b/sound/soc/codecs/tas2783.h
@@ -35,10 +35,12 @@
#define TAS2783_AMP_LEVEL_MASK GENMASK(5, 1)
#define PRAM_ADDR_START TASDEV_REG_SDW(0x8c, 0x01, 0x8)
-#define PRAM_ADDR_END TASDEV_REG_SDW(0x8c, 0xff, 0x7f)
+#define PRAM_ADDR_END TASDEV_REG_SDW(0x8c, 0xff, 0x80)
#define YRAM_ADDR_START TASDEV_REG_SDW(0x00, 0x02, 0x8)
#define YRAM_ADDR_END TASDEV_REG_SDW(0x00, 0x37, 0x7f)
+#define TAS2783_FW_VERSION TASDEV_REG_SDW(0x00, 0x20, 0x3c)
+
/* Calibration data */
#define TAS2783_CAL_R0 TASDEV_REG_SDW(0, 0x16, 0x4C)
#define TAS2783_CAL_INVR0 TASDEV_REG_SDW(0, 0x16, 0x5C)
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index 4d1c122c8f04..1ab82ef1c41d 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -743,11 +743,18 @@ static const struct snd_soc_dapm_route tas5086_dapm_routes[] = {
};
+static const u64 tas5086_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops tas5086_dai_ops = {
.hw_params = tas5086_hw_params,
.set_sysclk = tas5086_set_dai_sysclk,
.set_fmt = tas5086_set_dai_fmt,
.mute_stream = tas5086_mute_stream,
+ .auto_selectable_formats = &tas5086_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver tas5086_dai = {
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index 8b5f9accf120..5e2560347461 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -352,10 +352,17 @@ static int tas571x_set_bias_level(struct snd_soc_component *component,
return 0;
}
+static const u64 tas571x_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops tas571x_dai_ops = {
.set_fmt = tas571x_set_dai_fmt,
.hw_params = tas571x_hw_params,
.mute_stream = tas571x_mute,
+ .auto_selectable_formats = &tas571x_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
index 0bbcfaadf3f2..2539a097e743 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -682,11 +682,20 @@ static const struct snd_soc_component_driver soc_component_dev_tas5722 = {
#define TAS5720_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE |\
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 tas5720_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops tas5720_speaker_dai_ops = {
.hw_params = tas5720_hw_params,
.set_fmt = tas5720_set_dai_fmt,
.set_tdm_slot = tas5720_set_dai_tdm_slot,
.mute_stream = tas5720_mute,
+ .auto_selectable_formats = &tas5720_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c
index f5d50f8a1cfb..2718183e864c 100644
--- a/sound/soc/codecs/tas6424.c
+++ b/sound/soc/codecs/tas6424.c
@@ -378,11 +378,20 @@ static const struct snd_soc_component_driver soc_codec_dev_tas6424 = {
.endianness = 1,
};
+static const u64 tas6424_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops tas6424_speaker_dai_ops = {
.hw_params = tas6424_hw_params,
.set_fmt = tas6424_set_dai_fmt,
.set_tdm_slot = tas6424_set_dai_tdm_slot,
.mute_stream = tas6424_mute,
+ .auto_selectable_formats = &tas6424_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c
index f30479f1f53b..519103731768 100644
--- a/sound/soc/codecs/tfa9879.c
+++ b/sound/soc/codecs/tfa9879.c
@@ -248,10 +248,19 @@ static const struct regmap_config tfa9879_regmap = {
.cache_type = REGCACHE_RBTREE,
};
+static const u64 tfa9879_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops tfa9879_dai_ops = {
.hw_params = tfa9879_hw_params,
.mute_stream = tfa9879_mute_stream,
.set_fmt = tfa9879_set_fmt,
+ .auto_selectable_formats = &tfa9879_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c
index d7d958ecd8db..528c44446482 100644
--- a/sound/soc/codecs/tlv320adc3xxx.c
+++ b/sound/soc/codecs/tlv320adc3xxx.c
@@ -1362,10 +1362,24 @@ static int adc3xxx_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return 0;
}
+static const u64 adc3xxx_selectable_formats[] = {
+ /* Hi Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF,
+ /* Low Priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops adc3xxx_dai_ops = {
.hw_params = adc3xxx_hw_params,
.set_sysclk = adc3xxx_set_dai_sysclk,
.set_fmt = adc3xxx_set_dai_fmt,
+ .auto_selectable_formats = adc3xxx_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(adc3xxx_selectable_formats),
};
static struct snd_soc_dai_driver adc3xxx_dai = {
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index 4eb9cea27276..c29584d77649 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -916,10 +916,22 @@ static int adcx140_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
return 0;
}
+static const u64 adcx140_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops adcx140_dai_ops = {
.hw_params = adcx140_hw_params,
.set_fmt = adcx140_set_dai_fmt,
.set_tdm_slot = adcx140_set_dai_tdm_slot,
+ .auto_selectable_formats = &adcx140_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int adcx140_configure_gpo(struct adcx140_priv *adcx140)
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 04ec8fb5c6e5..f31ffa189580 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -507,6 +507,13 @@ static int tlv320aic23_set_bias_level(struct snd_soc_component *component,
#define AIC23_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 tlv320aic23_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops tlv320aic23_dai_ops = {
.prepare = tlv320aic23_pcm_prepare,
.hw_params = tlv320aic23_hw_params,
@@ -514,6 +521,8 @@ static const struct snd_soc_dai_ops tlv320aic23_dai_ops = {
.mute_stream = tlv320aic23_mute,
.set_fmt = tlv320aic23_set_dai_fmt,
.set_sysclk = tlv320aic23_set_dai_sysclk,
+ .auto_selectable_formats = &tlv320aic23_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 84e954311c1b..7dcc960a0b9e 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -208,11 +208,19 @@ static int aic26_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
#define AIC26_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_BE |\
SNDRV_PCM_FMTBIT_S24_BE | SNDRV_PCM_FMTBIT_S32_BE)
+static const u64 aic26_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A;
+
static const struct snd_soc_dai_ops aic26_dai_ops = {
.hw_params = aic26_hw_params,
.mute_stream = aic26_mute,
.set_sysclk = aic26_set_sysclk,
.set_fmt = aic26_set_fmt,
+ .auto_selectable_formats = &aic26_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 43bcbc5449e1..e0a8db435d6a 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1366,27 +1366,12 @@ static int aic31xx_set_jack(struct snd_soc_component *component,
static int aic31xx_codec_probe(struct snd_soc_component *component)
{
struct aic31xx_priv *aic31xx = snd_soc_component_get_drvdata(component);
- int i, ret;
+ int ret;
dev_dbg(aic31xx->dev, "## %s\n", __func__);
aic31xx->component = component;
- for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) {
- aic31xx->disable_nb[i].nb.notifier_call =
- aic31xx_regulator_event;
- aic31xx->disable_nb[i].aic31xx = aic31xx;
- ret = devm_regulator_register_notifier(
- aic31xx->supplies[i].consumer,
- &aic31xx->disable_nb[i].nb);
- if (ret) {
- dev_err(component->dev,
- "Failed to request regulator notifier: %d\n",
- ret);
- return ret;
- }
- }
-
regcache_cache_only(aic31xx->regmap, true);
regcache_mark_dirty(aic31xx->regmap);
@@ -1422,11 +1407,22 @@ static const struct snd_soc_component_driver soc_codec_driver_aic31xx = {
.endianness = 1,
};
+static const u64 aic31xx_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops aic31xx_dai_ops = {
.hw_params = aic31xx_hw_params,
.set_sysclk = aic31xx_set_dai_sysclk,
.set_fmt = aic31xx_set_dai_fmt,
.mute_stream = aic31xx_dac_mute,
+ .auto_selectable_formats = &aic31xx_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -1791,6 +1787,19 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c)
if (ret)
return dev_err_probe(aic31xx->dev, ret, "Failed to request supplies\n");
+ for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++) {
+ aic31xx->disable_nb[i].nb.notifier_call = aic31xx_regulator_event;
+ aic31xx->disable_nb[i].aic31xx = aic31xx;
+ ret = devm_regulator_register_notifier(aic31xx->supplies[i].consumer,
+ &aic31xx->disable_nb[i].nb);
+ if (ret) {
+ dev_err(aic31xx->dev,
+ "Failed to request regulator notifier: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
aic31xx_configure_ocmv(aic31xx);
if (aic31xx->irq > 0) {
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c
index deed61650e09..ad15ce9c6079 100644
--- a/sound/soc/codecs/tlv320aic32x4-clk.c
+++ b/sound/soc/codecs/tlv320aic32x4-clk.c
@@ -451,8 +451,8 @@ static struct aic32x4_clkdesc aic32x4_clkdesc_array[] = {
static struct clk *aic32x4_register_clk(struct device *dev,
struct aic32x4_clkdesc *desc)
{
- struct clk_init_data init;
struct clk_aic32x4 *priv;
+ struct clk_init_data init = {};
const char *devname = dev_name(dev);
init.ops = desc->ops;
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 9e0e4aa2b09c..7fec94768afb 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -48,8 +48,18 @@ struct aic32x4_priv {
enum aic32x4_type type;
unsigned int fmt;
+
+ struct clk_bulk_data clocks[7];
};
+#define AIC32X4_CLK_CODEC_CLKIN 0
+#define AIC32X4_CLK_PLL 1
+#define AIC32X4_CLK_NADC 2
+#define AIC32X4_CLK_MADC 3
+#define AIC32X4_CLK_NDAC 4
+#define AIC32X4_CLK_MDAC 5
+#define AIC32X4_CLK_BDIV 6
+
static int aic32x4_reset_adc(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -591,14 +601,10 @@ static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_component *component = codec_dai->component;
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
struct clk *mclk;
- struct clk *pll;
-
- pll = devm_clk_get(component->dev, "pll");
- if (IS_ERR(pll))
- return PTR_ERR(pll);
- mclk = clk_get_parent(pll);
+ mclk = clk_get_parent(aic32x4->clocks[AIC32X4_CLK_PLL].clk);
return clk_set_rate(mclk, freq);
}
@@ -750,17 +756,7 @@ static int aic32x4_setup_clocks(struct snd_soc_component *component,
unsigned long adc_clock_rate, dac_clock_rate;
int ret;
- struct clk_bulk_data clocks[] = {
- { .id = "pll" },
- { .id = "nadc" },
- { .id = "madc" },
- { .id = "ndac" },
- { .id = "mdac" },
- { .id = "bdiv" },
- };
- ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
- if (ret)
- return ret;
+ struct clk_bulk_data *clocks = &aic32x4->clocks[AIC32X4_CLK_PLL];
ret = aic32x4_configure_rate(component, sample_rate, &aosr,
&adc_resource_class, &dac_resource_class,
@@ -876,18 +872,15 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
int ret;
struct clk_bulk_data clocks[] = {
- { .id = "madc" },
- { .id = "mdac" },
- { .id = "bdiv" },
+ aic32x4->clocks[AIC32X4_CLK_MADC],
+ aic32x4->clocks[AIC32X4_CLK_MDAC],
+ aic32x4->clocks[AIC32X4_CLK_BDIV],
};
- ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
- if (ret)
- return ret;
-
switch (level) {
case SND_SOC_BIAS_ON:
ret = clk_bulk_prepare_enable(ARRAY_SIZE(clocks), clocks);
@@ -916,11 +909,19 @@ static int aic32x4_set_bias_level(struct snd_soc_component *component,
| SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_3LE \
| SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 aic32x4_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
static const struct snd_soc_dai_ops aic32x4_ops = {
.hw_params = aic32x4_hw_params,
.mute_stream = aic32x4_mute,
.set_fmt = aic32x4_set_dai_fmt,
.set_sysclk = aic32x4_set_dai_sysclk,
+ .auto_selectable_formats = &aic32x4_selectable_formats,
+ .num_auto_selectable_formats = 1,
.no_capture_mute = 1,
};
@@ -962,23 +963,16 @@ static int aic32x4_component_probe(struct snd_soc_component *component)
{
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
u32 tmp_reg;
- int ret;
- struct clk_bulk_data clocks[] = {
- { .id = "codec_clkin" },
- { .id = "pll" },
- { .id = "bdiv" },
- { .id = "mdac" },
- };
-
- ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
- if (ret)
- return ret;
+ struct clk *codec_clkin = aic32x4->clocks[AIC32X4_CLK_CODEC_CLKIN].clk;
+ struct clk *pll = aic32x4->clocks[AIC32X4_CLK_PLL].clk;
+ struct clk *bdiv = aic32x4->clocks[AIC32X4_CLK_BDIV].clk;
+ struct clk *mdac = aic32x4->clocks[AIC32X4_CLK_MDAC].clk;
aic32x4_setup_gpios(component);
- clk_set_parent(clocks[0].clk, clocks[1].clk);
- clk_set_parent(clocks[2].clk, clocks[3].clk);
+ clk_set_parent(codec_clkin, pll);
+ clk_set_parent(bdiv, mdac);
/* Power platform configuration */
if (aic32x4->power_cfg & AIC32X4_PWR_MICBIAS_2075_LDOIN) {
@@ -1119,23 +1113,16 @@ static int aic32x4_tas2505_component_probe(struct snd_soc_component *component)
{
struct aic32x4_priv *aic32x4 = snd_soc_component_get_drvdata(component);
u32 tmp_reg;
- int ret;
-
- struct clk_bulk_data clocks[] = {
- { .id = "codec_clkin" },
- { .id = "pll" },
- { .id = "bdiv" },
- { .id = "mdac" },
- };
- ret = devm_clk_bulk_get(component->dev, ARRAY_SIZE(clocks), clocks);
- if (ret)
- return ret;
+ struct clk *codec_clkin = aic32x4->clocks[AIC32X4_CLK_CODEC_CLKIN].clk;
+ struct clk *pll = aic32x4->clocks[AIC32X4_CLK_PLL].clk;
+ struct clk *bdiv = aic32x4->clocks[AIC32X4_CLK_BDIV].clk;
+ struct clk *mdac = aic32x4->clocks[AIC32X4_CLK_MDAC].clk;
aic32x4_setup_gpios(component);
- clk_set_parent(clocks[0].clk, clocks[1].clk);
- clk_set_parent(clocks[2].clk, clocks[3].clk);
+ clk_set_parent(codec_clkin, pll);
+ clk_set_parent(bdiv, mdac);
/* Power platform configuration */
if (aic32x4->power_cfg & AIC32X4_PWR_AVDD_DVDD_WEAK_DISABLE)
@@ -1354,6 +1341,21 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap,
if (ret)
goto err_disable_regulators;
+ aic32x4->clocks[AIC32X4_CLK_CODEC_CLKIN].id = "codec_clkin";
+ aic32x4->clocks[AIC32X4_CLK_PLL].id = "pll";
+ aic32x4->clocks[AIC32X4_CLK_NADC].id = "nadc";
+ aic32x4->clocks[AIC32X4_CLK_MADC].id = "madc";
+ aic32x4->clocks[AIC32X4_CLK_NDAC].id = "ndac";
+ aic32x4->clocks[AIC32X4_CLK_MDAC].id = "mdac";
+ aic32x4->clocks[AIC32X4_CLK_BDIV].id = "bdiv";
+
+ ret = devm_clk_bulk_get(dev, ARRAY_SIZE(aic32x4->clocks),
+ aic32x4->clocks);
+ if (ret) {
+ dev_err(dev, "Failed to get clocks\n");
+ goto err_disable_regulators;
+ }
+
switch (aic32x4->type) {
case AIC32X4_TYPE_TAS2505:
ret = devm_snd_soc_register_component(dev,
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index b38393a8130f..be83111accaa 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1500,6 +1500,18 @@ static int aic3x_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 aic3x_selectable_formats[] = {
+ /* Hi Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF,
+ /* Low Priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops aic3x_dai_ops = {
.hw_params = aic3x_hw_params,
.prepare = aic3x_prepare,
@@ -1507,6 +1519,8 @@ static const struct snd_soc_dai_ops aic3x_dai_ops = {
.set_sysclk = aic3x_set_dai_sysclk,
.set_fmt = aic3x_set_dai_fmt,
.set_tdm_slot = aic3x_set_dai_tdm_slot,
+ .auto_selectable_formats = aic3x_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(aic3x_selectable_formats),
.no_capture_mute = 1,
};
@@ -1627,24 +1641,9 @@ static int aic3x_init(struct snd_soc_component *component)
static int aic3x_component_probe(struct snd_soc_component *component)
{
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
- int ret, i;
aic3x->component = component;
- for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
- aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;
- aic3x->disable_nb[i].aic3x = aic3x;
- ret = devm_regulator_register_notifier(
- aic3x->supplies[i].consumer,
- &aic3x->disable_nb[i].nb);
- if (ret) {
- dev_err(component->dev,
- "Failed to request regulator notifier: %d\n",
- ret);
- return ret;
- }
- }
-
regcache_mark_dirty(aic3x->regmap);
aic3x_init(component);
@@ -1845,6 +1844,19 @@ int aic3x_probe(struct device *dev, struct regmap *regmap, kernel_ulong_t driver
if (ret)
return dev_err_probe(dev, ret, "Failed to request supplies\n");
+ for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++) {
+ aic3x->disable_nb[i].nb.notifier_call = aic3x_regulator_event;
+ aic3x->disable_nb[i].aic3x = aic3x;
+ ret = devm_regulator_register_notifier(aic3x->supplies[i].consumer,
+ &aic3x->disable_nb[i].nb);
+ if (ret) {
+ dev_err(dev,
+ "Failed to request regulator notifier: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
aic3x_configure_ocmv(dev, aic3x);
ret = devm_snd_soc_register_component(dev, &soc_component_dev_aic3x, &aic3x_dai, 1);
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index 9bd2ddd8dacd..b91ab42882d1 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -914,7 +914,7 @@ static int dac33_prepare_chip(struct snd_pcm_substream *substream,
/* OSC calibration time */
dac33_write(component, DAC33_CALIB_TIME, 96);
- /* adjustment treshold & step */
+ /* adjustment threshold & step */
dac33_write(component, DAC33_INT_OSC_CTRL_B, DAC33_ADJTHRSHLD(2) |
DAC33_ADJSTEP(1));
@@ -1435,6 +1435,12 @@ static const struct snd_soc_component_driver soc_component_dev_tlv320dac33 = {
SNDRV_PCM_RATE_48000)
#define DAC33_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 dac33_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A;
+
static const struct snd_soc_dai_ops dac33_dai_ops = {
.startup = dac33_startup,
.shutdown = dac33_shutdown,
@@ -1443,6 +1449,8 @@ static const struct snd_soc_dai_ops dac33_dai_ops = {
.delay = dac33_dai_delay,
.set_sysclk = dac33_set_dai_sysclk,
.set_fmt = dac33_set_dai_fmt,
+ .auto_selectable_formats = &dac33_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver dac33_dai = {
diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c
index b70c9d931e1e..e772dd39a849 100644
--- a/sound/soc/codecs/tscs454.c
+++ b/sound/soc/codecs/tscs454.c
@@ -3233,6 +3233,17 @@ static int tscs454_prepare(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 tscs454_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static struct snd_soc_dai_ops const tscs454_dai1_ops = {
.set_sysclk = tscs454_set_sysclk,
.set_bclk_ratio = tscs454_set_bclk_ratio,
@@ -3241,6 +3252,8 @@ static struct snd_soc_dai_ops const tscs454_dai1_ops = {
.hw_params = tscs454_hw_params,
.hw_free = tscs454_hw_free,
.prepare = tscs454_prepare,
+ .auto_selectable_formats = &tscs454_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_ops const tscs454_dai23_ops = {
@@ -3251,6 +3264,8 @@ static struct snd_soc_dai_ops const tscs454_dai23_ops = {
.hw_params = tscs454_hw_params,
.hw_free = tscs454_hw_free,
.prepare = tscs454_prepare,
+ .auto_selectable_formats = &tscs454_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int tscs454_probe(struct snd_soc_component *component)
diff --git a/sound/soc/codecs/twl4030.c b/sound/soc/codecs/twl4030.c
index 9476cdfd4dde..442624cf2a31 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -202,34 +202,31 @@ static void
twl4030_get_board_param_values(struct twl4030_board_params *board_params,
struct device_node *node)
{
- int value;
-
of_property_read_u32(node, "ti,digimic_delay", &board_params->digimic_delay);
of_property_read_u32(node, "ti,ramp_delay_value", &board_params->ramp_delay_value);
of_property_read_u32(node, "ti,offset_cncl_path", &board_params->offset_cncl_path);
- if (!of_property_read_u32(node, "ti,hs_extmute", &value))
- board_params->hs_extmute = value;
+ board_params->hs_extmute = of_property_read_bool(node, "ti,hs_extmute");
if (of_property_present(node, "ti,hs_extmute_gpio"))
board_params->hs_extmute = 1;
}
static struct twl4030_board_params*
-twl4030_get_board_params(struct snd_soc_component *component)
+twl4030_get_board_params(struct device *dev)
{
struct twl4030_board_params *board_params = NULL;
struct device_node *twl4030_codec_node = NULL;
- twl4030_codec_node = of_get_child_by_name(component->dev->parent->of_node,
+ twl4030_codec_node = of_get_child_by_name(dev->parent->of_node,
"codec");
if (twl4030_codec_node) {
- board_params = devm_kzalloc(component->dev,
+ board_params = devm_kzalloc(dev,
sizeof(struct twl4030_board_params),
GFP_KERNEL);
if (!board_params) {
of_node_put(twl4030_codec_node);
- return NULL;
+ return ERR_PTR(-ENOMEM);
}
twl4030_get_board_param_values(board_params, twl4030_codec_node);
of_node_put(twl4030_codec_node);
@@ -238,21 +235,22 @@ twl4030_get_board_params(struct snd_soc_component *component)
return board_params;
}
-static int twl4030_init_chip(struct snd_soc_component *component)
+static int twl4030_get_hw_params(struct device *dev,
+ struct twl4030_priv *twl4030)
{
struct twl4030_board_params *board_params;
- struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
- u8 reg, byte;
- int i = 0;
- board_params = twl4030_get_board_params(component);
+ board_params = twl4030_get_board_params(dev);
+ if (IS_ERR(board_params))
+ return PTR_ERR(board_params);
if (board_params && board_params->hs_extmute) {
- board_params->hs_extmute_gpio = devm_gpiod_get_optional(component->dev,
+ board_params->hs_extmute_gpio = devm_gpiod_get_optional(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),
+ return dev_err_probe(dev,
+ PTR_ERR(board_params->hs_extmute_gpio),
"Failed to get hs_extmute GPIO\n");
if (board_params->hs_extmute_gpio) {
@@ -260,7 +258,7 @@ static int twl4030_init_chip(struct snd_soc_component *component)
} else {
u8 pin_mux;
- dev_info(component->dev, "use TWL4030 GPIO6\n");
+ dev_info(dev, "use TWL4030 GPIO6\n");
/* Set TWL4030 GPIO6 as EXTMUTE signal */
twl_i2c_read_u8(TWL4030_MODULE_INTBR, &pin_mux,
@@ -272,6 +270,18 @@ static int twl4030_init_chip(struct snd_soc_component *component)
}
}
+ twl4030->board_params = board_params;
+
+ return 0;
+}
+
+static int twl4030_init_chip(struct snd_soc_component *component)
+{
+ struct twl4030_priv *twl4030 = snd_soc_component_get_drvdata(component);
+ struct twl4030_board_params *board_params = twl4030->board_params;
+ u8 reg, byte;
+ int i = 0;
+
/* Initialize the local ctl register cache */
tw4030_init_ctl_cache(twl4030);
@@ -291,8 +301,6 @@ static int twl4030_init_chip(struct snd_soc_component *component)
if (!board_params)
return 0;
- twl4030->board_params = board_params;
-
reg = twl4030_read(component, TWL4030_REG_HS_POPN_SET);
reg &= ~TWL4030_RAMP_DELAY;
reg |= (board_params->ramp_delay_value << 2);
@@ -2097,6 +2105,10 @@ static int twl4030_voice_set_tristate(struct snd_soc_dai *dai, int tristate)
#define TWL4030_RATES (SNDRV_PCM_RATE_8000_48000)
#define TWL4030_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 twl4030_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A;
+
static const struct snd_soc_dai_ops twl4030_dai_hifi_ops = {
.startup = twl4030_startup,
.shutdown = twl4030_shutdown,
@@ -2104,8 +2116,14 @@ static const struct snd_soc_dai_ops twl4030_dai_hifi_ops = {
.set_sysclk = twl4030_set_dai_sysclk,
.set_fmt = twl4030_set_dai_fmt,
.set_tristate = twl4030_set_tristate,
+ .auto_selectable_formats = &twl4030_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
+static const u64 twl4030_voice_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops twl4030_dai_voice_ops = {
.startup = twl4030_voice_startup,
.shutdown = twl4030_voice_shutdown,
@@ -2113,6 +2131,8 @@ static const struct snd_soc_dai_ops twl4030_dai_voice_ops = {
.set_sysclk = twl4030_voice_set_dai_sysclk,
.set_fmt = twl4030_voice_set_dai_fmt,
.set_tristate = twl4030_voice_set_tristate,
+ .auto_selectable_formats = &twl4030_voice_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver twl4030_dai[] = {
@@ -2154,15 +2174,9 @@ static struct snd_soc_dai_driver twl4030_dai[] = {
static int twl4030_soc_probe(struct snd_soc_component *component)
{
- struct twl4030_priv *twl4030;
+ struct twl4030_priv *twl4030 = dev_get_drvdata(component->dev);
- twl4030 = devm_kzalloc(component->dev, sizeof(struct twl4030_priv),
- GFP_KERNEL);
- if (!twl4030)
- return -ENOMEM;
snd_soc_component_set_drvdata(component, twl4030);
- /* Set the defaults, and power up the codec */
- twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
return twl4030_init_chip(component);
}
@@ -2184,6 +2198,22 @@ static const struct snd_soc_component_driver soc_component_dev_twl4030 = {
static int twl4030_codec_probe(struct platform_device *pdev)
{
+ struct twl4030_priv *twl4030;
+ int ret;
+
+ twl4030 = devm_kzalloc(&pdev->dev, sizeof(*twl4030), GFP_KERNEL);
+ if (!twl4030)
+ return -ENOMEM;
+
+ /* Set the defaults, and power up the codec */
+ twl4030->sysclk = twl4030_audio_get_mclk() / 1000;
+
+ ret = twl4030_get_hw_params(&pdev->dev, twl4030);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, twl4030);
+
return devm_snd_soc_register_component(&pdev->dev,
&soc_component_dev_twl4030,
twl4030_dai, ARRAY_SIZE(twl4030_dai));
diff --git a/sound/soc/codecs/uda1334.c b/sound/soc/codecs/uda1334.c
index 54c5cb5b3d4b..7ae6afdba38b 100644
--- a/sound/soc/codecs/uda1334.c
+++ b/sound/soc/codecs/uda1334.c
@@ -193,12 +193,18 @@ static int uda1334_mute_stream(struct snd_soc_dai *dai, int mute, int stream)
#define UDA1334_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 uda1334_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops uda1334_dai_ops = {
.startup = uda1334_startup,
.shutdown = uda1334_shutdown,
.set_sysclk = uda1334_set_dai_sysclk,
.set_fmt = uda1334_set_fmt,
.mute_stream = uda1334_mute_stream,
+ .auto_selectable_formats = &uda1334_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver uda1334_dai = {
diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c
index 4e2d6240a869..63d087590ac9 100644
--- a/sound/soc/codecs/uda1342.c
+++ b/sound/soc/codecs/uda1342.c
@@ -223,6 +223,11 @@ static const struct snd_soc_dapm_route uda1342_dapm_routes[] = {
{ "VOUTR", NULL, "DAC" },
};
+static const u64 uda1342_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops uda1342_dai_ops = {
.startup = uda1342_startup,
.shutdown = uda1342_shutdown,
@@ -230,6 +235,8 @@ static const struct snd_soc_dai_ops uda1342_dai_ops = {
.mute_stream = uda1342_mute,
.set_sysclk = uda1342_set_dai_sysclk,
.set_fmt = uda1342_set_dai_fmt,
+ .auto_selectable_formats = &uda1342_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver uda1342_dai = {
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 1dd09d10fc4a..02adc534dfb0 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -653,11 +653,18 @@ static int uda1380_set_bias_level(struct snd_soc_component *component,
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 |\
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+static const u64 uda1380_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops uda1380_dai_ops = {
.hw_params = uda1380_pcm_hw_params,
.shutdown = uda1380_pcm_shutdown,
.trigger = uda1380_trigger,
.set_fmt = uda1380_set_dai_fmt_both,
+ .auto_selectable_formats = &uda1380_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops uda1380_dai_ops_playback = {
@@ -665,6 +672,8 @@ static const struct snd_soc_dai_ops uda1380_dai_ops_playback = {
.shutdown = uda1380_pcm_shutdown,
.trigger = uda1380_trigger,
.set_fmt = uda1380_set_dai_fmt_playback,
+ .auto_selectable_formats = &uda1380_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops uda1380_dai_ops_capture = {
@@ -672,6 +681,8 @@ static const struct snd_soc_dai_ops uda1380_dai_ops_capture = {
.shutdown = uda1380_pcm_shutdown,
.trigger = uda1380_trigger,
.set_fmt = uda1380_set_dai_fmt_capture,
+ .auto_selectable_formats = &uda1380_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver uda1380_dai[] = {
diff --git a/sound/soc/codecs/wcd-mbhc-v2.c b/sound/soc/codecs/wcd-mbhc-v2.c
index bb0c8478a8eb..850be84d65ce 100644
--- a/sound/soc/codecs/wcd-mbhc-v2.c
+++ b/sound/soc/codecs/wcd-mbhc-v2.c
@@ -864,7 +864,7 @@ static int wcd_measure_adc_continuous(struct wcd_mbhc *mbhc)
u8 adc_en;
/* Pre-requisites for ADC continuous measurement */
- /* Read legacy electircal detection and disable */
+ /* Read legacy electrical detection and disable */
wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0x00);
/* Set ADC to continuous measurement */
wcd_mbhc_write_field(mbhc, WCD_MBHC_ADC_MODE, 1);
@@ -965,7 +965,7 @@ static int wcd_check_cross_conn(struct wcd_mbhc *mbhc)
if (wcd_mbhc_read_field(mbhc, WCD_MBHC_HPH_PA_EN))
return -EINVAL;
- /* Read legacy electircal detection and disable */
+ /* Read legacy electrical detection and disable */
elect_ctl = wcd_mbhc_read_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC);
wcd_mbhc_write_field(mbhc, WCD_MBHC_ELECT_SCHMT_ISRC, 0);
@@ -1470,14 +1470,14 @@ int wcd_dt_parse_mbhc_data(struct device *dev, struct wcd_mbhc_config *cfg)
ret = of_property_read_u32(np, "qcom,mbhc-headset-vthreshold-microvolt",
&microvolt);
if (ret)
- dev_dbg(dev, "missing qcom,mbhc-hs-mic-max-vthreshold--microvolt in dt node\n");
+ dev_dbg(dev, "missing qcom,mbhc-headset-vthreshold-microvolt in dt node\n");
else
cfg->hs_thr = microvolt/1000;
ret = of_property_read_u32(np, "qcom,mbhc-headphone-vthreshold-microvolt",
&microvolt);
if (ret)
- dev_dbg(dev, "missing qcom,mbhc-hs-mic-min-vthreshold-microvolt entry\n");
+ dev_dbg(dev, "missing qcom,mbhc-headphone-vthreshold-microvolt entry\n");
else
cfg->hph_thr = microvolt/1000;
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index 40de279a9750..a98d1fd78925 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5096,6 +5096,13 @@ static int wcd9335_slim_probe(struct slim_device *slim)
return 0;
}
+static void wcd9335_put_device_action(void *data)
+{
+ struct device *dev = data;
+
+ put_device(dev);
+}
+
static int wcd9335_slim_status(struct slim_device *sdev,
enum slim_device_status status)
{
@@ -5120,28 +5127,40 @@ static int wcd9335_slim_status(struct slim_device *sdev,
return -EINVAL;
}
+ ret = devm_add_action_or_reset(dev, wcd9335_put_device_action,
+ &wcd->slim_ifc_dev->dev);
+ if (ret)
+ return ret;
+
slim_get_logical_addr(wcd->slim_ifc_dev);
wcd->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config);
- if (IS_ERR(wcd->regmap))
+ if (IS_ERR(wcd->regmap)) {
+ put_device(&wcd->slim_ifc_dev->dev);
return dev_err_probe(dev, PTR_ERR(wcd->regmap),
"Failed to allocate slim register map\n");
+ }
wcd->if_regmap = regmap_init_slimbus(wcd->slim_ifc_dev,
&wcd9335_ifc_regmap_config);
- if (IS_ERR(wcd->if_regmap))
+ if (IS_ERR(wcd->if_regmap)) {
+ put_device(&wcd->slim_ifc_dev->dev);
return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
"Failed to allocate ifc register map\n");
+ }
ret = wcd9335_bring_up(wcd);
if (ret) {
dev_err(dev, "Failed to bringup WCD9335\n");
+ put_device(&wcd->slim_ifc_dev->dev);
return ret;
}
ret = wcd9335_irq_init(wcd);
- if (ret)
+ if (ret) {
+ put_device(&wcd->slim_ifc_dev->dev);
return ret;
+ }
wcd9335_probe(wcd);
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index caca5cc25100..0ed24cca088f 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -2130,9 +2130,9 @@ static struct clk *wcd934x_register_mclk_output(struct wcd934x_codec *wcd)
struct device *dev = wcd->dev;
struct device_node *np = dev->parent->of_node;
const char *parent_clk_name = NULL;
+ struct clk_init_data init = {};
const char *clk_name = "mclk";
struct clk_hw *hw;
- struct clk_init_data init;
int ret;
if (of_property_read_u32(np, "clock-frequency", &wcd->rate))
diff --git a/sound/soc/codecs/wcd9378-sdca.c b/sound/soc/codecs/wcd9378-sdca.c
new file mode 100644
index 000000000000..7c15f885390c
--- /dev/null
+++ b/sound/soc/codecs/wcd9378-sdca.c
@@ -0,0 +1,1080 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright (c) 2025 Qualcomm Technologies, Inc. All rights reserved.
+
+/*
+ * WCD9378 (Tambora) SDCA SimpleJack codec. Supplies the static SDCA
+ * topology on DT platforms where no ACPI/DisCo enumeration exists.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/kernel.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/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+#include <sound/sdca.h>
+#include <sound/sdca_class.h>
+#include <sound/sdca_function.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include "wcd9378-sdca.h"
+
+struct wcd9378_priv {
+ struct sdca_class_drv class;
+};
+
+static struct sdca_entity wcd9378_sdca_entities[];
+/*
+ * Entity array index map. Order matches the ASL entity-id-list;
+ * Function (Entity 0) is last.
+ *
+ * [0] E001 IT 41 (0x1) [12] E00F IT 33 (0xf)
+ * [1] E002 CS 41 (0x2) [13] E010 PDE 34 (0x10)
+ * [2] E003 MFPU 21 (0x3) [14] E011 FU 33 (0x11)
+ * [3] E004 XU 42 (0x4) [15] E012 SU 35 (0x12)
+ * [4] E007 SU 43 (0x7) [16] E013 XU 36 (0x13)
+ * [5] E008 SU 45 (0x8) [17] E015 CS 36 (0x15)
+ * [6] E009 PDE 47 (0x9) [18] E016 OT 36 (0x16)
+ * [7] E00A OT 43 (0xa) [19] E017 MFPU 236 (0x17)
+ * [8] E00B OT 45 (0xb) [20] E018 CS 236 (0x18)
+ * [9] E00C GE 35 (0xc) [21] E019 OT 236 (0x19)
+ * [10] E00D IT 131 (0xd) [22] E006 FU 6 (0x6)
+ * [11] E00E CS 131 (0xe) [23] E000 Function (0x0)
+ */
+#define QSJ_IT41 0
+#define QSJ_CS41 1
+#define QSJ_MFPU21 2
+#define QSJ_XU42 3
+#define QSJ_SU43 4
+#define QSJ_SU45 5
+#define QSJ_PDE47 6
+#define QSJ_OT43 7
+#define QSJ_OT45 8
+#define QSJ_GE35 9
+#define QSJ_IT131 10
+#define QSJ_CS131 11
+#define QSJ_IT33 12
+#define QSJ_PDE34 13
+#define QSJ_FU33 14
+#define QSJ_SU35 15
+#define QSJ_XU36 16
+#define QSJ_CS36 17
+#define QSJ_OT36 18
+#define QSJ_MFPU236 19
+#define QSJ_CS236 20
+#define QSJ_OT236 21
+#define QSJ_FU6 22
+
+/* Range data: {cols, rows, data[cols*rows]}, transcribed from ASL. */
+
+/* IT 41 Usage: HIFI (0x2). ULP (0x3) does not route to the HPH analog driver. */
+static u32 range_it41_usage_data[] = {
+ /* usage, CBN, sample_rate, sample_width, full_scale, noise_floor, tag */
+ 0x2, 0x2d0, 0xbb80, 0x10, 0x0, 0x0, 0x0,
+};
+
+static u32 range_it41_cluster_data[] = { 0x1, 0x1 };
+
+/* IT 41 DataPort selector: DP6 (HPH render). */
+static u32 range_it41_dp_data[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/* CS 41 SampleRateIndex: 1 -> 48kHz PCM. */
+static u32 range_cs41_sr_data[] = { 0x1, 0xbb80 };
+
+/* SU selector: disconnected / connected. */
+static u32 range_su_sel_data[] = { 0x0, 0x1 };
+
+/* PDE Requested_PS: PS0 / PS3. */
+static u32 range_pde_req_ps_data[] = { 0x0, 0x3 };
+
+/*
+ * GE 35 SelectedMode -> terminal type.
+ *
+ * Tambora MBHC only handles mechanical detection; ADC HP/HS
+ * discrimination is not implemented, so modes 0 (Unplugged) and 1
+ * (Unknown) are aliased to Headphone (mode 4) to keep the DAPM path
+ * alive. Mode 2 (Line-out) is not fitted on this board.
+ */
+static u32 range_ge35_mode_data[] = {
+ 0x0, 0x6c0, /* Unplugged -> HPH (alias) */
+ 0x1, 0x6c0, /* Unknown -> HPH (alias) */
+ 0x3, 0x6d0, /* Headset */
+ 0x4, 0x6c0, /* Headphone */
+};
+
+/* IT 131 Usage: optimization render stream at 192kHz. */
+static u32 range_it131_usage_data[] = {
+ 0x3, 0x334, 0x2ee00, 0x8, 0x0, 0x0, 0x0,
+};
+
+static u32 range_it131_cluster_data[] = { 0x1, 0x3 };
+
+/* IT 131 DataPort selector: DP7. */
+static u32 range_it131_dp_data[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/* CS 131 SampleRateIndex: 1 -> 192kHz. */
+static u32 range_cs131_sr_data[] = { 0x1, 0x2ee00 };
+
+/* IT 33 MIC_BIAS: SDCA MIC_BIAS index 0x5 (2.75V). */
+static u32 range_it33_micbias_data[] = { 0x5 };
+
+static u32 range_it33_usage_data[] = {
+ 0x1, 0x2c6, 0x0, 0x0, 0x0, 0x0, 0x0,
+};
+
+static u32 range_it33_cluster_data[] = { 0x1, 0x2 };
+
+static u32 range_cs36_sr_data[] = { 0x1, 0xbb80 };
+
+/* OT 36 Usage: PDM capture, host-visible 48kHz/16-bit PCM. */
+static u32 range_ot36_usage_data[] = {
+ 0x1, 0x2c6, 0xbb80, 0x10, 0x0, 0x0, 0x0,
+};
+
+/* OT 36 DataPort selector: DP2. */
+static u32 range_ot36_dp_data[] = {
+ 0xff, 0xff, 0x2, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static u32 range_cs236_sr_data[] = { 0x1, 0xbb80 };
+
+/* OT 236 Usage: optimization capture at 192kHz (clocked by CS 131). */
+static u32 range_ot236_usage_data[] = {
+ 0x1, 0x334, 0x2ee00, 0x8, 0x0, 0x0, 0x0,
+};
+
+/* OT 236 DataPort selector: DP5. */
+static u32 range_ot236_dp_data[] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+/* Entity 0 (Function) Control Values */
+static int ctrl_fun_sdca_ver_vals[] = { 0x11 };
+static int ctrl_fun_type_vals[] = { 0x08 }; /* SimpleJack */
+static int ctrl_fun_man_id_vals[] = { 0x0217 };
+static int ctrl_fun_id_vals[] = { 0x3 };
+static int ctrl_fun_ver_vals[] = { 0x0 };
+static int ctrl_dev_sdca_ver_vals[] = { 0x11 };
+
+/* Entity 1 (IT 41) Control Values */
+static int ctrl_it41_latency_vals[] = { 0x0 };
+static int ctrl_it41_cluster_vals[] = { 0x1 };
+static int ctrl_it41_dp_vals[] = { 0x6 };
+
+/* Entity 2 (CS 41) Control Values */
+static int ctrl_cs41_sr_vals[] = { 0x1 };
+
+/* Entity 3 (MFPU 21) Control Values */
+static int ctrl_mfpu21_bypass_vals[] = { 0x1 };
+
+/* Entity 4 (XU 42) Control Values */
+static int ctrl_xu42_id_vals[] = { 0x2131 };
+static int ctrl_xu42_ver_vals[] = { 0x1 };
+
+/* Entity 0xd (IT 131) Control Values */
+static int ctrl_it131_latency_vals[] = { 0x0 };
+static int ctrl_it131_cluster_vals[] = { 0x1 };
+static int ctrl_it131_dp_vals[] = { 0x7 };
+
+/* Entity 0xe (CS 131) Control Values */
+static int ctrl_cs131_sr_vals[] = { 0x1 };
+
+/* IT 33 MIC_BIAS: fixed 2.75V (SDCA MIC_BIAS index 0x5), reapplied by PDE34. */
+static int ctrl_it33_micbias_vals[] = { 0x5 };
+static int ctrl_it33_latency_vals[] = { 0x0 };
+static int ctrl_it33_cluster_vals[] = { 0x1 };
+
+/* Entity 0x13 (XU 36) Control Values */
+static int ctrl_xu36_bypass_vals[] = { 0x1 };
+static int ctrl_xu36_id_vals[] = { 0x2131 };
+static int ctrl_xu36_ver_vals[] = { 0x1 };
+
+/* Entity 0x15 (CS 36) Control Values */
+static int ctrl_cs36_sr_vals[] = { 0x1 };
+
+/* Entity 0x16 (OT 36) Control Values */
+static int ctrl_ot36_latency_vals[] = { 0x0 };
+static int ctrl_ot36_dp_vals[] = { 0x2 };
+
+/* Entity 0x17 (MFPU 236) Control Values */
+static int ctrl_mfpu236_bypass_vals[] = { 0x1 };
+
+/* Entity 0x18 (CS 236) Control Values */
+static int ctrl_cs236_sr_vals[] = { 0x1 };
+
+/* Entity 0x19 (OT 236) Control Values */
+static int ctrl_ot236_latency_vals[] = { 0x0 };
+static int ctrl_ot236_dp_vals[] = { 0x5 };
+
+/* Entity 0 (Function) Controls */
+static struct sdca_control entity0_controls[] = {
+ { .sel = 0x1, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_COMMIT_GROUP_MASK_NAME },
+ { .sel = 0x4, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_fun_sdca_ver_vals, .has_fixed = true,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_FUNCTION_SDCA_VERSION_NAME },
+ { .sel = 0x5, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_fun_type_vals, .has_fixed = true,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_FUNCTION_TYPE_NAME },
+ { .sel = 0x6, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_fun_man_id_vals, .has_fixed = true,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_FUNCTION_MANUFACTURER_ID_NAME },
+ { .sel = 0x7, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_fun_id_vals, .has_fixed = true,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_FUNCTION_ID_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_fun_ver_vals, .has_fixed = true,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_FUNCTION_VERSION_NAME },
+ { .sel = 0x9, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_FUNCTION_EXTENSION_ID_NAME },
+ { .sel = 0xa, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_FUNCTION_EXTENSION_VERSION_NAME },
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_RW1C, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_FUNCTION_STATUS_NAME },
+ { .sel = 0x11, .mode = SDCA_ACCESS_MODE_RW1S, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_FUNCTION_ACTION_NAME },
+ { .sel = 0x2c, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_DEVICE_MANUFACTURER_ID_NAME },
+ { .sel = 0x2d, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_DEVICE_PART_ID_NAME },
+ { .sel = 0x2e, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_DEVICE_VERSION_NAME },
+ { .sel = 0x2f, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_dev_sdca_ver_vals, .has_fixed = true,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_DEVICE_SDCA_VERSION_NAME },
+};
+
+/* Entity 1 (IT 41) Controls */
+static struct sdca_control entity_it41_controls[] = {
+ { .sel = 0x4, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x7, .rows = 0x1, .data = range_it41_usage_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_USAGE_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_it41_latency_vals, .has_fixed = true, .label = SDCA_CTL_LATENCY_NAME },
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_it41_cluster_vals, .has_fixed = true,
+ .range = { .cols = 0x2, .rows = 0x1, .data = range_it41_cluster_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_CLUSTERINDEX_NAME },
+ { .sel = 0x11, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_it41_dp_vals, .has_fixed = true,
+ .range = { .cols = 0x10, .rows = 0x4, .data = range_it41_dp_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_DATAPORT_SELECTOR_NAME },
+};
+
+/* Entity 2 (CS 41) Controls */
+static struct sdca_control entity_cs41_controls[] = {
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_cs41_sr_vals, .has_fixed = true,
+ .range = { .cols = 0x2, .rows = 0x1, .data = range_cs41_sr_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_SAMPLERATEINDEX_NAME },
+};
+
+/* Entity 3 (MFPU 21) Controls */
+static struct sdca_entity *entity_mfpu21_sources[] = {
+ &wcd9378_sdca_entities[QSJ_IT41],
+ &wcd9378_sdca_entities[QSJ_IT131],
+};
+
+static struct sdca_control entity_mfpu21_controls[] = {
+ { .sel = SDCA_CTL_MFPU_BYPASS, .mode = SDCA_ACCESS_MODE_DC,
+ .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .has_reset = true, .reset = 1,
+ .values = ctrl_mfpu21_bypass_vals, .has_fixed = true, .label = SDCA_CTL_BYPASS_NAME },
+};
+
+/* Entity 4 (XU 42) Controls */
+static struct sdca_entity *entity_xu42_sources[] = { &wcd9378_sdca_entities[QSJ_MFPU21] };
+
+static struct sdca_control entity_xu42_controls[] = {
+ { .sel = SDCA_CTL_XU_BYPASS, .mode = SDCA_ACCESS_MODE_RW,
+ .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .has_reset = true, .reset = 1,
+ .label = SDCA_CTL_BYPASS_NAME },
+ { .sel = 0x7, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_xu42_id_vals, .has_fixed = true, .label = SDCA_CTL_XU_ID_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_xu42_ver_vals, .has_fixed = true, .label = SDCA_CTL_XU_VERSION_NAME },
+};
+
+/*
+ * SU Selector is RO but its value is deterministic from the current
+ * GE mode which the class framework drives; leave it non-volatile so
+ * regmap caches it and DAPM reads succeed when the codec is
+ * runtime-suspended.
+ */
+
+/* Entity 7 (SU 43) Controls */
+static struct sdca_entity *entity_su43_sources[] = { &wcd9378_sdca_entities[QSJ_XU42] };
+
+static struct sdca_control entity_su43_controls[] = {
+ { .sel = 0x1, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_DEVICE,
+ .cn_list = 0x1,
+ .range = { .cols = 0x1, .rows = 0x2, .data = range_su_sel_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_SELECTOR_NAME },
+};
+
+/* Entity 8 (SU 45) Controls */
+static struct sdca_entity *entity_su45_sources[] = { &wcd9378_sdca_entities[QSJ_XU42] };
+
+static struct sdca_control entity_su45_controls[] = {
+ { .sel = 0x1, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_DEVICE,
+ .cn_list = 0x1,
+ .range = { .cols = 0x1, .rows = 0x2, .data = range_su_sel_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_SELECTOR_NAME },
+};
+
+/*
+ * PDE 47 manages the HPH render path (OT 43, OT 45). FU 6 is listed
+ * so its cached mute/volume are reasserted on PS0 entry.
+ */
+static struct sdca_entity *entity_pde47_managed[] = {
+ &wcd9378_sdca_entities[QSJ_FU6],
+ &wcd9378_sdca_entities[QSJ_OT43],
+ &wcd9378_sdca_entities[QSJ_OT45],
+};
+
+static struct sdca_pde_delay pde47_delays[] = {
+ { .from_ps = 3, .to_ps = 0, .us = 30000 },
+ { .from_ps = 0, .to_ps = 3, .us = 30000 },
+};
+
+static struct sdca_control entity_pde47_controls[] = {
+ { .sel = SDCA_CTL_PDE_REQUESTED_PS, .mode = SDCA_ACCESS_MODE_RW,
+ .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x1, .rows = 0x2, .data = range_pde_req_ps_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .has_reset = true, .reset = 3,
+ .label = SDCA_CTL_REQUESTED_PS_NAME },
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_ACTUAL_PS_NAME },
+ /*
+ * HPH protection IRQs (OCP/CNP/SURGE) fire in codec HW but are
+ * not exposed to Linux; needs an SDCA framework interface for
+ * standalone status-bit IRQs. Wire up in a follow-up.
+ */
+};
+
+/* OT 43 (Headphone), OT 45 (Headset): no controls. */
+static struct sdca_entity *entity_ot43_sources[] = { &wcd9378_sdca_entities[QSJ_IT41] };
+static struct sdca_entity *entity_ot45_sources[] = { &wcd9378_sdca_entities[QSJ_IT41] };
+
+/*
+ * GE 35 mode -> SU selector. Modes 0/1 alias to Headphone (see
+ * range_ge35_mode_data). SU 45 = source 1 (XU 42) for HPH paths,
+ * SU 43 = source 1 (XU 42) for Headset.
+ */
+static struct sdca_ge_control ge35_mode0_controls[] = {
+ { .id = 0x8, .sel = 0x1, .cn = 0x0, .val = 0x1 },
+};
+
+static struct sdca_ge_control ge35_mode1_controls[] = {
+ { .id = 0x8, .sel = 0x1, .cn = 0x0, .val = 0x1 },
+};
+
+static struct sdca_ge_control ge35_mode3_controls[] = {
+ { .id = 0x7, .sel = 0x1, .cn = 0x0, .val = 0x1 },
+};
+
+static struct sdca_ge_control ge35_mode4_controls[] = {
+ { .id = 0x8, .sel = 0x1, .cn = 0x0, .val = 0x1 },
+};
+
+static struct sdca_ge_mode ge35_modes[] = {
+ { .val = 0x0, .num_controls = ARRAY_SIZE(ge35_mode0_controls), .controls = ge35_mode0_controls },
+ { .val = 0x1, .num_controls = ARRAY_SIZE(ge35_mode1_controls), .controls = ge35_mode1_controls },
+ { .val = 0x3, .num_controls = ARRAY_SIZE(ge35_mode3_controls), .controls = ge35_mode3_controls },
+ { .val = 0x4, .num_controls = ARRAY_SIZE(ge35_mode4_controls), .controls = ge35_mode4_controls },
+};
+
+static struct sdca_control entity_ge35_controls[] = {
+ { .sel = SDCA_CTL_GE_SELECTED_MODE, .mode = SDCA_ACCESS_MODE_RW,
+ .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .range = { .cols = 0x2, .rows = 0x4, .data = range_ge35_mode_data },
+ .has_reset = true, .reset = 0,
+ .label = SDCA_CTL_SELECTED_MODE_NAME },
+ { .sel = 0x2, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = 4, /* SDCA_4 = GE_DETECTED_MODE, see init_table INTMASK_1 */
+ .is_volatile = true, .label = SDCA_CTL_DETECTED_MODE_NAME },
+};
+
+static struct sdca_control entity_it131_controls[] = {
+ { .sel = 0x4, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x7, .rows = 0x1, .data = range_it131_usage_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_USAGE_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_it131_latency_vals, .has_fixed = true, .label = SDCA_CTL_LATENCY_NAME },
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_it131_cluster_vals, .has_fixed = true,
+ .range = { .cols = 0x2, .rows = 0x1, .data = range_it131_cluster_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_CLUSTERINDEX_NAME },
+ { .sel = 0x11, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_it131_dp_vals, .has_fixed = true,
+ .range = { .cols = 0x10, .rows = 0x4, .data = range_it131_dp_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_DATAPORT_SELECTOR_NAME },
+};
+
+/* Entity 0xe (CS 131) Controls */
+static struct sdca_control entity_cs131_controls[] = {
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_cs131_sr_vals, .has_fixed = true,
+ .range = { .cols = 0x2, .rows = 0x1, .data = range_cs131_sr_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_SAMPLERATEINDEX_NAME },
+};
+
+/* Entity 0xf (IT 33) - headset mic input */
+static struct sdca_control entity_it33_controls[] = {
+ { .sel = 0x3, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_it33_micbias_vals, .has_default = true,
+ .range = { .cols = 0x1, .rows = 0x1, .data = range_it33_micbias_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_MIC_BIAS_NAME },
+ { .sel = 0x4, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x7, .rows = 0x1, .data = range_it33_usage_data },
+ .has_reset = true,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_USAGE_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_it33_latency_vals, .has_fixed = true, .label = SDCA_CTL_LATENCY_NAME },
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_it33_cluster_vals, .has_fixed = true,
+ .range = { .cols = 0x2, .rows = 0x1, .data = range_it33_cluster_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_CLUSTERINDEX_NAME },
+};
+
+/* Entity 0x10 (PDE 34) - manages IT 33 */
+static struct sdca_entity *entity_pde34_managed[] = { &wcd9378_sdca_entities[QSJ_IT33] };
+
+static struct sdca_pde_delay pde34_delays[] = {
+ { .from_ps = 3, .to_ps = 0, .us = 30000 },
+ { .from_ps = 0, .to_ps = 3, .us = 30000 },
+};
+
+static struct sdca_control entity_pde34_controls[] = {
+ { .sel = SDCA_CTL_PDE_REQUESTED_PS, .mode = SDCA_ACCESS_MODE_RW,
+ .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x1, .rows = 0x2, .data = range_pde_req_ps_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .has_reset = true, .reset = 3,
+ .label = SDCA_CTL_REQUESTED_PS_NAME },
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_RO, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .is_volatile = true, .label = SDCA_CTL_ACTUAL_PS_NAME },
+};
+
+/*
+ * FU 6 (vendor FU42): HPH mute + Q7.8 volume. DUAL-mode CVR-alias
+ * writes take effect without an SCP_COMMIT.
+ */
+/*
+ * FU 6 volume range: MIN, MAX, STEP in Q7.8 (LSB = 1/256 dB).
+ * 0x8000 = -128 dB, 0x7fff = +127.996 dB, STEP = 1. Framework
+ * sign-extends and converts to 0.01 dB TLV via (val * 100) >> 8.
+ */
+static u32 range_fu6_vol_data[] = {
+ 0x00008000, 0x00007fff, 0x00000001,
+};
+
+static struct sdca_control entity_fu6_controls[] = {
+ { .sel = SDCA_CTL_FU_MUTE, .mode = SDCA_ACCESS_MODE_DUAL,
+ .layers = SDCA_ACCESS_LAYER_USER, .cn_list = 0x6, .nbits = 1,
+ .type = SDCA_CTL_DATATYPE_ONEBIT,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .has_reset = true, .reset = 1,
+ .label = SDCA_CTL_MUTE_NAME },
+ { .sel = SDCA_CTL_FU_CHANNEL_VOLUME, .mode = SDCA_ACCESS_MODE_DUAL,
+ .layers = SDCA_ACCESS_LAYER_USER, .cn_list = 0x6, .nbits = 16,
+ .type = SDCA_CTL_DATATYPE_Q7P8DB,
+ .range = { .cols = SDCA_VOLUME_LINEAR_NCOLS, .rows = 1,
+ .data = range_fu6_vol_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_CHANNEL_VOLUME_NAME },
+};
+
+/* Entity 0x11 (FU 33) - no controls */
+static struct sdca_entity *entity_fu33_sources[] = { &wcd9378_sdca_entities[QSJ_IT33] };
+
+/* Entity 0x12 (SU 35) Controls */
+static struct sdca_entity *entity_su35_sources[] = { &wcd9378_sdca_entities[QSJ_FU33] };
+
+/*
+ * SU 35 is class-layer: the SDCA class framework drives the Selector
+ * directly via a DAPM mux widget, so the Control must be writeable.
+ */
+static struct sdca_control entity_su35_controls[] = {
+ { .sel = 0x1, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x1, .rows = 0x2, .data = range_su_sel_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_SELECTOR_NAME },
+};
+
+/* Entity 0x13 (XU 36) Controls */
+static struct sdca_entity *entity_xu36_sources[] = { &wcd9378_sdca_entities[QSJ_SU35] };
+
+static struct sdca_control entity_xu36_controls[] = {
+ /* bypass=1: pass mic signal through XU36 without proprietary processing */
+ { .sel = SDCA_CTL_XU_BYPASS, .mode = SDCA_ACCESS_MODE_RW,
+ .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .has_reset = true, .reset = 1,
+ .values = ctrl_xu36_bypass_vals, .has_default = true, .label = SDCA_CTL_BYPASS_NAME },
+ { .sel = 0x7, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_xu36_id_vals, .has_fixed = true, .label = SDCA_CTL_XU_ID_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_xu36_ver_vals, .has_fixed = true, .label = SDCA_CTL_XU_VERSION_NAME },
+};
+
+/* Entity 0x15 (CS 36) Controls */
+static struct sdca_control entity_cs36_controls[] = {
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_cs36_sr_vals, .has_fixed = true,
+ .range = { .cols = 0x2, .rows = 0x1, .data = range_cs36_sr_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_SAMPLERATEINDEX_NAME },
+};
+
+/* OT 36 mic capture: IT33 -> FU33 -> SU35 -> XU36 -> OT36. */
+static struct sdca_entity *entity_ot36_sources[] = { &wcd9378_sdca_entities[QSJ_XU36] };
+
+static struct sdca_control entity_ot36_controls[] = {
+ { .sel = 0x4, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x7, .rows = 0x1, .data = range_ot36_usage_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_USAGE_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_ot36_latency_vals, .has_fixed = true, .label = SDCA_CTL_LATENCY_NAME },
+ { .sel = 0x11, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_ot36_dp_vals, .has_fixed = true,
+ .range = { .cols = 0x10, .rows = 0x4, .data = range_ot36_dp_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_DATAPORT_SELECTOR_NAME },
+};
+
+/* Entity 0x17 (MFPU 236) Controls */
+static struct sdca_control entity_mfpu236_controls[] = {
+ { .sel = SDCA_CTL_MFPU_BYPASS, .mode = SDCA_ACCESS_MODE_DC,
+ .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .has_reset = true, .reset = 1,
+ .values = ctrl_mfpu236_bypass_vals, .has_fixed = true, .label = SDCA_CTL_BYPASS_NAME },
+};
+
+/* Entity 0x18 (CS 236) Controls */
+static struct sdca_control entity_cs236_controls[] = {
+ { .sel = 0x10, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_cs236_sr_vals, .has_fixed = true,
+ .range = { .cols = 0x2, .rows = 0x1, .data = range_cs236_sr_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_SAMPLERATEINDEX_NAME },
+};
+
+/* Entity 0x19 (OT 236) - optimization stream capture output */
+static struct sdca_entity *entity_ot236_sources[] = { &wcd9378_sdca_entities[QSJ_IT33] };
+
+static struct sdca_control entity_ot236_controls[] = {
+ { .sel = 0x4, .mode = SDCA_ACCESS_MODE_RW, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .range = { .cols = 0x7, .rows = 0x1, .data = range_ot236_usage_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_USAGE_NAME },
+ { .sel = 0x8, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .values = ctrl_ot236_latency_vals, .has_fixed = true, .label = SDCA_CTL_LATENCY_NAME },
+ { .sel = 0x11, .mode = SDCA_ACCESS_MODE_DC, .layers = SDCA_ACCESS_LAYER_CLASS, .cn_list = 0x1,
+ .values = ctrl_ot236_dp_vals, .has_fixed = true,
+ .range = { .cols = 0x10, .rows = 0x4, .data = range_ot236_dp_data },
+ .interrupt_position = SDCA_NO_INTERRUPT,
+ .label = SDCA_CTL_DATAPORT_SELECTOR_NAME },
+};
+
+static struct sdca_entity wcd9378_sdca_entities[] = {
+ /* [0] E001: IT 41 - PDM render stream input */
+ { .id = 0x1, .label = "IT 41", .type = SDCA_ENTITY_TYPE_IT,
+ .iot = { .type = 0x0191, .is_dataport = true, .clock = &wcd9378_sdca_entities[QSJ_CS41] },
+ .num_controls = ARRAY_SIZE(entity_it41_controls), .controls = entity_it41_controls },
+ /* [1] E002: CS 41 */
+ { .id = 0x2, .label = "CS 41", .type = SDCA_ENTITY_TYPE_CS,
+ .cs = { .type = 0x0 },
+ .num_controls = ARRAY_SIZE(entity_cs41_controls), .controls = entity_cs41_controls },
+ /* [2] E003: MFPU 21 */
+ { .id = 0x3, .label = "MFPU 21", .type = SDCA_ENTITY_TYPE_MFPU,
+ .num_controls = ARRAY_SIZE(entity_mfpu21_controls), .controls = entity_mfpu21_controls,
+ .num_sources = ARRAY_SIZE(entity_mfpu21_sources), .sources = entity_mfpu21_sources },
+ /* [3] E004: XU 42 */
+ { .id = 0x4, .label = "XU 42", .type = SDCA_ENTITY_TYPE_XU,
+ .num_controls = ARRAY_SIZE(entity_xu42_controls), .controls = entity_xu42_controls,
+ .num_sources = ARRAY_SIZE(entity_xu42_sources), .sources = entity_xu42_sources },
+ /* [4] E007: SU 43 - render selector (headset path), driven by GE 35 jack detection */
+ { .id = 0x7, .label = "SU 43", .type = SDCA_ENTITY_TYPE_SU,
+ .group = &wcd9378_sdca_entities[QSJ_GE35],
+ .num_controls = ARRAY_SIZE(entity_su43_controls), .controls = entity_su43_controls,
+ .num_sources = ARRAY_SIZE(entity_su43_sources), .sources = entity_su43_sources },
+ /* [5] E008: SU 45 - render selector (headphone path), driven by GE 35 jack detection */
+ { .id = 0x8, .label = "SU 45", .type = SDCA_ENTITY_TYPE_SU,
+ .group = &wcd9378_sdca_entities[QSJ_GE35],
+ .num_controls = ARRAY_SIZE(entity_su45_controls), .controls = entity_su45_controls,
+ .num_sources = ARRAY_SIZE(entity_su45_sources), .sources = entity_su45_sources },
+ /* [6] E009: PDE 47 - render power domain */
+ { .id = 0x9, .label = "PDE 47", .type = SDCA_ENTITY_TYPE_PDE,
+ .pde = { .num_managed = ARRAY_SIZE(entity_pde47_managed), .managed = entity_pde47_managed,
+ .num_max_delay = ARRAY_SIZE(pde47_delays), .max_delay = pde47_delays },
+ .num_controls = ARRAY_SIZE(entity_pde47_controls), .controls = entity_pde47_controls },
+ /* [7] E00A: OT 43 - Headphone on jack */
+ { .id = 0xa, .label = "OT 43", .type = SDCA_ENTITY_TYPE_OT,
+ .iot = { .type = 0x06c0 },
+ .num_sources = ARRAY_SIZE(entity_ot43_sources), .sources = entity_ot43_sources },
+ /* [8] E00B: OT 45 - Headset output on jack */
+ { .id = 0xb, .label = "OT 45", .type = SDCA_ENTITY_TYPE_OT,
+ .iot = { .type = 0x06d0 },
+ .num_sources = ARRAY_SIZE(entity_ot45_sources), .sources = entity_ot45_sources },
+ /* [9] E00C: GE 35 - jack detection group entity */
+ { .id = 0xc, .label = "GE 35", .type = SDCA_ENTITY_TYPE_GE,
+ .ge = { .num_modes = ARRAY_SIZE(ge35_modes), .modes = ge35_modes },
+ .num_controls = ARRAY_SIZE(entity_ge35_controls), .controls = entity_ge35_controls },
+ /* [10] E00D: IT 131 - optimization stream input */
+ { .id = 0xd, .label = "IT 131", .type = SDCA_ENTITY_TYPE_IT,
+ .iot = { .type = 0x0190, .is_dataport = true, .clock = &wcd9378_sdca_entities[QSJ_CS131] },
+ .num_controls = ARRAY_SIZE(entity_it131_controls), .controls = entity_it131_controls },
+ /* [11] E00E: CS 131 */
+ { .id = 0xe, .label = "CS 131", .type = SDCA_ENTITY_TYPE_CS,
+ .cs = { .type = 0x0 },
+ .num_controls = ARRAY_SIZE(entity_cs131_controls), .controls = entity_cs131_controls },
+ /* [12] E00F: IT 33 - headset mic input */
+ { .id = 0xf, .label = "IT 33", .type = SDCA_ENTITY_TYPE_IT,
+ .iot = { .type = 0x06d0 },
+ .num_controls = ARRAY_SIZE(entity_it33_controls), .controls = entity_it33_controls },
+ /* [13] E010: PDE 34 - mic power domain */
+ { .id = 0x10, .label = "PDE 34", .type = SDCA_ENTITY_TYPE_PDE,
+ .pde = { .num_managed = ARRAY_SIZE(entity_pde34_managed), .managed = entity_pde34_managed,
+ .num_max_delay = ARRAY_SIZE(pde34_delays), .max_delay = pde34_delays },
+ .num_controls = ARRAY_SIZE(entity_pde34_controls), .controls = entity_pde34_controls },
+ /* [14] E011: FU 33 - mic feature unit */
+ { .id = 0x11, .label = "FU 33", .type = SDCA_ENTITY_TYPE_FU,
+ .num_sources = ARRAY_SIZE(entity_fu33_sources), .sources = entity_fu33_sources },
+ /* [15] E012: SU 35 - mic selector */
+ { .id = 0x12, .label = "SU 35", .type = SDCA_ENTITY_TYPE_SU,
+ .num_controls = ARRAY_SIZE(entity_su35_controls), .controls = entity_su35_controls,
+ .num_sources = ARRAY_SIZE(entity_su35_sources), .sources = entity_su35_sources },
+ /* [16] E013: XU 36 - mic extension unit */
+ { .id = 0x13, .label = "XU 36", .type = SDCA_ENTITY_TYPE_XU,
+ .num_controls = ARRAY_SIZE(entity_xu36_controls), .controls = entity_xu36_controls,
+ .num_sources = ARRAY_SIZE(entity_xu36_sources), .sources = entity_xu36_sources },
+ /* [17] E015: CS 36 */
+ { .id = 0x15, .label = "CS 36", .type = SDCA_ENTITY_TYPE_CS,
+ .cs = { .type = 0x0 },
+ .num_controls = ARRAY_SIZE(entity_cs36_controls), .controls = entity_cs36_controls },
+ /* [18] E016: OT 36 - PDM mic capture output */
+ { .id = 0x16, .label = "OT 36", .type = SDCA_ENTITY_TYPE_OT,
+ .iot = { .type = 0x0191, .is_dataport = true, .clock = &wcd9378_sdca_entities[QSJ_CS36] },
+ .num_controls = ARRAY_SIZE(entity_ot36_controls), .controls = entity_ot36_controls,
+ .num_sources = ARRAY_SIZE(entity_ot36_sources), .sources = entity_ot36_sources },
+ /* [19] E017: MFPU 236 - optimization TX processing */
+ { .id = 0x17, .label = "MFPU 236", .type = SDCA_ENTITY_TYPE_MFPU,
+ .num_controls = ARRAY_SIZE(entity_mfpu236_controls), .controls = entity_mfpu236_controls },
+ /* [20] E018: CS 236 */
+ { .id = 0x18, .label = "CS 236", .type = SDCA_ENTITY_TYPE_CS,
+ .cs = { .type = 0x0 },
+ .num_controls = ARRAY_SIZE(entity_cs236_controls), .controls = entity_cs236_controls },
+ /* [21] E019: OT 236 - optimization stream capture output */
+ { .id = 0x19, .label = "OT 236", .type = SDCA_ENTITY_TYPE_OT,
+ .iot = { .type = 0x0190, .is_dataport = true, .clock = &wcd9378_sdca_entities[QSJ_CS131] },
+ .num_controls = ARRAY_SIZE(entity_ot236_controls), .controls = entity_ot236_controls,
+ .num_sources = ARRAY_SIZE(entity_ot236_sources), .sources = entity_ot236_sources },
+ /* E006: FU 6 (FU42) - vendor Feature Unit; HPH mute + Q7.8 volume. */
+ { .id = 0x6, .label = "FU 6", .type = SDCA_ENTITY_TYPE_FU,
+ .num_controls = ARRAY_SIZE(entity_fu6_controls), .controls = entity_fu6_controls },
+ /* Entity 0 (Function) */
+ { .id = 0x0, .label = "entity0",
+ .num_controls = ARRAY_SIZE(entity0_controls), .controls = entity0_controls },
+};
+
+/* Clusters */
+static struct sdca_channel cl1_channels[] = { /* CL01 - render (HPH) stereo */
+ { .id = 0x1, .purpose = 0x1, .relationship = 0x2 }, /* Left */
+ { .id = 0x2, .purpose = 0x1, .relationship = 0x3 }, /* Right */
+};
+
+static struct sdca_channel cl2_channels[] = { /* CL02 - mic capture mono */
+ { .id = 0xff, .purpose = 0x1, .relationship = 0x1 },
+};
+
+static struct sdca_channel cl3_channels[] = { /* CL03 - optimization RX */
+ { .id = 0xff, .purpose = 0x1, .relationship = 0x1 }, /* Mono */
+ { .id = 0x1, .purpose = 0x1, .relationship = 0x2 }, /* Left */
+ { .id = 0x2, .purpose = 0x1, .relationship = 0x3 }, /* Right */
+};
+
+static struct sdca_channel cl5_channels[] = { /* CL05 - optimization TX mono */
+ { .id = 0xff, .purpose = 0x1, .relationship = 0x1 },
+};
+
+static struct sdca_cluster wcd9378_sdca_clusters[] = {
+ { .id = 0x1, .num_channels = ARRAY_SIZE(cl1_channels), .channels = cl1_channels },
+ { .id = 0x2, .num_channels = ARRAY_SIZE(cl2_channels), .channels = cl2_channels },
+ { .id = 0x3, .num_channels = ARRAY_SIZE(cl3_channels), .channels = cl3_channels },
+ { .id = 0x5, .num_channels = ARRAY_SIZE(cl5_channels), .channels = cl5_channels },
+};
+
+/* Init table transcribed from ASL. */
+static struct sdca_init_write wcd9378_sdca_init_table[] = {
+ { .addr = 0x401804f0, .val = 0x00 }, /* DIGITAL_PLATFORM_CTL */
+ { .addr = 0x4018046e, .val = 0x10 }, /* DIGITAL_INTR_MODE */
+ { .addr = 0x0000004d, .val = 0x01 }, /* SWRS_SCP_BUSCLOCK_BASE */
+ { .addr = 0x00000062, .val = 0x02 }, /* SWRS_SCP_BUSCLOCK_SCALE_BANK */
+ { .addr = 0x4018016a, .val = 0x80 }, /* CP_DTOP_CTRL_14 */
+ { .addr = 0x40180165, .val = 0x6b }, /* CP_DTOP_CTRL_9 */
+ { .addr = 0x40180103, .val = 0x1e }, /* SLEEP_CTL BG_CTL (0.9V) */
+ { .addr = 0x40180103, .val = 0x9e }, /* SLEEP_CTL BG_EN */
+ { .addr = 0x40180103, .val = 0xde }, /* SLEEP_CTL LDOL_BG_SEL */
+ { .addr = 0x40180029, .val = 0xb5 }, /* BIAS_VBG_FINE_ADJ */
+ { .addr = 0x40180001, .val = 0x80 }, /* ANA_BIAS ANALOG_BIAS_EN */
+ { .addr = 0x40180001, .val = 0xc0 }, /* ANA_BIAS PRECHRG_EN(1) */
+ { .addr = 0x40180001, .val = 0x80 }, /* ANA_BIAS PRECHRG_EN(0) */
+ { .addr = 0x4018007b, .val = 0xa2 }, /* TX_COM_TXFE_DIV_CTL SEQ_BYPASS */
+ { .addr = 0x40180465, .val = 0x17 }, /* PDM_WD_CTL0 TIME_OUT_SEL_PCM */
+ { .addr = 0x40180466, .val = 0x17 }, /* PDM_WD_CTL1 TIME_OUT_SEL_PCM */
+ { .addr = 0x4018006c, .val = 0x01 }, /* MICB1_TEST_CTL_2 IBIAS_LDO_DRIVER */
+ { .addr = 0x40180072, .val = 0x81 }, /* MICB3_TEST_CTL_2 IBIAS_LDO_DRIVER */
+ { .addr = 0x401800ce, .val = 0x38 }, /* HPH_OCP_CTL OCP_FSM_EN */
+ { .addr = 0x401800ce, .val = 0x3a }, /* HPH_OCP_CTL SCD_OP_EN */
+ { .addr = 0x401800d4, .val = 0xe1 }, /* HPH_L_TEST OCP_DET_EN */
+ { .addr = 0x401800d7, .val = 0xe1 }, /* HPH_R_TEST OCP_DET_EN */
+ { .addr = 0x4018044e, .val = 0x04 }, /* CDC_HPH_GAIN_CTL HPHL_RX_EN */
+ { .addr = 0x4018044e, .val = 0x0c }, /* CDC_HPH_GAIN_CTL HPHR_RX_EN */
+ { .addr = 0x4018000f, .val = 0x0c }, /* ANA_TX_CH2 GAIN (18.0dB) */
+ { .addr = 0x40180133, .val = 0x84 }, /* HPH_NEW_INT_RDAC_HD2_CTL_L */
+ { .addr = 0x40180136, .val = 0x84 }, /* HPH_NEW_INT_RDAC_HD2_CTL_R */
+ { .addr = 0x401800d9, .val = 0x19 }, /* HPH_RDAC_CLK_CTL1 OPAMP_CHOP_CLK_EN */
+ { .addr = 0x40180132, .val = 0x50 }, /* HPH_NEW_INT_RDAC_GAIN_CTL RDAC_GAINCTL(0.55) */
+ { .addr = 0x40180510, .val = 0x05 }, /* SEQR_CTRL HPH_UP_T0 */
+ { .addr = 0x40180519, .val = 0x05 }, /* SEQR_CTRL HPH_UP_T9 */
+ { .addr = 0x4018051b, .val = 0x06 }, /* SEQR_CTRL HPH_DN_T0 */
+ { .addr = 0x40180414, .val = 0x02 }, /* CDC_COMP_CTL_0 HPHL_COMP_EN */
+ { .addr = 0x40180414, .val = 0x03 }, /* CDC_COMP_CTL_0 HPHR_COMP_EN */
+ { .addr = 0x401804f2, .val = 0x80 }, /* DRE_DLY_VAL SWR_HPHL(0) */
+ { .addr = 0x401804f2, .val = 0x00 }, /* DRE_DLY_VAL SWR_HPHR(0) */
+ { .addr = 0x40180501, .val = 0x01 }, /* SEQR_CTRL SYS_USAGE_CTRL */
+ /* Arms MBHC: jack insertion asserts SDCA_4 (GE_DETECTED_MODE). */
+ { .addr = 0x40180601, .val = 0x01 }, /* MBHC_CTRL DEVICE_DET */
+ { .addr = 0x40180414, .val = 0x00 }, /* CDC_COMP_CTL_0 */
+ { .addr = 0x401804f2, .val = 0x88 }, /* DRE_DLY_VAL */
+ { .addr = 0x40180517, .val = 0x07 }, /* SEQR_CTRL HPH_UP_T7 */
+ { .addr = 0x4018051c, .val = 0x07 }, /* SEQR_CTRL HPH_DN_T1 */
+ { .addr = 0x401800ce, .val = 0x28 }, /* HPH_OCP_CTL */
+ { .addr = 0x401800d4, .val = 0xe0 }, /* HPH_L_TEST */
+ { .addr = 0x401800d7, .val = 0xe0 }, /* HPH_R_TEST */
+ { .addr = 0x40180510, .val = 0x07 }, /* SEQR_CTRL HPH_UP_T0 */
+ { .addr = 0x40c80008, .val = 0x01 }, /* SMP_JACK_CTRL FUNC_ACT (RESET_FUNCTION_NOW) */
+ { .addr = 0x40c00008, .val = 0x02 }, /* SMP_JACK_CTRL CMT_GRP_MASK */
+ { .addr = 0x40c80000, .val = 0xff }, /* SMP_JACK_CTRL FUNC_STAT */
+ { .addr = 0x00000000, .val = 0x08 }, /* clear DP0 INT status SDCA_CASCADE */
+ { .addr = 0x00000041, .val = 0x08 }, /* SCP_INT_STATUS_MASK_1 PORT_0_CASCADE_3 */
+ /* Only SDCA_4 unmasked; protection IRQs stay masked. */
+ { .addr = 0x0000005c, .val = 0x10 }, /* INTMASK_1 SDCA_4 (GE_DETECTED_MODE) */
+ { .addr = 0x4018016a, .val = 0x00 }, /* CP_DTOP_CTRL_14 */
+ { .addr = 0x40180165, .val = 0x6b }, /* CP_DTOP_CTRL_9 */
+ /*
+ * L_DET_EN is left disabled: asserting SDCA_4 before the machine
+ * card registers the jack blocks the deferred card probe.
+ */
+};
+
+/* Function Descriptor */
+static struct sdca_function_desc wcd9378_sdca_desc = {
+ .adr = 0x3,
+ .type = SDCA_FUNCTION_TYPE_SIMPLE_JACK,
+ .name = SDCA_FUNCTION_TYPE_SIMPLE_NAME,
+};
+
+/* Main Function Data */
+static struct sdca_function_data wcd9378_sdca_data = {
+ .desc = &wcd9378_sdca_desc,
+ .num_entities = ARRAY_SIZE(wcd9378_sdca_entities),
+ .entities = wcd9378_sdca_entities,
+ .num_clusters = ARRAY_SIZE(wcd9378_sdca_clusters),
+ .clusters = wcd9378_sdca_clusters,
+ .num_init_table = ARRAY_SIZE(wcd9378_sdca_init_table),
+ .init_table = wcd9378_sdca_init_table,
+ .reset_max_delay = 25000, /* 25ms — WCD9378 power-on reset completes well within this */
+};
+
+/* Vendor SCP register: host clock divide-by-2 (bank 1 shadow). */
+#define WCD9378_SCP_HOST_CLK_DIV2_CTL_B1 0xf0
+
+/* Slave ports 1..8, mapped to master ports via qcom,port-mapping. */
+#define WCD9378_SDCA_MAX_PORTS 8
+
+static const char * const wcd9378_sdca_supplies[] = {
+ "vdd-buck", "vdd-rxtx", "vdd-io", "vdd-mic-bias",
+};
+
+int wcd9378_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ struct device *dev = &slave->dev;
+ struct sdw_dpn_prop *sink, *src;
+ int ret;
+
+ /*
+ * All SoundWire slave properties for compute-mode WCD9378 are
+ * fixed by the silicon and are not described in DT, so we do
+ * not call sdw_slave_read_prop() here.
+ */
+ prop->use_domain_irq = true;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY |
+ SDW_SCP_INT1_IMPL_DEF;
+ prop->simple_clk_stop_capable = true;
+ prop->paging_support = true;
+ prop->clock_reg_supported = true;
+ prop->lane_control_support = true;
+
+ /* Source ports: DP2 (headset mic), DP5 (optimisation TX). */
+ prop->source_ports = BIT(2) | BIT(5);
+ /* Sink ports: DP6 (HPH audio), DP7 (HPH envelope), DP8 (optimisation RX). */
+ prop->sink_ports = BIT(6) | BIT(7) | BIT(8);
+
+ src = devm_kcalloc(dev, 2, sizeof(*src), GFP_KERNEL);
+ if (!src)
+ return -ENOMEM;
+
+ src[0].num = 2;
+ src[0].type = SDW_DPN_SIMPLE;
+ src[0].simple_ch_prep_sm = true;
+ src[0].ch_prep_timeout = 10;
+ src[0].max_ch = 1;
+ src[0].min_ch = 1;
+
+ src[1].num = 5;
+ src[1].type = SDW_DPN_SIMPLE;
+ src[1].simple_ch_prep_sm = true;
+ src[1].ch_prep_timeout = 10;
+ src[1].max_ch = 1;
+ src[1].min_ch = 1;
+
+ prop->src_dpn_prop = src;
+
+ sink = devm_kcalloc(dev, 3, sizeof(*sink), GFP_KERNEL);
+ if (!sink)
+ return -ENOMEM;
+
+ sink[0].num = 6;
+ sink[0].type = SDW_DPN_SIMPLE;
+ sink[0].simple_ch_prep_sm = true;
+ sink[0].ch_prep_timeout = 10;
+ sink[0].max_ch = 2;
+ sink[0].min_ch = 1;
+
+ sink[1].num = 7;
+ sink[1].type = SDW_DPN_FULL;
+ sink[1].simple_ch_prep_sm = true;
+ sink[1].ch_prep_timeout = 10;
+ sink[1].max_ch = 1;
+ sink[1].min_ch = 1;
+
+ sink[2].num = 8;
+ sink[2].type = SDW_DPN_REDUCED;
+ sink[2].simple_ch_prep_sm = true;
+ sink[2].ch_prep_timeout = 10;
+ sink[2].max_ch = 2;
+ sink[2].min_ch = 1;
+
+ prop->sink_dpn_prop = sink;
+
+ ret = device_property_read_u32_array(dev, "qcom,port-mapping",
+ &slave->m_port_map[1],
+ WCD9378_SDCA_MAX_PORTS);
+ if (ret)
+ return dev_err_probe(dev, ret, "qcom,port-mapping missing\n");
+
+ return 0;
+}
+
+static int wcd9378_sdca_populate_function(struct device *dev,
+ struct sdca_function_data *function)
+{
+ /* @function->desc is already set by the framework; fill payload only. */
+ if (function->desc->type != wcd9378_sdca_desc.type)
+ return -EINVAL;
+
+ function->num_entities = wcd9378_sdca_data.num_entities;
+ function->entities = wcd9378_sdca_data.entities;
+ function->num_clusters = wcd9378_sdca_data.num_clusters;
+ function->clusters = wcd9378_sdca_data.clusters;
+ function->num_init_table = wcd9378_sdca_data.num_init_table;
+ function->init_table = wcd9378_sdca_data.init_table;
+ function->reset_max_delay = wcd9378_sdca_data.reset_max_delay;
+
+ return 0;
+}
+
+static const struct sdca_class_ops wcd9378_sdca_class_ops = {
+ .populate_function = wcd9378_sdca_populate_function,
+};
+
+int wcd9378_sdca_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct device *dev = &slave->dev;
+ struct sdca_device_data *data = &slave->sdca_data;
+ struct wcd9378_priv *priv;
+ struct gpio_desc *reset;
+ int ret;
+
+ /*
+ * 0x0217:0x0110 covers both mobile (non-SDCA) and SDCA-compliant
+ * fuse variants. Only SDCA-compliant nodes bind to this driver;
+ * mobile-mode nodes are handled by a different driver.
+ */
+ if (!device_property_read_bool(dev, "qcom,sdca-compliant"))
+ return -ENODEV;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, priv);
+
+ /* No SPMI parent: supplies and reset live on the SoundWire DT node. */
+ ret = devm_regulator_bulk_get_enable(dev,
+ ARRAY_SIZE(wcd9378_sdca_supplies),
+ wcd9378_sdca_supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable supplies\n");
+
+ reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(reset))
+ return dev_err_probe(dev, PTR_ERR(reset),
+ "failed to get reset GPIO\n");
+
+ if (reset) {
+ gpiod_set_value(reset, 1);
+ usleep_range(20, 30);
+ gpiod_set_value(reset, 0);
+ usleep_range(20, 30);
+ }
+
+ /* SCP writes below need the slave attached. */
+ ret = sdw_slave_wait_for_init(slave, 5000);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "slave attach timeout: %d\n", ret);
+
+ /*
+ * TX PDM clock: bank-1 shadow + SCP_COMMIT. SCP survives PDE
+ * cycles; one-shot before any port is enabled.
+ */
+ ret = sdw_write_no_pm(slave, WCD9378_SCP_HOST_CLK_DIV2_CTL_B1, 0x01);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "HOST_CLK_DIV2_CTL_B1: %d\n", ret);
+
+ ret = sdw_write_no_pm(slave, SDW_SCP_COMMIT, 0x02);
+ if (ret)
+ return dev_err_probe(dev, ret, "SCP_COMMIT: %d\n", ret);
+
+ /* DT has no DisCo enumeration; seed the descriptor here. */
+ if (!data->num_functions) {
+ data->function[0].type = wcd9378_sdca_desc.type;
+ data->function[0].adr = wcd9378_sdca_desc.adr;
+ data->function[0].name = wcd9378_sdca_desc.name;
+ data->num_functions = 1;
+ }
+
+ return sdca_class_probe(slave, &priv->class, &wcd9378_sdca_class_ops);
+}
+
+void wcd9378_sdca_remove(struct sdw_slave *slave)
+{
+ struct wcd9378_priv *priv = dev_get_drvdata(&slave->dev);
+
+ sdca_class_remove(&priv->class);
+}
+
+int wcd9378_sdca_runtime_suspend(struct device *dev)
+{
+ struct wcd9378_priv *priv = dev_get_drvdata(dev);
+
+ return sdca_class_runtime_suspend(&priv->class);
+}
+
+int wcd9378_sdca_runtime_resume(struct device *dev)
+{
+ struct wcd9378_priv *priv = dev_get_drvdata(dev);
+
+ return sdca_class_runtime_resume(&priv->class);
+}
+
+int wcd9378_sdca_system_suspend(struct device *dev)
+{
+ struct wcd9378_priv *priv = dev_get_drvdata(dev);
+
+ return sdca_class_system_suspend(&priv->class);
+}
+
+int wcd9378_sdca_system_resume(struct device *dev)
+{
+ struct wcd9378_priv *priv = dev_get_drvdata(dev);
+
+ return sdca_class_system_resume(&priv->class);
+}
+
+/* SoundWire slave-driver plumbing lives in wcd9378-sdw.c. */
diff --git a/sound/soc/codecs/wcd9378-sdca.h b/sound/soc/codecs/wcd9378-sdca.h
new file mode 100644
index 000000000000..6ddae9da395b
--- /dev/null
+++ b/sound/soc/codecs/wcd9378-sdca.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
+/* Copyright (c) 2025 Qualcomm Technologies, Inc. */
+
+#ifndef _WCD9378_SDCA_H
+#define _WCD9378_SDCA_H
+
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+
+int wcd9378_sdca_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id);
+void wcd9378_sdca_remove(struct sdw_slave *slave);
+int wcd9378_sdca_read_prop(struct sdw_slave *slave);
+
+int wcd9378_sdca_runtime_suspend(struct device *dev);
+int wcd9378_sdca_runtime_resume(struct device *dev);
+int wcd9378_sdca_system_suspend(struct device *dev);
+int wcd9378_sdca_system_resume(struct device *dev);
+
+#endif /* _WCD9378_SDCA_H */
diff --git a/sound/soc/codecs/wcd9378-sdw.c b/sound/soc/codecs/wcd9378-sdw.c
new file mode 100644
index 000000000000..52295e7646cf
--- /dev/null
+++ b/sound/soc/codecs/wcd9378-sdw.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+// Copyright (c) 2025 Qualcomm Technologies, Inc.
+
+/*
+ * WCD9378 (Tambora) SoundWire driver glue for the SDCA compute-mode
+ * codec. The SDCA topology and hardware-specific probe logic live in
+ * wcd9378-sdca.c; this file carries the SoundWire slave driver plumbing
+ * and the module boilerplate.
+ */
+
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include "wcd9378-sdca.h"
+
+static const struct dev_pm_ops wcd9378_sdw_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(wcd9378_sdca_system_suspend,
+ wcd9378_sdca_system_resume)
+ RUNTIME_PM_OPS(wcd9378_sdca_runtime_suspend,
+ wcd9378_sdca_runtime_resume, NULL)
+};
+
+static const struct sdw_slave_ops wcd9378_sdw_ops = {
+ .read_prop = wcd9378_sdca_read_prop,
+};
+
+static const struct sdw_device_id wcd9378_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x0217, 0x0110, 0),
+ { }
+};
+MODULE_DEVICE_TABLE(sdw, wcd9378_sdw_id);
+
+static struct sdw_driver wcd9378_sdw_driver = {
+ .driver = {
+ .name = "wcd9378",
+ .pm = pm_ptr(&wcd9378_sdw_pm_ops),
+ },
+ .probe = wcd9378_sdca_probe,
+ .remove = wcd9378_sdca_remove,
+ .id_table = wcd9378_sdw_id,
+ .ops = &wcd9378_sdw_ops,
+};
+module_sdw_driver(wcd9378_sdw_driver);
+
+MODULE_DESCRIPTION("Qualcomm WCD9378 (Tambora) SoundWire codec");
+MODULE_AUTHOR("Qualcomm Technologies, Inc.");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_IMPORT_NS("SND_SOC_SDCA");
+MODULE_IMPORT_NS("SND_SOC_SDCA_CLASS");
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index bc78b553b6cb..692e933dba75 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2066,10 +2066,20 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 wm2200_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm2200_dai_ops = {
.probe = wm2200_dai_probe,
.set_fmt = wm2200_set_fmt,
.hw_params = wm2200_hw_params,
+ .auto_selectable_formats = &wm2200_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define WM2200_RATES SNDRV_PCM_RATE_8000_48000
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 2fe937a93026..f7ecbac386e5 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -1501,9 +1501,19 @@ static int wm5100_hw_params(struct snd_pcm_substream *substream,
return 0;
}
+static const u64 wm5100_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm5100_dai_ops = {
.set_fmt = wm5100_set_fmt,
.hw_params = wm5100_hw_params,
+ .auto_selectable_formats = &wm5100_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int wm5100_set_sysclk(struct snd_soc_component *component, int clk_id,
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index ad68ff1790e0..0e7c8a5fae72 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1426,6 +1426,28 @@ EXPORT_SYMBOL_GPL(wm8350_mic_jack_detect);
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8350_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /*
+ * 2nd priority
+ *
+ * iface of DSP_A/B are same, the diff is only LRCLK_INV.
+ * It will be impossible to distinguish between DSP_A/B
+ * if xB_IF are used. Allow xB_NF only.
+ */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8350_dai_ops = {
.hw_params = wm8350_pcm_hw_params,
.mute_stream = wm8350_mute,
@@ -1434,6 +1456,8 @@ static const struct snd_soc_dai_ops wm8350_dai_ops = {
.set_pll = wm8350_set_fll,
.set_clkdiv = wm8350_set_clkdiv,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8350_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8350_selectable_formats),
};
static struct snd_soc_dai_driver wm8350_dai = {
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index 0eecc8657a38..83e2d72a4a76 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1222,6 +1222,13 @@ static int wm8400_set_bias_level(struct snd_soc_component *component,
#define WM8400_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8400_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops wm8400_dai_ops = {
.hw_params = wm8400_hw_params,
.mute_stream = wm8400_mute,
@@ -1230,6 +1237,8 @@ static const struct snd_soc_dai_ops wm8400_dai_ops = {
.set_sysclk = wm8400_set_dai_sysclk,
.set_pll = wm8400_set_dai_pll,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8400_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
/*
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 137dcd3d7487..7d40ed21f569 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -545,6 +545,16 @@ static int wm8510_set_bias_level(struct snd_soc_component *component,
#define WM8510_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8510_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8510_dai_ops = {
.hw_params = wm8510_pcm_hw_params,
.mute_stream = wm8510_mute,
@@ -552,6 +562,8 @@ static const struct snd_soc_dai_ops wm8510_dai_ops = {
.set_clkdiv = wm8510_set_dai_clkdiv,
.set_pll = wm8510_set_dai_pll,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8510_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8510_dai = {
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index b8832a1d61fe..9972d6599002 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -374,11 +374,24 @@ static int wm8523_set_bias_level(struct snd_soc_component *component,
#define WM8523_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8523_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8523_dai_ops = {
.startup = wm8523_startup,
.hw_params = wm8523_hw_params,
.set_sysclk = wm8523_set_dai_sysclk,
.set_fmt = wm8523_set_dai_fmt,
+ .auto_selectable_formats = &wm8523_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8523_dai = {
diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c
index 23daf158f35f..8d0565b103cd 100644
--- a/sound/soc/codecs/wm8524.c
+++ b/sound/soc/codecs/wm8524.c
@@ -190,6 +190,10 @@ static int wm8524_hw_params(struct snd_pcm_substream *substream,
SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8525_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops wm8524_dai_ops = {
.startup = wm8524_startup,
.shutdown = wm8524_shutdown,
@@ -197,6 +201,8 @@ static const struct snd_soc_dai_ops wm8524_dai_ops = {
.set_fmt = wm8524_set_fmt,
.mute_stream = wm8524_mute_stream,
.hw_params = wm8524_hw_params,
+ .auto_selectable_formats = &wm8525_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8524_dai = {
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index eb374ba6e5b5..9bc5d1e795dc 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -860,6 +860,22 @@ static int wm8580_playback_startup(struct snd_pcm_substream *substream,
#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8580_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8580_dai_ops_playback = {
.startup = wm8580_playback_startup,
.set_sysclk = wm8580_set_sysclk,
@@ -869,6 +885,8 @@ static const struct snd_soc_dai_ops wm8580_dai_ops_playback = {
.set_pll = wm8580_set_dai_pll,
.mute_stream = wm8580_mute,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8580_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8580_selectable_formats),
};
static const struct snd_soc_dai_ops wm8580_dai_ops_capture = {
@@ -877,6 +895,8 @@ static const struct snd_soc_dai_ops wm8580_dai_ops_capture = {
.set_fmt = wm8580_set_paif_dai_fmt,
.set_clkdiv = wm8580_set_dai_clkdiv,
.set_pll = wm8580_set_dai_pll,
+ .auto_selectable_formats = wm8580_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8580_selectable_formats),
};
static struct snd_soc_dai_driver wm8580_dai[] = {
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 2db8829661c3..390af15a749a 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -325,6 +325,17 @@ static int wm8711_set_bias_level(struct snd_soc_component *component,
#define WM8711_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8711_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8711_ops = {
.prepare = wm8711_pcm_prepare,
.hw_params = wm8711_hw_params,
@@ -333,6 +344,8 @@ static const struct snd_soc_dai_ops wm8711_ops = {
.set_sysclk = wm8711_set_dai_sysclk,
.set_fmt = wm8711_set_dai_fmt,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8711_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8711_dai = {
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 3109a6a0df74..7af5ce0fffe0 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -190,11 +190,20 @@ static int wm8728_set_bias_level(struct snd_soc_component *component,
#define WM8728_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8728_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8728_dai_ops = {
.hw_params = wm8728_hw_params,
.mute_stream = wm8728_mute,
.set_fmt = wm8728_set_dai_fmt,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8728_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8728_dai = {
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index ce87280d590d..f25b50919a9a 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -519,6 +519,17 @@ static int wm8731_startup(struct snd_pcm_substream *substream,
#define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8731_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8731_dai_ops = {
.startup = wm8731_startup,
.hw_params = wm8731_hw_params,
@@ -526,6 +537,8 @@ static const struct snd_soc_dai_ops wm8731_dai_ops = {
.set_sysclk = wm8731_set_dai_sysclk,
.set_fmt = wm8731_set_dai_fmt,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8731_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8731_dai = {
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index 33a3f88fffb3..72b7d09503a9 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -516,10 +516,21 @@ static int wm8737_set_bias_level(struct snd_soc_component *component,
#define WM8737_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8737_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF;
+
static const struct snd_soc_dai_ops wm8737_dai_ops = {
.hw_params = wm8737_hw_params,
.set_sysclk = wm8737_set_dai_sysclk,
.set_fmt = wm8737_set_dai_fmt,
+ .auto_selectable_formats = &wm8737_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8737_dai = {
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index ca56fdfb5088..e49adc9808b4 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -381,6 +381,17 @@ static int wm8741_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
#define WM8741_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8741_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8741_dai_ops = {
.startup = wm8741_startup,
.hw_params = wm8741_hw_params,
@@ -388,6 +399,8 @@ static const struct snd_soc_dai_ops wm8741_dai_ops = {
.set_fmt = wm8741_set_dai_fmt,
.mute_stream = wm8741_mute,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8741_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8741_dai = {
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index d3f1178c2d34..b0b444793830 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -659,12 +659,25 @@ static int wm8750_set_bias_level(struct snd_soc_component *component,
#define WM8750_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8750_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8750_dai_ops = {
.hw_params = wm8750_pcm_hw_params,
.mute_stream = wm8750_mute,
.set_fmt = wm8750_set_dai_fmt,
.set_sysclk = wm8750_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8750_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8750_dai = {
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index ac4008b4832d..cbdf1e6aea5b 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -1380,6 +1380,22 @@ static int wm8753_set_bias_level(struct snd_soc_component *component,
* 3. Voice disabled - HIFI over HIFI
* 4. Voice disabled - HIFI over HIFI, uses voice DAI LRC for capture
*/
+static const u64 wm8753_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+};
+
static const struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = {
.hw_params = wm8753_i2s_hw_params,
.mute_stream = wm8753_mute,
@@ -1388,6 +1404,8 @@ static const struct snd_soc_dai_ops wm8753_dai_ops_hifi_mode = {
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8753_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8753_selectable_formats),
};
static const struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = {
@@ -1398,6 +1416,8 @@ static const struct snd_soc_dai_ops wm8753_dai_ops_voice_mode = {
.set_pll = wm8753_set_dai_pll,
.set_sysclk = wm8753_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8753_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8753_selectable_formats),
};
static struct snd_soc_dai_driver wm8753_dai[] = {
diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c
index b8b4d1e823e6..1e485d2fe21a 100644
--- a/sound/soc/codecs/wm8770.c
+++ b/sound/soc/codecs/wm8770.c
@@ -537,12 +537,23 @@ static int wm8770_set_bias_level(struct snd_soc_component *component,
#define WM8770_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8770_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8770_dai_ops = {
.mute_stream = wm8770_mute,
.hw_params = wm8770_hw_params,
.set_fmt = wm8770_set_fmt,
.set_sysclk = wm8770_set_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8770_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8770_dai = {
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index a8e4f71c77d1..c633fe824356 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -360,18 +360,31 @@ static int wm8776_set_bias_level(struct snd_soc_component *component,
#define WM8776_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8776_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8776_dac_ops = {
.mute_stream = wm8776_mute,
.hw_params = wm8776_hw_params,
.set_fmt = wm8776_set_fmt,
.set_sysclk = wm8776_set_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8776_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops wm8776_adc_ops = {
.hw_params = wm8776_hw_params,
.set_fmt = wm8776_set_fmt,
.set_sysclk = wm8776_set_sysclk,
+ .auto_selectable_formats = &wm8776_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8776_dai[] = {
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 94aa3c8de0ab..2fc9634fe51f 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -502,12 +502,25 @@ static int wm8804_set_clkdiv(struct snd_soc_dai *dai,
return 0;
}
+static const u64 wm8804_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8804_dai_ops = {
.hw_params = wm8804_hw_params,
.set_fmt = wm8804_set_fmt,
.set_sysclk = wm8804_set_sysclk,
.set_clkdiv = wm8804_set_clkdiv,
- .set_pll = wm8804_set_pll
+ .set_pll = wm8804_set_pll,
+ .auto_selectable_formats = &wm8804_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define WM8804_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index a9128cfa6ff1..dcda7a7be0b1 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -992,6 +992,22 @@ static int wm8900_mute(struct snd_soc_dai *codec_dai, int mute, int direction)
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8900_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8900_dai_ops = {
.hw_params = wm8900_hw_params,
.set_clkdiv = wm8900_set_dai_clkdiv,
@@ -999,6 +1015,8 @@ static const struct snd_soc_dai_ops wm8900_dai_ops = {
.set_fmt = wm8900_set_dai_fmt,
.mute_stream = wm8900_mute,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8900_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8900_selectable_formats),
};
static struct snd_soc_dai_driver wm8900_dai = {
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index fbf388cf966d..553f8d68a546 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1733,12 +1733,30 @@ static irqreturn_t wm8903_irq(int irq, void *data)
SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8903_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8903_dai_ops = {
.hw_params = wm8903_hw_params,
.mute_stream = wm8903_mute,
.set_fmt = wm8903_set_dai_fmt,
.set_sysclk = wm8903_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8903_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8903_selectable_formats),
};
static struct snd_soc_dai_driver wm8903_dai = {
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 9e5782e50f47..96d2e54a2c6a 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2026,6 +2026,22 @@ static int wm8904_set_bias_level(struct snd_soc_component *component,
#define WM8904_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8904_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8904_dai_ops = {
.set_sysclk = wm8904_set_sysclk,
.set_fmt = wm8904_set_fmt,
@@ -2034,6 +2050,8 @@ static const struct snd_soc_dai_ops wm8904_dai_ops = {
.hw_params = wm8904_hw_params,
.mute_stream = wm8904_mute,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8904_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8904_selectable_formats),
};
static struct snd_soc_dai_driver wm8904_dai = {
@@ -2186,7 +2204,7 @@ static void wm8904_handle_pdata(struct snd_soc_component *component)
/* We need an array of texts for the enum API */
wm8904->drc_texts = kmalloc_array(pdata->num_drc_cfgs,
- sizeof(char *),
+ sizeof(*wm8904->drc_texts),
GFP_KERNEL);
if (!wm8904->drc_texts)
return;
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 229252363188..012556215d12 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -730,6 +730,17 @@ static int wm8940_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8940_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8940_dai_ops = {
.hw_params = wm8940_i2s_hw_params,
.set_sysclk = wm8940_set_dai_sysclk,
@@ -738,6 +749,8 @@ static const struct snd_soc_dai_ops wm8940_dai_ops = {
.set_clkdiv = wm8940_set_dai_clkdiv,
.set_pll = wm8940_set_dai_pll,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8940_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8940_dai = {
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index c897a9ab764d..017ec2e37d1e 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -845,12 +845,30 @@ static int wm8955_set_bias_level(struct snd_soc_component *component,
#define WM8955_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8955_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8955_dai_ops = {
.set_sysclk = wm8955_set_sysclk,
.set_fmt = wm8955_set_fmt,
.hw_params = wm8955_hw_params,
.mute_stream = wm8955_mute,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8955_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8955_selectable_formats),
};
static struct snd_soc_dai_driver wm8955_dai = {
@@ -870,17 +888,7 @@ static int wm8955_probe(struct snd_soc_component *component)
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct wm8955_priv *wm8955 = snd_soc_component_get_drvdata(component);
struct wm8955_pdata *pdata = dev_get_platdata(component->dev);
- int ret, i;
-
- for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++)
- wm8955->supplies[i].supply = wm8955_supply_names[i];
-
- ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(wm8955->supplies),
- wm8955->supplies);
- if (ret != 0) {
- dev_err(component->dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
+ int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(wm8955->supplies),
wm8955->supplies);
@@ -972,7 +980,7 @@ static const struct regmap_config wm8955_regmap = {
static int wm8955_i2c_probe(struct i2c_client *i2c)
{
struct wm8955_priv *wm8955;
- int ret;
+ int i, ret;
wm8955 = devm_kzalloc(&i2c->dev, sizeof(struct wm8955_priv),
GFP_KERNEL);
@@ -989,6 +997,16 @@ static int wm8955_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, wm8955);
+ for (i = 0; i < ARRAY_SIZE(wm8955->supplies); i++)
+ wm8955->supplies[i].supply = wm8955_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8955->supplies),
+ wm8955->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_wm8955, &wm8955_dai, 1);
diff --git a/sound/soc/codecs/wm8958-dsp2.c b/sound/soc/codecs/wm8958-dsp2.c
index f75a6dc9d2bb..b26c36ca8249 100644
--- a/sound/soc/codecs/wm8958-dsp2.c
+++ b/sound/soc/codecs/wm8958-dsp2.c
@@ -928,7 +928,7 @@ void wm8958_dsp2_init(struct snd_soc_component *component)
/* We need an array of texts for the enum API */
wm8994->mbc_texts = kmalloc_array(pdata->num_mbc_cfgs,
- sizeof(char *),
+ sizeof(*wm8994->mbc_texts),
GFP_KERNEL);
if (!wm8994->mbc_texts)
return;
@@ -954,7 +954,7 @@ void wm8958_dsp2_init(struct snd_soc_component *component)
/* We need an array of texts for the enum API */
wm8994->vss_texts = kmalloc_array(pdata->num_vss_cfgs,
- sizeof(char *),
+ sizeof(*wm8994->vss_texts),
GFP_KERNEL);
if (!wm8994->vss_texts)
return;
@@ -981,7 +981,7 @@ void wm8958_dsp2_init(struct snd_soc_component *component)
/* We need an array of texts for the enum API */
wm8994->vss_hpf_texts = kmalloc_array(pdata->num_vss_hpf_cfgs,
- sizeof(char *),
+ sizeof(*wm8994->vss_hpf_texts),
GFP_KERNEL);
if (!wm8994->vss_hpf_texts)
return;
@@ -1009,7 +1009,7 @@ void wm8958_dsp2_init(struct snd_soc_component *component)
/* We need an array of texts for the enum API */
wm8994->enh_eq_texts = kmalloc_array(pdata->num_enh_eq_cfgs,
- sizeof(char *),
+ sizeof(*wm8994->enh_eq_texts),
GFP_KERNEL);
if (!wm8994->enh_eq_texts)
return;
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index a810732c6af0..093530ae872b 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -1340,6 +1340,17 @@ static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8960_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8960_dai_ops = {
.hw_params = wm8960_hw_params,
.hw_free = wm8960_hw_free,
@@ -1349,6 +1360,8 @@ static const struct snd_soc_dai_ops wm8960_dai_ops = {
.set_pll = wm8960_set_dai_pll,
.set_sysclk = wm8960_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8960_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8960_dai = {
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index 504fc59e4be2..55ccac7286c9 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -803,6 +803,22 @@ static int wm8961_set_bias_level(struct snd_soc_component *component,
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8961_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8961_dai_ops = {
.hw_params = wm8961_hw_params,
.set_sysclk = wm8961_set_sysclk,
@@ -811,6 +827,8 @@ static const struct snd_soc_dai_ops wm8961_dai_ops = {
.set_tristate = wm8961_set_tristate,
.set_clkdiv = wm8961_set_clkdiv,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8961_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8961_selectable_formats),
};
static struct snd_soc_dai_driver wm8961_dai = {
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 8a9598161b35..db8b86d522ff 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3056,6 +3056,22 @@ static int wm8962_mute(struct snd_soc_dai *dai, int mute, int direction)
#define WM8962_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8962_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8962_dai_ops = {
.hw_params = wm8962_hw_params,
.set_sysclk = wm8962_set_dai_sysclk,
@@ -3063,6 +3079,8 @@ static const struct snd_soc_dai_ops wm8962_dai_ops = {
.set_tdm_slot = wm8962_set_tdm_slot,
.mute_stream = wm8962_mute,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8962_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8962_selectable_formats),
};
static struct snd_soc_dai_driver wm8962_dai = {
@@ -3355,7 +3373,7 @@ static void wm8962_init_beep(struct snd_soc_component *component)
struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int ret;
- wm8962->beep = devm_input_allocate_device(component->dev);
+ wm8962->beep = input_allocate_device();
if (!wm8962->beep) {
dev_err(component->dev, "Failed to allocate beep device\n");
return;
@@ -3376,8 +3394,10 @@ static void wm8962_init_beep(struct snd_soc_component *component)
ret = input_register_device(wm8962->beep);
if (ret != 0) {
+ input_free_device(wm8962->beep);
wm8962->beep = NULL;
dev_err(component->dev, "Failed to register beep device\n");
+ return;
}
ret = device_create_file(component->dev, &dev_attr_beep);
@@ -3393,7 +3413,10 @@ static void wm8962_free_beep(struct snd_soc_component *component)
device_remove_file(component->dev, &dev_attr_beep);
cancel_work_sync(&wm8962->beep_work);
- wm8962->beep = NULL;
+ if (wm8962->beep) {
+ input_unregister_device(wm8962->beep);
+ wm8962->beep = NULL;
+ }
snd_soc_component_update_bits(component, WM8962_BEEP_GENERATOR_1, WM8962_BEEP_ENA,0);
}
@@ -3430,7 +3453,7 @@ static int wm8962_gpio_request(struct gpio_chip *chip, unsigned offset)
/* The WM8962 GPIOs aren't linearly numbered. For simplicity
* we export linear numbers and error out if the unsupported
- * ones are requsted.
+ * ones are requested.
*/
switch (offset + 1) {
case 2:
@@ -3525,34 +3548,12 @@ static void wm8962_free_gpio(struct snd_soc_component *component)
static int wm8962_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- int ret;
struct wm8962_priv *wm8962 = snd_soc_component_get_drvdata(component);
int i;
bool dmicclk, dmicdat;
wm8962->component = component;
- wm8962->disable_nb[0].notifier_call = wm8962_regulator_event_0;
- wm8962->disable_nb[1].notifier_call = wm8962_regulator_event_1;
- wm8962->disable_nb[2].notifier_call = wm8962_regulator_event_2;
- wm8962->disable_nb[3].notifier_call = wm8962_regulator_event_3;
- wm8962->disable_nb[4].notifier_call = wm8962_regulator_event_4;
- wm8962->disable_nb[5].notifier_call = wm8962_regulator_event_5;
- wm8962->disable_nb[6].notifier_call = wm8962_regulator_event_6;
- wm8962->disable_nb[7].notifier_call = wm8962_regulator_event_7;
-
- /* This should really be moved into the regulator core */
- for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) {
- ret = devm_regulator_register_notifier(
- wm8962->supplies[i].consumer,
- &wm8962->disable_nb[i]);
- if (ret != 0) {
- dev_err(component->dev,
- "Failed to register regulator notifier: %d\n",
- ret);
- }
- }
-
wm8962_add_widgets(component);
/* Save boards having to disable DMIC when not in use */
@@ -3742,6 +3743,27 @@ static int wm8962_i2c_probe(struct i2c_client *i2c)
regcache_cache_bypass(wm8962->regmap, false);
+ wm8962->disable_nb[0].notifier_call = wm8962_regulator_event_0;
+ wm8962->disable_nb[1].notifier_call = wm8962_regulator_event_1;
+ wm8962->disable_nb[2].notifier_call = wm8962_regulator_event_2;
+ wm8962->disable_nb[3].notifier_call = wm8962_regulator_event_3;
+ wm8962->disable_nb[4].notifier_call = wm8962_regulator_event_4;
+ wm8962->disable_nb[5].notifier_call = wm8962_regulator_event_5;
+ wm8962->disable_nb[6].notifier_call = wm8962_regulator_event_6;
+ wm8962->disable_nb[7].notifier_call = wm8962_regulator_event_7;
+
+ /* This should really be moved into the regulator core */
+ for (i = 0; i < ARRAY_SIZE(wm8962->supplies); i++) {
+ ret = devm_regulator_register_notifier(wm8962->supplies[i].consumer,
+ &wm8962->disable_nb[i]);
+ if (ret != 0) {
+ dev_err(&i2c->dev,
+ "Failed to register regulator notifier: %d\n",
+ ret);
+ goto err_enable;
+ }
+ }
+
ret = wm8962_reset(wm8962);
if (ret < 0) {
dev_err(&i2c->dev, "Failed to issue reset\n");
@@ -3813,7 +3835,7 @@ static int wm8962_i2c_probe(struct i2c_client *i2c)
regmap_update_bits(wm8962->regmap, WM8962_EQ1,
WM8962_EQ_SHARED_COEFF, 0);
- /* Don't debouce interrupts so we don't need SYSCLK */
+ /* Don't debounce interrupts so we don't need SYSCLK */
regmap_update_bits(wm8962->regmap, WM8962_IRQ_DEBOUNCE,
WM8962_FLL_LOCK_DB | WM8962_PLL3_LOCK_DB |
WM8962_PLL2_LOCK_DB | WM8962_TEMP_SHUT_DB,
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index a5d2e91b66bb..388ee05d4520 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -601,12 +601,25 @@ static int wm8971_set_bias_level(struct snd_soc_component *component,
#define WM8971_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8971_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8971_dai_ops = {
.hw_params = wm8971_pcm_hw_params,
.mute_stream = wm8971_mute,
.set_fmt = wm8971_set_dai_fmt,
.set_sysclk = wm8971_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8971_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8971_dai = {
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 4656652b8e97..5e1737d455b1 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -623,6 +623,21 @@ static int wm8974_set_bias_level(struct snd_soc_component *component,
#define WM8974_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8974_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8974_ops = {
.hw_params = wm8974_pcm_hw_params,
.mute_stream = wm8974_mute,
@@ -631,6 +646,8 @@ static const struct snd_soc_dai_ops wm8974_ops = {
.set_pll = wm8974_set_dai_pll,
.set_sysclk = wm8974_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8974_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8974_selectable_formats),
};
static struct snd_soc_dai_driver wm8974_dai = {
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index ad8064bbaaac..25cc433c7653 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -892,6 +892,16 @@ static int wm8978_set_bias_level(struct snd_soc_component *component,
#define WM8978_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8978_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8978_dai_ops = {
.hw_params = wm8978_hw_params,
.mute_stream = wm8978_mute,
@@ -899,6 +909,8 @@ static const struct snd_soc_dai_ops wm8978_dai_ops = {
.set_clkdiv = wm8978_set_dai_clkdiv,
.set_sysclk = wm8978_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8978_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
/* Also supports 12kHz */
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index fab25f35fd14..3366166a2b77 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -943,6 +943,16 @@ static int wm8983_probe(struct snd_soc_component *component)
return 0;
}
+/* FIXME: it doesn't support DSP A/B for now */
+static const u64 wm8983_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8983_dai_ops = {
.mute_stream = wm8983_dac_mute,
.hw_params = wm8983_hw_params,
@@ -950,6 +960,8 @@ static const struct snd_soc_dai_ops wm8983_dai_ops = {
.set_sysclk = wm8983_set_sysclk,
.set_pll = wm8983_set_pll,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8983_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define WM8983_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 2a64d5a851da..b5ca4af715c0 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1024,23 +1024,30 @@ static int wm8985_set_bias_level(struct snd_soc_component *component,
return 0;
}
-static int wm8985_probe(struct snd_soc_component *component)
+static int wm8985_get_regulators(struct device *dev,
+ struct wm8985_priv *wm8985)
{
size_t i;
- struct wm8985_priv *wm8985;
int ret;
- wm8985 = snd_soc_component_get_drvdata(component);
-
for (i = 0; i < ARRAY_SIZE(wm8985->supplies); i++)
wm8985->supplies[i].supply = wm8985_supply_names[i];
- ret = devm_regulator_bulk_get(component->dev, ARRAY_SIZE(wm8985->supplies),
- wm8985->supplies);
- if (ret) {
- dev_err(component->dev, "Failed to request supplies: %d\n", ret);
- return ret;
- }
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8985->supplies),
+ wm8985->supplies);
+ if (ret)
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
+
+ return ret;
+}
+
+static int wm8985_probe(struct snd_soc_component *component)
+{
+ size_t i;
+ struct wm8985_priv *wm8985;
+ int ret;
+
+ wm8985 = snd_soc_component_get_drvdata(component);
ret = regulator_bulk_enable(ARRAY_SIZE(wm8985->supplies),
wm8985->supplies);
@@ -1072,6 +1079,22 @@ err_reg_enable:
return ret;
}
+static const u64 wm8985_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8985_dai_ops = {
.mute_stream = wm8985_dac_mute,
.hw_params = wm8985_hw_params,
@@ -1079,6 +1102,8 @@ static const struct snd_soc_dai_ops wm8985_dai_ops = {
.set_sysclk = wm8985_set_sysclk,
.set_pll = wm8985_set_pll,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8985_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8985_selectable_formats),
};
#define WM8985_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -1153,6 +1178,10 @@ static int wm8985_spi_probe(struct spi_device *spi)
return ret;
}
+ ret = wm8985_get_regulators(&spi->dev, wm8985);
+ if (ret)
+ return ret;
+
ret = devm_snd_soc_register_component(&spi->dev,
&soc_component_dev_wm8985, &wm8985_dai, 1);
return ret;
@@ -1189,6 +1218,10 @@ static int wm8985_i2c_probe(struct i2c_client *i2c)
return ret;
}
+ ret = wm8985_get_regulators(&i2c->dev, wm8985);
+ if (ret)
+ return ret;
+
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_wm8985, &wm8985_dai, 1);
return ret;
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 741aecc7641a..6f97c9aa453a 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -762,6 +762,17 @@ static int wm8988_set_bias_level(struct snd_soc_component *component,
#define WM8988_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8988_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8988_ops = {
.startup = wm8988_pcm_startup,
.hw_params = wm8988_pcm_hw_params,
@@ -769,6 +780,8 @@ static const struct snd_soc_dai_ops wm8988_ops = {
.set_sysclk = wm8988_set_dai_sysclk,
.mute_stream = wm8988_mute,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8988_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8988_dai = {
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 11dfbbbdcd92..c8fef187f540 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1151,6 +1151,13 @@ static int wm8990_set_bias_level(struct snd_soc_component *component,
* 1. ADC/DAC on Primary Interface
* 2. ADC on Primary Interface/DAC on secondary
*/
+static const u64 wm8990_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops wm8990_dai_ops = {
.hw_params = wm8990_hw_params,
.mute_stream = wm8990_mute,
@@ -1159,6 +1166,8 @@ static const struct snd_soc_dai_ops wm8990_dai_ops = {
.set_pll = wm8990_set_dai_pll,
.set_sysclk = wm8990_set_dai_sysclk,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8990_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8990_dai = {
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index 225c235d3d89..2cb7fb9962af 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1195,6 +1195,13 @@ static int wm8991_set_bias_level(struct snd_soc_component *component,
#define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm8991_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops wm8991_ops = {
.hw_params = wm8991_hw_params,
.mute_stream = wm8991_mute,
@@ -1202,6 +1209,8 @@ static const struct snd_soc_dai_ops wm8991_ops = {
.set_clkdiv = wm8991_set_dai_clkdiv,
.set_pll = wm8991_set_dai_pll,
.no_capture_mute = 1,
+ .auto_selectable_formats = &wm8991_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
/*
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 2a94b23319b5..934fa41a53cf 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1441,6 +1441,22 @@ static irqreturn_t wm8993_irq(int irq, void *data)
return IRQ_HANDLED;
}
+static const u64 wm8993_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8993_ops = {
.set_sysclk = wm8993_set_sysclk,
.set_fmt = wm8993_set_dai_fmt,
@@ -1449,6 +1465,8 @@ static const struct snd_soc_dai_ops wm8993_ops = {
.set_pll = wm8993_set_fll,
.set_tdm_slot = wm8993_set_tdm_slot,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8993_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8993_selectable_formats),
};
#define WM8993_RATES SNDRV_PCM_RATE_8000_48000
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 8bf58a6a8af7..b8881b260a4b 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -3202,6 +3202,22 @@ static int wm8994_aif2_probe(struct snd_soc_dai *dai)
#define WM8994_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8994_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
@@ -3210,6 +3226,8 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8994_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8994_selectable_formats),
};
static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
@@ -3221,6 +3239,8 @@ static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
.set_pll = wm8994_set_fll,
.set_tristate = wm8994_set_tristate,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8994_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8994_selectable_formats),
};
static const struct snd_soc_dai_ops wm8994_aif3_dai_ops = {
@@ -3532,6 +3552,7 @@ int wm8994_mic_detect(struct snd_soc_component *component, struct snd_soc_jack *
break;
default:
dev_warn(component->dev, "Invalid MICBIAS %d\n", micbias);
+ pm_runtime_put(component->dev);
return -EINVAL;
}
@@ -4340,6 +4361,7 @@ static int wm8994_component_probe(struct snd_soc_component *component)
ret = regmap_read(control->regmap, WM8994_GPIO_1, &reg);
if (ret < 0) {
dev_err(component->dev, "Failed to read GPIO1 state: %d\n", ret);
+ pm_runtime_put(component->dev);
goto err_irq;
}
if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
@@ -4352,6 +4374,7 @@ static int wm8994_component_probe(struct snd_soc_component *component)
ret = regmap_read(control->regmap, WM8994_GPIO_6, &reg);
if (ret < 0) {
dev_err(component->dev, "Failed to read GPIO6 state: %d\n", ret);
+ pm_runtime_put(component->dev);
goto err_irq;
}
if ((reg & WM8994_GPN_FN_MASK) != WM8994_GP_FN_PIN_SPECIFIC) {
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 799989a5bf0f..7f01d203e058 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -1993,23 +1993,17 @@ static int wm8995_set_bias_level(struct snd_soc_component *component,
return 0;
}
-static int wm8995_probe(struct snd_soc_component *component)
+static int wm8995_hw_init(struct device *dev, struct wm8995_priv *wm8995)
{
- struct wm8995_priv *wm8995;
- int i;
- int ret;
-
- wm8995 = snd_soc_component_get_drvdata(component);
- wm8995->component = component;
+ int i, ret;
for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++)
wm8995->supplies[i].supply = wm8995_supply_names[i];
- ret = devm_regulator_bulk_get(component->dev,
- ARRAY_SIZE(wm8995->supplies),
+ ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
if (ret) {
- dev_err(component->dev, "Failed to request supplies: %d\n", ret);
+ dev_err(dev, "Failed to request supplies: %d\n", ret);
return ret;
}
@@ -2024,16 +2018,27 @@ static int wm8995_probe(struct snd_soc_component *component)
/* This should really be moved into the regulator core */
for (i = 0; i < ARRAY_SIZE(wm8995->supplies); i++) {
- ret = devm_regulator_register_notifier(
- wm8995->supplies[i].consumer,
- &wm8995->disable_nb[i]);
+ ret = devm_regulator_register_notifier(wm8995->supplies[i].consumer,
+ &wm8995->disable_nb[i]);
if (ret) {
- dev_err(component->dev,
+ dev_err(dev,
"Failed to register regulator notifier: %d\n",
ret);
+ return ret;
}
}
+ return 0;
+}
+
+static int wm8995_probe(struct snd_soc_component *component)
+{
+ struct wm8995_priv *wm8995;
+ int ret;
+
+ wm8995 = snd_soc_component_get_drvdata(component);
+ wm8995->component = component;
+
ret = regulator_bulk_enable(ARRAY_SIZE(wm8995->supplies),
wm8995->supplies);
if (ret) {
@@ -2091,6 +2096,22 @@ err_reg_enable:
#define WM8995_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8995_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm8995_aif1_dai_ops = {
.set_sysclk = wm8995_set_dai_sysclk,
.set_fmt = wm8995_set_dai_fmt,
@@ -2099,6 +2120,8 @@ static const struct snd_soc_dai_ops wm8995_aif1_dai_ops = {
.set_pll = wm8995_set_fll,
.set_tristate = wm8995_set_tristate,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8995_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8995_selectable_formats),
};
static const struct snd_soc_dai_ops wm8995_aif2_dai_ops = {
@@ -2109,6 +2132,8 @@ static const struct snd_soc_dai_ops wm8995_aif2_dai_ops = {
.set_pll = wm8995_set_fll,
.set_tristate = wm8995_set_tristate,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm8995_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm8995_selectable_formats),
};
static const struct snd_soc_dai_ops wm8995_aif3_dai_ops = {
@@ -2216,6 +2241,10 @@ static int wm8995_spi_probe(struct spi_device *spi)
return ret;
}
+ ret = wm8995_hw_init(&spi->dev, wm8995);
+ if (ret)
+ return ret;
+
ret = devm_snd_soc_register_component(&spi->dev,
&soc_component_dev_wm8995, wm8995_dai,
ARRAY_SIZE(wm8995_dai));
@@ -2249,6 +2278,10 @@ static int wm8995_i2c_probe(struct i2c_client *i2c)
return ret;
}
+ ret = wm8995_hw_init(&i2c->dev, wm8995);
+ if (ret)
+ return ret;
+
ret = devm_snd_soc_register_component(&i2c->dev,
&soc_component_dev_wm8995, wm8995_dai,
ARRAY_SIZE(wm8995_dai));
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index dface555f928..9d8fb4ee4312 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -2106,7 +2106,7 @@ static int wm8996_set_fll(struct snd_soc_component *component, int fll_id, int s
if (i2c->irq)
timeout *= 10;
else
- /* ensure timeout of atleast 1 jiffies */
+ /* ensure timeout of at least 1 jiffies */
timeout = (timeout/2) ? : 1;
for (retry = 0; retry < 10; retry++) {
@@ -2703,10 +2703,22 @@ static const struct snd_soc_component_driver soc_component_dev_wm8996 = {
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE |\
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm8996_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm8996_dai_ops = {
.set_fmt = wm8996_set_fmt,
.hw_params = wm8996_hw_params,
.set_sysclk = wm8996_set_sysclk,
+ .auto_selectable_formats = &wm8996_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm8996_dai[] = {
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 2e4e0a76499d..824e9c6b5f3e 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1230,12 +1230,30 @@ static int wm9081_set_tdm_slot(struct snd_soc_dai *dai,
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 wm9081_selectable_formats[] = {
+ /* 1st priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF,
+ /* 2nd priority */
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF,
+};
+
static const struct snd_soc_dai_ops wm9081_dai_ops = {
.hw_params = wm9081_hw_params,
.set_fmt = wm9081_set_dai_fmt,
.mute_stream = wm9081_mute,
.set_tdm_slot = wm9081_set_tdm_slot,
.no_capture_mute = 1,
+ .auto_selectable_formats = wm9081_selectable_formats,
+ .num_auto_selectable_formats = ARRAY_SIZE(wm9081_selectable_formats),
};
/* We report two channels because the CODEC processes a stereo signal, even
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 3ba7ca3c1770..3cffdfbbf9dd 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -1067,6 +1067,17 @@ static int ac97_aux_prepare(struct snd_pcm_substream *substream,
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 wm9713_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops wm9713_dai_ops_hifi = {
.prepare = ac97_hifi_prepare,
.set_clkdiv = wm9713_set_dai_clkdiv,
@@ -1085,6 +1096,8 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
.set_pll = wm9713_set_dai_pll,
.set_fmt = wm9713_set_dai_fmt,
.set_tristate = wm9713_set_dai_tristate,
+ .auto_selectable_formats = &wm9713_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver wm9713_dai[] = {
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index f80dfc488e3c..019940589e51 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -338,7 +338,7 @@ static int wm8993_put_dc_servo(struct snd_kcontrol *kcontrol,
ret = snd_soc_put_volsw(kcontrol, ucontrol);
/* If we're applying an offset correction then updating the
- * callibration would be likely to introduce further offsets. */
+ * calibration would be likely to introduce further offsets. */
if (hubs->dcs_codes_l || hubs->dcs_codes_r || hubs->no_series_update)
return ret;
diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c
index 894b8eb42e39..b7062860cca1 100644
--- a/sound/soc/codecs/zl38060.c
+++ b/sound/soc/codecs/zl38060.c
@@ -334,10 +334,16 @@ static const struct reg_sequence cp_config_stereo_bypass[] = {
{ 0x0202, 0x000F }, /* enable I2S1 + DAC */
};
+static const u64 zl38_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops zl38_dai_ops = {
.set_fmt = zl38_set_fmt,
.hw_params = zl38_hw_params,
.hw_free = zl38_hw_free,
+ .auto_selectable_formats = &zl38_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver zl38_dai = {
diff --git a/sound/soc/dwc/dwc-i2s.c b/sound/soc/dwc/dwc-i2s.c
index 2e9fb79b1cd8..ade89c196a79 100644
--- a/sound/soc/dwc/dwc-i2s.c
+++ b/sound/soc/dwc/dwc-i2s.c
@@ -464,6 +464,13 @@ static int dw_i2s_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 dw_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
.probe = dw_i2s_dai_probe,
.startup = dw_i2s_startup,
@@ -472,6 +479,8 @@ static const struct snd_soc_dai_ops dw_i2s_dai_ops = {
.trigger = dw_i2s_trigger,
.set_fmt = dw_i2s_set_fmt,
.set_tdm_slot = dw_i2s_set_tdm_slot,
+ .auto_selectable_formats = &dw_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int dw_i2s_runtime_suspend(struct device *dev)
diff --git a/sound/soc/fsl/Kconfig b/sound/soc/fsl/Kconfig
index 04940879dfd8..101bc952aa14 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -234,7 +234,7 @@ config SND_SOC_MPC5200_I2S
config SND_SOC_MPC5200_AC97
tristate "Freescale MPC5200 PSC in AC97 mode driver"
- depends on PPC_BESTCOMM
+ depends on PPC_BESTCOMM && PPC_MPC52xx
select SND_SOC_AC97_BUS
select SND_MPC52xx_DMA
select PPC_BESTCOMM_GEN_BD
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index b24737add001..faf348dd582d 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -521,10 +521,10 @@ static int fsl_asrc_config_pair(struct fsl_asrc_pair *pair, bool use_ideal_rate)
ASRCTR_USR(index));
/* Set the input and output clock sources */
- regmap_update_bits(asrc->regmap, REG_ASRCSR,
- ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
- ASRCSR_AICS(index, clk_index[IN]) |
- ASRCSR_AOCS(index, clk_index[OUT]));
+ regmap_write_bits(asrc->regmap, REG_ASRCSR,
+ ASRCSR_AICSi_MASK(index) | ASRCSR_AOCSi_MASK(index),
+ ASRCSR_AICS(index, clk_index[IN]) |
+ ASRCSR_AOCS(index, clk_index[OUT]));
/* Calculate the input clock divisors */
indiv = fsl_asrc_cal_asrck_divisor(pair, div[IN]);
@@ -638,7 +638,7 @@ static struct dma_chan *fsl_asrc_get_dma_channel(struct fsl_asrc_pair *pair,
sprintf(name, "%cx%c", dir == IN ? 'r' : 't', index + 'a');
- return dma_request_slave_channel(&asrc->pdev->dev, name);
+ return dma_request_chan(&asrc->pdev->dev, name);
}
static int fsl_asrc_dai_startup(struct snd_pcm_substream *substream,
@@ -781,18 +781,7 @@ static int fsl_asrc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-static int fsl_asrc_dai_probe(struct snd_soc_dai *dai)
-{
- struct fsl_asrc *asrc = snd_soc_dai_get_drvdata(dai);
-
- snd_soc_dai_init_dma_data(dai, &asrc->dma_params_tx,
- &asrc->dma_params_rx);
-
- return 0;
-}
-
static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
- .probe = fsl_asrc_dai_probe,
.startup = fsl_asrc_dai_startup,
.hw_params = fsl_asrc_dai_hw_params,
.hw_free = fsl_asrc_dai_hw_free,
@@ -803,27 +792,76 @@ static const struct snd_soc_dai_ops fsl_asrc_dai_ops = {
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE)
-static struct snd_soc_dai_driver fsl_asrc_dai = {
- .playback = {
- .stream_name = "ASRC-Playback",
- .channels_min = 1,
- .channels_max = 10,
- .rate_min = 5512,
- .rate_max = 192000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_ASRC_FORMATS |
- SNDRV_PCM_FMTBIT_S8,
+static struct snd_soc_dai_driver fsl_asrc_dai[] = {
+ {
+ .name = "asrc-0",
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
},
- .capture = {
- .stream_name = "ASRC-Capture",
- .channels_min = 1,
- .channels_max = 10,
- .rate_min = 5512,
- .rate_max = 192000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_ASRC_FORMATS,
+ {
+ .name = "asrc-1",
+ .playback = {
+ .stream_name = "ASRC1-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "ASRC1-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
+ },
+ {
+ .name = "asrc-2",
+ .playback = {
+ .stream_name = "ASRC2-Playback",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_S8,
+ },
+ .capture = {
+ .stream_name = "ASRC2-Capture",
+ .channels_min = 1,
+ .channels_max = 10,
+ .rate_min = 5512,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_ASRC_FORMATS,
+ },
+ .ops = &fsl_asrc_dai_ops,
},
- .ops = &fsl_asrc_dai_ops,
};
static bool fsl_asrc_readable_reg(struct device *dev, unsigned int reg)
@@ -1404,7 +1442,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
goto err_pm_get_sync;
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
- &fsl_asrc_dai, 1);
+ fsl_asrc_dai, ARRAY_SIZE(fsl_asrc_dai));
if (ret) {
dev_err(&pdev->dev, "failed to register ASoC DAI\n");
goto err_pm_get_sync;
diff --git a/sound/soc/fsl/fsl_asrc_common.h b/sound/soc/fsl/fsl_asrc_common.h
index c8a1a2b5915d..4e6b00cb5d63 100644
--- a/sound/soc/fsl/fsl_asrc_common.h
+++ b/sound/soc/fsl/fsl_asrc_common.h
@@ -53,6 +53,7 @@ struct fsl_asrc_m2m_cap {
* @dma_data: private dma data
* @pos: hardware pointer position
* @req_dma_chan: flag to release dev_to_dev chan
+ * @dma_params: DMA parameters for transmit/receive channel
* @private: pair private area
* @complete: dma task complete
* @sample_format: format of m2m
@@ -76,6 +77,8 @@ struct fsl_asrc_pair {
unsigned int pos;
bool req_dma_chan;
+ struct snd_dmaengine_dai_dma_data dma_params;
+
void *private;
/* used for m2m */
@@ -92,8 +95,6 @@ struct fsl_asrc_pair {
/**
* fsl_asrc: ASRC common data
*
- * @dma_params_rx: DMA parameters for receive channel
- * @dma_params_tx: DMA parameters for transmit channel
* @pdev: platform device pointer
* @regmap: regmap handler
* @paddr: physical address to the base address of registers
@@ -128,8 +129,6 @@ struct fsl_asrc_pair {
* @private: private data structure
*/
struct fsl_asrc {
- struct snd_dmaengine_dai_dma_data dma_params_rx;
- struct snd_dmaengine_dai_dma_data dma_params_tx;
struct platform_device *pdev;
struct regmap *regmap;
unsigned long paddr;
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index 2f662bdf14d0..b417de6abebe 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -136,8 +136,8 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
struct snd_dmaengine_dai_dma_data *dma_params_be = NULL;
struct snd_pcm_runtime *runtime = substream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
- struct dma_chan *tmp_chan = NULL, *be_chan = NULL;
struct snd_soc_component *component_be = NULL;
+ struct dma_chan *tmp_chan, *be_chan = NULL;
struct fsl_asrc *asrc = pair->asrc;
struct dma_slave_config config_fe = {}, config_be = {};
struct sdma_peripheral_config audio_config;
@@ -190,11 +190,12 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
dma_params_fe->addr = asrc->paddr + asrc->get_fifo_addr(!dir, index);
dma_params_fe->maxburst = dma_params_be->maxburst;
- pair->dma_chan[!dir] = asrc->get_dma_channel(pair, !dir);
- if (!pair->dma_chan[!dir]) {
+ tmp_chan = asrc->get_dma_channel(pair, !dir);
+ if (IS_ERR(tmp_chan)) {
dev_err(dev, "failed to request DMA channel\n");
- return -EINVAL;
+ return PTR_ERR(tmp_chan);
}
+ pair->dma_chan[!dir] = tmp_chan;
ret = snd_dmaengine_pcm_prepare_slave_config(substream, params, &config_fe);
if (ret) {
@@ -223,6 +224,8 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
be_chan = pcm->chan[substream->stream];
tmp_chan = be_chan;
+ } else {
+ tmp_chan = NULL;
}
if (!tmp_chan) {
tmp_chan = dma_request_chan(dev_be, tx ? "tx" : "rx");
@@ -248,6 +251,11 @@ static int fsl_asrc_dma_hw_params(struct snd_soc_component *component,
/* Get DMA request of Front-End */
tmp_chan = asrc->get_dma_channel(pair, dir);
+ if (!tmp_chan) {
+ dma_release_channel(pair->dma_chan[!dir]);
+ pair->dma_chan[!dir] = NULL;
+ return -EINVAL;
+ }
tmp_data = tmp_chan->private;
pair->dma_data.dma_request2 = tmp_data->dma_request;
pair->dma_data.peripheral_type = tmp_data->peripheral_type;
@@ -392,6 +400,20 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component,
runtime->private_data = pair;
+ /*
+ * Point the cpu DAI dma_data at the per-pair params so that
+ * concurrent hw_params calls on different pairs each write to
+ * their own struct and do not race on addr/maxburst. Use the
+ * per-direction setters so that a concurrent open of the other
+ * direction on the same DAI does not NULL out its pointer.
+ */
+ if (tx)
+ snd_soc_dai_dma_data_set_playback(snd_soc_rtd_to_cpu(rtd, 0),
+ &pair->dma_params);
+ else
+ snd_soc_dai_dma_data_set_capture(snd_soc_rtd_to_cpu(rtd, 0),
+ &pair->dma_params);
+
/* Request a dummy pair, which will be released later.
* Request pair function needs channel num as input, for this
* dummy pair, we just request "1" channel temporarily.
@@ -404,9 +426,9 @@ static int fsl_asrc_dma_startup(struct snd_soc_component *component,
/* Request a dummy dma channel, which will be released later. */
tmp_chan = asrc->get_dma_channel(pair, dir);
- if (!tmp_chan) {
+ ret = PTR_ERR_OR_ZERO(tmp_chan);
+ if (ret) {
dev_err(dev, "failed to get dma channel\n");
- ret = -EINVAL;
goto dma_chan_err;
}
@@ -497,9 +519,9 @@ static int fsl_asrc_dma_pcm_new(struct snd_soc_component *component,
/* Request a dma channel, which will be released later. */
chan = asrc->get_dma_channel(pair, IN);
- if (!chan) {
+ ret = PTR_ERR_OR_ZERO(chan);
+ if (ret) {
dev_err(dev, "failed to get dma channel\n");
- ret = -EINVAL;
goto dma_chan_err;
}
diff --git a/sound/soc/fsl/fsl_asrc_m2m.c b/sound/soc/fsl/fsl_asrc_m2m.c
index 4bc40f328f58..a5e3670349f9 100644
--- a/sound/soc/fsl/fsl_asrc_m2m.c
+++ b/sound/soc/fsl/fsl_asrc_m2m.c
@@ -320,7 +320,7 @@ static int fsl_asrc_m2m_comp_open(struct snd_compr_stream *stream)
if (ret)
goto error_alloc_out_buf;
- ret = pm_runtime_get_sync(dev);
+ ret = pm_runtime_resume_and_get(dev);
if (ret < 0) {
dev_err(dev, "Failed to power up asrc\n");
goto err_pm_runtime;
@@ -466,6 +466,7 @@ static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
struct snd_compr_runtime *runtime = stream->runtime;
struct fsl_asrc_pair *pair = runtime->private_data;
struct device *dev = &asrc->pdev->dev;
+ struct dma_chan *tmp_chan;
int ret;
exp_info_in.ops = &fsl_asrc_m2m_dma_buf_ops;
@@ -502,19 +503,21 @@ static int fsl_asrc_m2m_comp_task_create(struct snd_compr_stream *stream,
}
/* Request dma channels */
- pair->dma_chan[IN] = asrc->get_dma_channel(pair, IN);
- if (!pair->dma_chan[IN]) {
+ tmp_chan = asrc->get_dma_channel(pair, IN);
+ ret = PTR_ERR_OR_ZERO(tmp_chan);
+ if (ret) {
dev_err(dev, "[ctx%d] failed to get input DMA channel\n", pair->index);
- ret = -EBUSY;
goto err_dma_channel_in;
}
+ pair->dma_chan[IN] = tmp_chan;
- pair->dma_chan[OUT] = asrc->get_dma_channel(pair, OUT);
- if (!pair->dma_chan[OUT]) {
+ tmp_chan = asrc->get_dma_channel(pair, OUT);
+ ret = PTR_ERR_OR_ZERO(tmp_chan);
+ if (ret) {
dev_err(dev, "[ctx%d] failed to get output DMA channel\n", pair->index);
- ret = -EBUSY;
goto err_dma_channel_out;
}
+ pair->dma_chan[OUT] = tmp_chan;
return 0;
diff --git a/sound/soc/fsl/fsl_easrc.c b/sound/soc/fsl/fsl_easrc.c
index aced16d1228a..5d715bc07357 100644
--- a/sound/soc/fsl/fsl_easrc.c
+++ b/sound/soc/fsl/fsl_easrc.c
@@ -1433,7 +1433,7 @@ static struct dma_chan *fsl_easrc_get_dma_channel(struct fsl_asrc_pair *ctx,
/* Example of dma name: ctx0_rx */
sprintf(name, "ctx%c_%cx", index + '0', dir == IN ? 'r' : 't');
- return dma_request_slave_channel(&easrc->pdev->dev, name);
+ return dma_request_chan(&easrc->pdev->dev, name);
};
static const unsigned int easrc_rates[] = {
@@ -1582,45 +1582,106 @@ static int fsl_easrc_hw_free(struct snd_pcm_substream *substream,
return 0;
}
-static int fsl_easrc_dai_probe(struct snd_soc_dai *cpu_dai)
-{
- struct fsl_asrc *easrc = dev_get_drvdata(cpu_dai->dev);
-
- snd_soc_dai_init_dma_data(cpu_dai,
- &easrc->dma_params_tx,
- &easrc->dma_params_rx);
- return 0;
-}
-
static const struct snd_soc_dai_ops fsl_easrc_dai_ops = {
- .probe = fsl_easrc_dai_probe,
.startup = fsl_easrc_startup,
.trigger = fsl_easrc_trigger,
.hw_params = fsl_easrc_hw_params,
.hw_free = fsl_easrc_hw_free,
};
-static struct snd_soc_dai_driver fsl_easrc_dai = {
- .playback = {
- .stream_name = "ASRC-Playback",
- .channels_min = 1,
- .channels_max = 32,
- .rate_min = 8000,
- .rate_max = 768000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_EASRC_FORMATS,
+static struct snd_soc_dai_driver fsl_easrc_dai[] = {
+ {
+ .name = "easrc-0",
+ .playback = {
+ .stream_name = "ASRC-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
+ },
+ {
+ .name = "easrc-1",
+ .playback = {
+ .stream_name = "ASRC1-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC1-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
+ },
+ {
+ .name = "easrc-2",
+ .playback = {
+ .stream_name = "ASRC2-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC2-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
},
- .capture = {
- .stream_name = "ASRC-Capture",
- .channels_min = 1,
- .channels_max = 32,
- .rate_min = 8000,
- .rate_max = 768000,
- .rates = SNDRV_PCM_RATE_KNOT,
- .formats = FSL_EASRC_FORMATS |
- SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ {
+ .name = "easrc-3",
+ .playback = {
+ .stream_name = "ASRC3-Playback",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASRC3-Capture",
+ .channels_min = 1,
+ .channels_max = 32,
+ .rate_min = 8000,
+ .rate_max = 768000,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = FSL_EASRC_FORMATS |
+ SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE,
+ },
+ .ops = &fsl_easrc_dai_ops,
},
- .ops = &fsl_easrc_dai_ops,
};
static const struct snd_soc_component_driver fsl_easrc_component = {
@@ -2245,7 +2306,7 @@ static int fsl_easrc_probe(struct platform_device *pdev)
regcache_cache_only(easrc->regmap, true);
ret = devm_snd_soc_register_component(dev, &fsl_easrc_component,
- &fsl_easrc_dai, 1);
+ fsl_easrc_dai, ARRAY_SIZE(fsl_easrc_dai));
if (ret) {
dev_err(dev, "failed to register ASoC DAI\n");
goto err_pm_disable;
diff --git a/sound/soc/fsl/fsl_mqs.c b/sound/soc/fsl/fsl_mqs.c
index 901f840df904..5b00488253bb 100644
--- a/sound/soc/fsl/fsl_mqs.c
+++ b/sound/soc/fsl/fsl_mqs.c
@@ -183,7 +183,21 @@ static void fsl_mqs_shutdown(struct snd_pcm_substream *substream,
mqs_priv->soc->en_mask, 0);
}
+static const struct snd_soc_dapm_widget fsl_mqs_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("MQS_L"),
+ SND_SOC_DAPM_OUTPUT("MQS_R"),
+};
+
+static const struct snd_soc_dapm_route fsl_mqs_dapm_routes[] = {
+ { "MQS_L", NULL, "Playback" },
+ { "MQS_R", NULL, "Playback" },
+};
+
static const struct snd_soc_component_driver soc_codec_fsl_mqs = {
+ .dapm_widgets = fsl_mqs_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(fsl_mqs_dapm_widgets),
+ .dapm_routes = fsl_mqs_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(fsl_mqs_dapm_routes),
.idle_bias_on = 1,
};
diff --git a/sound/soc/fsl/fsl_xcvr.c b/sound/soc/fsl/fsl_xcvr.c
index 982827204351..29929dfcce2a 100644
--- a/sound/soc/fsl/fsl_xcvr.c
+++ b/sound/soc/fsl/fsl_xcvr.c
@@ -3,6 +3,7 @@
#include <linux/bitrev.h>
#include <linux/clk.h>
+#include <linux/devm-helpers.h>
#include <linux/firmware.h>
#include <linux/interrupt.h>
#include <linux/module.h>
@@ -57,6 +58,7 @@ struct fsl_xcvr {
struct snd_aes_iec958 tx_iec958;
u8 cap_ds[FSL_XCVR_CAPDS_SIZE];
struct work_struct work_rst;
+ int irq;
spinlock_t lock; /* Protect hw_reset and trigger */
struct snd_pcm_hw_constraint_list spdif_constr_rates;
u32 spdif_constr_rates_list[SPDIF_NUM_RATES];
@@ -1617,7 +1619,7 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
struct fsl_xcvr *xcvr;
struct resource *rx_res, *tx_res;
void __iomem *regs;
- int ret, irq;
+ int ret;
xcvr = devm_kzalloc(dev, sizeof(*xcvr), GFP_KERNEL);
if (!xcvr)
@@ -1705,12 +1707,22 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(xcvr->reset),
"failed to get XCVR reset control\n");
+ /*
+ * irq0_isr() schedules work_rst. Prepare the work and its lock
+ * before the IRQ, and register the cancel action first so a failed
+ * probe frees the IRQ and then cancels any queued work.
+ */
+ spin_lock_init(&xcvr->lock);
+ ret = devm_work_autocancel(dev, &xcvr->work_rst, reset_rx_work);
+ if (ret)
+ return ret;
+
/* get IRQs */
- irq = platform_get_irq(pdev, 0);
- if (irq < 0)
- return irq;
+ xcvr->irq = platform_get_irq(pdev, 0);
+ if (xcvr->irq < 0)
+ return xcvr->irq;
- ret = devm_request_irq(dev, irq, irq0_isr, 0, pdev->name, xcvr);
+ ret = devm_request_irq(dev, xcvr->irq, irq0_isr, 0, pdev->name, xcvr);
if (ret)
return dev_err_probe(dev, ret, "failed to claim IRQ0\n");
@@ -1751,8 +1763,6 @@ static int fsl_xcvr_probe(struct platform_device *pdev)
fsl_xcvr_comp.name);
}
- INIT_WORK(&xcvr->work_rst, reset_rx_work);
- spin_lock_init(&xcvr->lock);
return ret;
}
@@ -1760,6 +1770,8 @@ static void fsl_xcvr_remove(struct platform_device *pdev)
{
struct fsl_xcvr *xcvr = dev_get_drvdata(&pdev->dev);
+ /* Free the IRQ first so irq0_isr() cannot requeue work_rst. */
+ devm_free_irq(&pdev->dev, xcvr->irq, xcvr);
cancel_work_sync(&xcvr->work_rst);
pm_runtime_disable(&pdev->dev);
}
diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index 0a8a6d891d1f..127c2e6b308e 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -629,6 +629,7 @@ static int graph_probe(struct platform_device *pdev)
card->dapm_widgets = graph_dapm_widgets;
card->num_dapm_widgets = ARRAY_SIZE(graph_dapm_widgets);
card->probe = graph_util_card_probe;
+ card->remove = graph_util_card_remove;
if (of_device_get_match_data(dev))
priv->dpcm_selectable = 1;
diff --git a/sound/soc/generic/audio-graph-card2.c b/sound/soc/generic/audio-graph-card2.c
index 9fb3d3df5cf6..503170578cff 100644
--- a/sound/soc/generic/audio-graph-card2.c
+++ b/sound/soc/generic/audio-graph-card2.c
@@ -969,7 +969,8 @@ int audio_graph2_link_c2c(struct simple_util_priv *priv,
struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, li->link);
struct device_node *port0 = lnk;
struct device_node *ports __free(device_node) = port_to_ports(port0);
- struct device_node *port1 __free(device_node) = of_graph_get_next_port(ports, port0);
+ struct device_node *port1 __free(device_node) =
+ of_graph_get_next_port(ports, of_node_get(port0));
u32 val = 0;
int ret = -EINVAL;
@@ -1310,6 +1311,7 @@ int audio_graph2_parse_of(struct simple_util_priv *priv, struct device *dev,
goto end;
card->probe = graph_util_card_probe;
+ card->remove = graph_util_card_remove;
card->owner = THIS_MODULE;
card->dev = dev;
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 5f3423129b13..de27163eeb0b 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -825,6 +825,16 @@ int simple_util_init_jack(struct snd_soc_card *card,
}
EXPORT_SYMBOL_GPL(simple_util_init_jack);
+void simple_util_remove_jack(struct simple_util_jack *sjack)
+{
+ if (!sjack->gpio.desc)
+ return;
+
+ snd_soc_jack_free_gpios(&sjack->jack, 1, &sjack->gpio);
+ sjack->gpio.desc = NULL;
+}
+EXPORT_SYMBOL_GPL(simple_util_remove_jack);
+
int simple_util_init_aux_jacks(struct snd_soc_card *card, char *prefix)
{
struct simple_util_priv *priv = snd_soc_card_get_drvdata(card);
@@ -1018,6 +1028,17 @@ end:
}
EXPORT_SYMBOL_GPL(graph_util_card_probe);
+int graph_util_card_remove(struct snd_soc_card *card)
+{
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card);
+
+ simple_util_remove_jack(&priv->hp_jack);
+ simple_util_remove_jack(&priv->mic_jack);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(graph_util_card_remove);
+
int graph_util_is_ports0(struct device_node *np)
{
struct device_node *parent __free(device_node) = of_get_parent(np);
@@ -1116,7 +1137,7 @@ 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 char *dai_name = snd_soc_dai_name(dai);
const struct of_phandle_args *dai_args = snd_soc_copy_dai_args(dev, &args);
ret = -ENOMEM;
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index e7ad7af04714..b2fc91b8ab81 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -92,7 +92,7 @@ static int simple_parse_dai(struct simple_util_priv *priv,
dai = snd_soc_get_dai_via_args(&args);
if (dai) {
ret = -ENOMEM;
- dlc->dai_name = snd_soc_dai_name_get(dai);
+ dlc->dai_name = snd_soc_dai_name(dai);
dlc->dai_args = snd_soc_copy_dai_args(dev, &args);
if (!dlc->dai_args)
goto end;
@@ -666,6 +666,16 @@ end:
return simple_ret(priv, ret);
}
+static int simple_soc_remove(struct snd_soc_card *card)
+{
+ struct simple_util_priv *priv = snd_soc_card_get_drvdata(card);
+
+ simple_util_remove_jack(&priv->hp_jack);
+ simple_util_remove_jack(&priv->mic_jack);
+
+ return 0;
+}
+
static int simple_parse_of(struct simple_util_priv *priv)
{
struct snd_soc_card *card = simple_priv_to_card(priv);
@@ -754,6 +764,7 @@ static int simple_probe(struct platform_device *pdev)
card->owner = THIS_MODULE;
card->dev = dev;
card->probe = simple_soc_probe;
+ card->remove = simple_soc_remove;
card->driver_name = "simple-card";
return simple_parse_of(priv);
diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c
index 33b1dafb2540..554356f060b0 100644
--- a/sound/soc/hisilicon/hi6210-i2s.c
+++ b/sound/soc/hisilicon/hi6210-i2s.c
@@ -507,6 +507,11 @@ static int hi6210_i2s_dai_probe(struct snd_soc_dai *dai)
}
+static const u64 hi6210_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops hi6210_i2s_dai_ops = {
.probe = hi6210_i2s_dai_probe,
.trigger = hi6210_i2s_trigger,
@@ -514,6 +519,8 @@ static const struct snd_soc_dai_ops hi6210_i2s_dai_ops = {
.set_fmt = hi6210_i2s_set_fmt,
.startup = hi6210_i2s_startup,
.shutdown = hi6210_i2s_shutdown,
+ .auto_selectable_formats = &hi6210_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_driver hi6210_i2s_dai_init = {
diff --git a/sound/soc/img/img-i2s-in.c b/sound/soc/img/img-i2s-in.c
index 98d2ab68883a..a0c87e2a9b30 100644
--- a/sound/soc/img/img-i2s-in.c
+++ b/sound/soc/img/img-i2s-in.c
@@ -379,11 +379,21 @@ static int img_i2s_in_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 img_i2s_in_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops img_i2s_in_dai_ops = {
.probe = img_i2s_in_dai_probe,
.trigger = img_i2s_in_trigger,
.hw_params = img_i2s_in_hw_params,
- .set_fmt = img_i2s_in_set_fmt
+ .set_fmt = img_i2s_in_set_fmt,
+ .auto_selectable_formats = &img_i2s_in_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_component_driver img_i2s_in_component = {
diff --git a/sound/soc/img/img-i2s-out.c b/sound/soc/img/img-i2s-out.c
index aedcf56a5359..caa7870dbec6 100644
--- a/sound/soc/img/img-i2s-out.c
+++ b/sound/soc/img/img-i2s-out.c
@@ -385,11 +385,22 @@ static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 img_i2s_out_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_CONT |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
.probe = img_i2s_out_dai_probe,
.trigger = img_i2s_out_trigger,
.hw_params = img_i2s_out_hw_params,
- .set_fmt = img_i2s_out_set_fmt
+ .set_fmt = img_i2s_out_set_fmt,
+ .auto_selectable_formats = &img_i2s_out_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_component_driver img_i2s_out_component = {
diff --git a/sound/soc/img/img-parallel-out.c b/sound/soc/img/img-parallel-out.c
index 86b60e6dab38..89270913ab35 100644
--- a/sound/soc/img/img-parallel-out.c
+++ b/sound/soc/img/img-parallel-out.c
@@ -183,11 +183,17 @@ static int img_prl_out_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 img_prl_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF;
+
static const struct snd_soc_dai_ops img_prl_out_dai_ops = {
.probe = img_prl_out_dai_probe,
.trigger = img_prl_out_trigger,
.hw_params = img_prl_out_hw_params,
- .set_fmt = img_prl_out_set_fmt
+ .set_fmt = img_prl_out_set_fmt,
+ .auto_selectable_formats = &img_prl_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver img_prl_out_dai = {
diff --git a/sound/soc/intel/atom/sst-mfld-dsp.h b/sound/soc/intel/atom/sst-mfld-dsp.h
index c8f0816edb53..59606259a06e 100644
--- a/sound/soc/intel/atom/sst-mfld-dsp.h
+++ b/sound/soc/intel/atom/sst-mfld-dsp.h
@@ -189,7 +189,7 @@ struct ipc_dsp_hdr {
union ipc_header_high {
struct {
u32 msg_id:8; /* Message ID - Max 256 Message Types */
- u32 task_id:4; /* Task ID associated with this comand */
+ u32 task_id:4; /* Task ID associated with this command */
u32 drv_id:4; /* Identifier for the driver to track*/
u32 rsvd1:8; /* Reserved */
u32 result:4; /* Reserved */
diff --git a/sound/soc/intel/atom/sst/sst.h b/sound/soc/intel/atom/sst/sst.h
index c43946c5ecee..e24d9dad7ef6 100644
--- a/sound/soc/intel/atom/sst/sst.h
+++ b/sound/soc/intel/atom/sst/sst.h
@@ -182,7 +182,7 @@ struct sst_block {
* @pcm_substream : PCM substream
* @period_elapsed : PCM period elapsed callback
* @sfreq : stream sampling freq
- * @cumm_bytes : cummulative bytes decoded
+ * @cumm_bytes : cumulative bytes decoded
*/
struct stream_info {
unsigned int status;
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 73624e1b138a..de1ad168008b 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -254,6 +254,11 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
return 0;
}
+static void sst_unregister_platform_device(void *data)
+{
+ platform_device_unregister(data);
+}
+
static int sst_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -342,7 +347,10 @@ static int sst_acpi_probe(struct platform_device *pdev)
pdata->platform);
return PTR_ERR(plat_dev);
}
-
+ ret = devm_add_action_or_reset(dev, sst_unregister_platform_device,
+ plat_dev);
+ if (ret)
+ return ret;
/*
* Create platform device for sst machine driver,
* pass machine info as pdata
@@ -355,6 +363,10 @@ static int sst_acpi_probe(struct platform_device *pdev)
return PTR_ERR(mdev);
}
+ ret = devm_add_action_or_reset(dev, sst_unregister_platform_device,
+ mdev);
+ if (ret)
+ return ret;
/* Fill sst platform data */
ctx->pdata = pdata;
strscpy(ctx->firmware_name, mach->fw_filename);
diff --git a/sound/soc/intel/avs/boards/hdaudio.c b/sound/soc/intel/avs/boards/hdaudio.c
index 2e15a293298d..c1d2d0082b14 100644
--- a/sound/soc/intel/avs/boards/hdaudio.c
+++ b/sound/soc/intel/avs/boards/hdaudio.c
@@ -147,17 +147,15 @@ static int avs_probing_link_init(struct snd_soc_pcm_runtime *rtm)
struct snd_soc_dai_link *links = NULL;
struct snd_soc_card *card = rtm->card;
struct hda_codec *codec;
- struct hda_pcm *pcm;
- int ret, pcm_count = 0;
+ int ret, pcm_count;
mach = dev_get_platdata(card->dev);
pdata = mach->pdata;
codec = pdata->codec;
+ pcm_count = list_count_nodes(&codec->pcm_list_head);
- if (list_empty(&codec->pcm_list_head))
+ if (!pcm_count)
return -EINVAL;
- list_for_each_entry(pcm, &codec->pcm_list_head, list)
- pcm_count++;
ret = avs_create_dai_links(card->dev, codec, pcm_count, &links);
if (ret < 0) {
diff --git a/sound/soc/intel/avs/pcm.c b/sound/soc/intel/avs/pcm.c
index ad25bd355769..2d7ced953fde 100644
--- a/sound/soc/intel/avs/pcm.c
+++ b/sound/soc/intel/avs/pcm.c
@@ -1065,7 +1065,7 @@ finalize:
return 0;
err_load_libs:
- avs_remove_topology(component);
+ snd_soc_tplg_component_remove(component);
return ret;
}
@@ -1083,7 +1083,7 @@ static void avs_component_remove(struct snd_soc_component *component)
mutex_unlock(&adev->comp_list_mutex);
if (mach->tplg_filename) {
- ret = avs_remove_topology(component);
+ ret = snd_soc_tplg_component_remove(component);
if (ret < 0)
dev_err(component->dev, "unload topology failed: %d\n", ret);
}
@@ -1614,7 +1614,7 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
struct hda_codec *codec;
struct hda_pcm *pcm;
const char *cname;
- int pcm_count = 0, ret, i;
+ int pcm_count, ret, i;
mach = dev_get_platdata(component->card->dev);
if (!mach)
@@ -1622,10 +1622,9 @@ static int avs_component_hda_probe(struct snd_soc_component *component)
pdata = mach->pdata;
codec = pdata->codec;
- if (list_empty(&codec->pcm_list_head))
+ pcm_count = list_count_nodes(&codec->pcm_list_head);
+ if (!pcm_count)
return -EINVAL;
- list_for_each_entry(pcm, &codec->pcm_list_head, list)
- pcm_count++;
dais = devm_kcalloc(component->dev, pcm_count, sizeof(*dais),
GFP_KERNEL);
diff --git a/sound/soc/intel/avs/topology.c b/sound/soc/intel/avs/topology.c
index 5d70be63a4a7..1085090d5a9c 100644
--- a/sound/soc/intel/avs/topology.c
+++ b/sound/soc/intel/avs/topology.c
@@ -2226,28 +2226,3 @@ struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp)
return tplg;
}
-
-int avs_load_topology(struct snd_soc_component *comp, const char *filename)
-{
- const struct firmware *fw __free(firmware) = NULL;
- int ret;
-
- ret = request_firmware(&fw, filename, comp->dev);
- if (ret < 0) {
- dev_err(comp->dev, "request topology \"%s\" failed: %d\n", filename, ret);
- return ret;
- }
-
- ret = snd_soc_tplg_component_load(comp, &avs_tplg_ops, fw);
- if (ret < 0)
- dev_err(comp->dev, "load topology \"%s\" failed: %d\n", filename, ret);
-
- return ret;
-}
-
-int avs_remove_topology(struct snd_soc_component *comp)
-{
- snd_soc_tplg_component_remove(comp);
-
- return 0;
-}
diff --git a/sound/soc/intel/avs/topology.h b/sound/soc/intel/avs/topology.h
index 189984ce7b51..25f3888a1939 100644
--- a/sound/soc/intel/avs/topology.h
+++ b/sound/soc/intel/avs/topology.h
@@ -233,7 +233,4 @@ struct avs_tplg_module {
extern const struct snd_soc_tplg_ops avs_tplg_ops;
struct avs_tplg *avs_tplg_new(struct snd_soc_component *comp);
-int avs_load_topology(struct snd_soc_component *comp, const char *filename);
-int avs_remove_topology(struct snd_soc_component *comp);
-
#endif
diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig
index b795bcade8e9..919d3f5f5328 100644
--- a/sound/soc/intel/boards/Kconfig
+++ b/sound/soc/intel/boards/Kconfig
@@ -536,6 +536,7 @@ config SND_SOC_INTEL_SOUNDWIRE_SOF_MACH
imply SND_SOC_TAC5XX2_SDW
select SND_SOC_DMIC
select SND_SOC_INTEL_HDA_DSP_COMMON
+ select SND_SOC_SN624X_SDCA_SDW
imply SND_SOC_SDW_MOCKUP
help
Add support for Intel SoundWire-based platforms connected to
diff --git a/sound/soc/intel/boards/sof_cirrus_common.c b/sound/soc/intel/boards/sof_cirrus_common.c
index a4ecb1f9a615..c7e77e20a096 100644
--- a/sound/soc/intel/boards/sof_cirrus_common.c
+++ b/sound/soc/intel/boards/sof_cirrus_common.c
@@ -151,7 +151,7 @@ static const char * const cs35l41_name_prefixes[] = { "WL", "WR", "TL", "TR" };
* UID 0x2 -> TL
* UID 0x3 -> TR
* Note: If there are less than 4 Amps, UIDs still map to WL/WR/TL/TR. Dynamic code will only create
- * dai links for UIDs which exist, and ignore non-existant ones. Only 2 or 4 amps are expected.
+ * dai links for UIDs which exist, and ignore non-existent ones. Only 2 or 4 amps are expected.
* Return number of codecs found.
*/
static int cs35l41_compute_codec_conf(void)
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 1033f2d45c8a..575889a4c69b 100644
--- a/sound/soc/intel/common/soc-acpi-intel-arl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-arl-match.c
@@ -531,6 +531,63 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_machines[] = {
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_arl_machines);
/* this table is used when there is no I2S codec present */
+
+/*
+ * Senary SN624x on Arrow Lake: one multi-function device on link 0.
+ * Separate mach entries per part ID (adr_d[] is AND, not OR). Amp endpoint
+ * is aggregated like the generic SOC_SDW_DAI_TYPE_AMP shape from
+ * find_acpi_adr_device().
+ */
+static const struct snd_soc_acpi_endpoint sn624x_endpoints[] = {
+ /* Jack */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+ },
+ /* DMIC */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+#define SN624X_ARL_SINGLE_ADR(_sym, _adr) \
+static const struct snd_soc_acpi_adr_device _sym[] = { \
+ { \
+ .adr = (_adr), \
+ .num_endpoints = ARRAY_SIZE(sn624x_endpoints), \
+ .endpoints = sn624x_endpoints, \
+ .name_prefix = "sn624x", \
+ }, \
+}
+
+#define SN624X_ARL_LINK(_sym, _adr_arr) \
+static const struct snd_soc_acpi_link_adr _sym[] = { \
+ { \
+ .mask = BIT(0), \
+ .num_adr = ARRAY_SIZE(_adr_arr), \
+ .adr_d = _adr_arr, \
+ }, \
+ {} \
+}
+
+SN624X_ARL_SINGLE_ADR(sn6242_single_adr, 0x0000000496624201ull);
+SN624X_ARL_SINGLE_ADR(sn6244_single_adr, 0x0000000496624401ull);
+
+SN624X_ARL_LINK(arl_link_sn6242, sn6242_single_adr);
+SN624X_ARL_LINK(arl_link_sn6244, sn6244_single_adr);
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
{
.link_mask = BIT(0) | BIT(2) | BIT(3),
@@ -624,6 +681,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_arl_sdw_machines[] = {
.sof_tplg_filename = "sof-arl-es9356.tplg",
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
+ {
+ .link_mask = BIT(0),
+ .links = arl_link_sn6242,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = arl_link_sn6244,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-arl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_arl_sdw_machines);
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 62b8811395cb..cbf79eb2ec00 100644
--- a/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-lnl-match.c
@@ -685,6 +685,63 @@ static const struct snd_soc_acpi_link_adr lnl_sdw_rt712_vb_l2_rt1320_l1[] = {
};
/* this table is used when there is no I2S codec present */
+
+/*
+ * Senary SN624x on Lunar Lake: one multi-function device on link 0.
+ * Separate mach entries per part ID (adr_d[] is AND, not OR). Amp endpoint
+ * is aggregated like the generic SOC_SDW_DAI_TYPE_AMP shape from
+ * find_acpi_adr_device().
+ */
+static const struct snd_soc_acpi_endpoint sn624x_endpoints[] = {
+ /* Jack */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+ },
+ /* DMIC */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+#define SN624X_LNL_SINGLE_ADR(_sym, _adr) \
+static const struct snd_soc_acpi_adr_device _sym[] = { \
+ { \
+ .adr = (_adr), \
+ .num_endpoints = ARRAY_SIZE(sn624x_endpoints), \
+ .endpoints = sn624x_endpoints, \
+ .name_prefix = "sn624x", \
+ }, \
+}
+
+#define SN624X_LNL_LINK(_sym, _adr_arr) \
+static const struct snd_soc_acpi_link_adr _sym[] = { \
+ { \
+ .mask = BIT(0), \
+ .num_adr = ARRAY_SIZE(_adr_arr), \
+ .adr_d = _adr_arr, \
+ }, \
+ {} \
+}
+
+SN624X_LNL_SINGLE_ADR(sn6242_single_adr, 0x0000000496624201ull);
+SN624X_LNL_SINGLE_ADR(sn6244_single_adr, 0x0000000496624401ull);
+
+SN624X_LNL_LINK(lnl_link_sn6242, sn6242_single_adr);
+SN624X_LNL_LINK(lnl_link_sn6244, sn6244_single_adr);
+
/* this table is used when there is no I2S codec present */
struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
/* mockup tests need to be first */
@@ -806,6 +863,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_lnl_sdw_machines[] = {
.sof_tplg_filename = "sof-lnl-rt713-l2-rt1320-l13.tplg",
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
+ {
+ .link_mask = BIT(0),
+ .links = lnl_link_sn6242,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = lnl_link_sn6244,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-lnl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_lnl_sdw_machines);
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 596582053d93..e89c5cfd6ed1 100644
--- a/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-mtl-match.c
@@ -1296,6 +1296,63 @@ static const struct snd_soc_acpi_link_adr mtl_cs35l63_x2_link1_link3_fb[] = {
};
/* this table is used when there is no I2S codec present */
+
+/*
+ * Senary SN624x on Meteor Lake: one multi-function device on link 0.
+ * Separate mach entries per part ID (adr_d[] is AND, not OR). Amp endpoint
+ * is aggregated like the generic SOC_SDW_DAI_TYPE_AMP shape from
+ * find_acpi_adr_device().
+ */
+static const struct snd_soc_acpi_endpoint sn624x_endpoints[] = {
+ /* Jack */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+ },
+ /* DMIC */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+#define SN624X_MTL_SINGLE_ADR(_sym, _adr) \
+static const struct snd_soc_acpi_adr_device _sym[] = { \
+ { \
+ .adr = (_adr), \
+ .num_endpoints = ARRAY_SIZE(sn624x_endpoints), \
+ .endpoints = sn624x_endpoints, \
+ .name_prefix = "sn624x", \
+ }, \
+}
+
+#define SN624X_MTL_LINK(_sym, _adr_arr) \
+static const struct snd_soc_acpi_link_adr _sym[] = { \
+ { \
+ .mask = BIT(0), \
+ .num_adr = ARRAY_SIZE(_adr_arr), \
+ .adr_d = _adr_arr, \
+ }, \
+ {} \
+}
+
+SN624X_MTL_SINGLE_ADR(sn6242_single_adr, 0x0000000496624201ull);
+SN624X_MTL_SINGLE_ADR(sn6244_single_adr, 0x0000000496624401ull);
+
+SN624X_MTL_LINK(mtl_link_sn6242, sn6242_single_adr);
+SN624X_MTL_LINK(mtl_link_sn6244, sn6244_single_adr);
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
/* mockup tests need to be first */
{
@@ -1503,6 +1560,20 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_mtl_sdw_machines[] = {
.sof_tplg_filename = "sof-mtl-sdw-cs42l42-l0-max98363-l2.tplg",
.get_function_tplg_files = sof_sdw_get_tplg_files,
},
+ {
+ .link_mask = BIT(0),
+ .links = mtl_link_sn6242,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(0),
+ .links = mtl_link_sn6244,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-mtl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_mtl_sdw_machines);
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 756bbf82a326..0ce51c44bfe6 100644
--- a/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
+++ b/sound/soc/intel/common/soc-acpi-intel-ptl-match.c
@@ -51,12 +51,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = {
.drv_name = "ptl_es83x6_c1_h02",
.machine_quirk = snd_soc_acpi_codec_list,
.quirk_data = &ptl_lt6911_hdmi,
+ .get_function_tplg_files = sof_i2s_get_tplg_files,
.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 */
+ .get_function_tplg_files = sof_i2s_get_tplg_files,
.tplg_quirk_mask = SND_SOC_ACPI_TPLG_INTEL_SSP_NUMBER |
SND_SOC_ACPI_TPLG_INTEL_SSP_MSB |
SND_SOC_ACPI_TPLG_INTEL_DMIC_NUMBER,
@@ -65,6 +67,7 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_machines[] = {
{
.id = "INTC10B0",
.drv_name = "ptl_lt6911_hdmi_ssp",
+ .get_function_tplg_files = sof_i2s_get_tplg_files,
.sof_tplg_filename = "sof-ptl-hdmi-ssp02.tplg",
},
{},
@@ -438,6 +441,66 @@ static const struct snd_soc_acpi_link_adr ptl_sdw_rt712_vb_l3_rt1320_l3[] = {
};
/* this table is used when there is no I2S codec present */
+
+/*
+ * Senary SN624x on Panther Lake: one multi-function device on link 3.
+ * Separate mach entries per part ID (adr_d[] is AND, not OR). ADR version
+ * nibble 0x3 matches the silicon SoundWire version. Amp endpoint is
+ * aggregated like the generic SOC_SDW_DAI_TYPE_AMP shape from
+ * find_acpi_adr_device().
+ */
+static const struct snd_soc_acpi_endpoint sn624x_endpoints[] = {
+ /* Jack */
+ {
+ .num = 0,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+ /* Amp */
+ {
+ .num = 1,
+ .aggregated = 1,
+ .group_position = 1,
+ .group_id = 1,
+ },
+ /* DMIC */
+ {
+ .num = 2,
+ .aggregated = 0,
+ .group_position = 0,
+ .group_id = 0,
+ },
+};
+
+#define SN624X_PTL_SINGLE_ADR(_sym, _adr) \
+static const struct snd_soc_acpi_adr_device _sym[] = { \
+ { \
+ .adr = (_adr), \
+ .num_endpoints = ARRAY_SIZE(sn624x_endpoints), \
+ .endpoints = sn624x_endpoints, \
+ .name_prefix = "sn624x", \
+ }, \
+}
+
+#define SN624X_PTL_LINK(_sym, _adr_arr) \
+static const struct snd_soc_acpi_link_adr _sym[] = { \
+ { \
+ .mask = BIT(3), \
+ .num_adr = ARRAY_SIZE(_adr_arr), \
+ .adr_d = _adr_arr, \
+ }, \
+ {} \
+}
+
+SN624X_PTL_SINGLE_ADR(sn6242_single_adr, 0x0003300496624201ull);
+SN624X_PTL_SINGLE_ADR(sn6244_single_adr, 0x0003300496624401ull);
+SN624X_PTL_SINGLE_ADR(sn6247_single_adr, 0x0003300496624701ull);
+
+SN624X_PTL_LINK(ptl_link_sn6242, sn6242_single_adr);
+SN624X_PTL_LINK(ptl_link_sn6244, sn6244_single_adr);
+SN624X_PTL_LINK(ptl_link_sn6247, sn6247_single_adr);
+
struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
/* Order Priority: mockup > most links > most bit link-mask > alphabetical */
{
@@ -531,6 +594,28 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_ptl_sdw_machines[] = {
.machine_check = snd_soc_acpi_intel_rt712_vb_no_function_topology,
.sof_tplg_filename = "sof-ptl-rt712-l3-rt1320-l3.tplg",
},
+
+ {
+ .link_mask = BIT(3),
+ .links = ptl_link_sn6242,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(3),
+ .links = ptl_link_sn6244,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
+ {
+ .link_mask = BIT(3),
+ .links = ptl_link_sn6247,
+ .drv_name = "sof_sdw",
+ .sof_tplg_filename = "sof-ptl-sn624x.tplg",
+ .get_function_tplg_files = sof_sdw_get_tplg_files,
+ },
{},
};
EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_ptl_sdw_machines);
diff --git a/sound/soc/intel/common/sof-function-topology-lib.c b/sound/soc/intel/common/sof-function-topology-lib.c
index b6e5a40b78cc..57e089f8e721 100644
--- a/sound/soc/intel/common/sof-function-topology-lib.c
+++ b/sound/soc/intel/common/sof-function-topology-lib.c
@@ -19,6 +19,10 @@ enum tplg_device_id {
TPLG_DEVICE_SDCA_MIC,
TPLG_DEVICE_INTEL_PCH_DMIC,
TPLG_DEVICE_HDMI,
+ TPLG_DEVICE_SSP_JACK,
+ TPLG_DEVICE_SSP_AMP,
+ TPLG_DEVICE_SSP_BT,
+ TPLG_DEVICE_SSP_HDMI_IN,
TPLG_DEVICE_LOOPBACK_VIRTUAL,
TPLG_DEVICE_MAX
};
@@ -28,6 +32,137 @@ enum tplg_device_id {
#define SOF_INTEL_PLATFORM_NAME_MAX 4
+static int get_platform_name(struct snd_soc_card *card,
+ const struct snd_soc_acpi_mach *mach, char *platform)
+{
+ int ret;
+
+ ret = sscanf(mach->sof_tplg_filename, "sof-%3s-*.tplg", platform);
+ if (ret != 1) {
+ dev_err(card->dev, "Invalid platform name of tplg %s\n",
+ mach->sof_tplg_filename);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static bool tplg_files_exist(struct device *dev, const char *tplg_files)
+{
+ const struct firmware *fw;
+ int ret;
+
+ ret = firmware_request_nowarn(&fw, tplg_files, dev);
+ if (!ret) {
+ release_firmware(fw);
+ return true;
+ }
+
+ dev_warn(dev,
+ "Failed to open topology file: %s, you might need to\n",
+ tplg_files);
+ dev_warn(dev,
+ "download it from https://github.com/thesofproject/sof-bin/\n");
+ return false;
+}
+
+static char *get_tplg_filename(struct device *dev, const char *prefix,
+ const char *platform, const char *tplg_dev_name,
+ int dai_link_id, int tplg_dev)
+{
+ char *filename = NULL;
+
+ /*
+ * The tplg file naming rule is sof-<platform>-<function>-id<BE id number>.tplg
+ * where <platform> is required for functions that depend on NHLT blobs (e.g. DMIC/SSP)
+ * as the nhlt blob is platform dependent.
+ */
+ switch (tplg_dev) {
+ case TPLG_DEVICE_INTEL_PCH_DMIC:
+ case TPLG_DEVICE_SSP_JACK:
+ case TPLG_DEVICE_SSP_AMP:
+ case TPLG_DEVICE_SSP_BT:
+ case TPLG_DEVICE_SSP_HDMI_IN:
+ filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-%s-id%d.tplg",
+ prefix, platform, tplg_dev_name, dai_link_id);
+ break;
+ default:
+ filename = devm_kasprintf(dev, GFP_KERNEL, "%s/sof-%s-id%d.tplg",
+ prefix, tplg_dev_name, dai_link_id);
+ break;
+ }
+
+ return filename;
+}
+
+static int get_dmic_tplg_dev(struct device *dev, int dmic_num,
+ int *tplg_dev, char **tplg_dev_name)
+{
+ switch (dmic_num) {
+ case 2:
+ *tplg_dev_name = "dmic-2ch";
+ break;
+ case 4:
+ *tplg_dev_name = "dmic-4ch";
+ break;
+ default:
+ dev_warn(dev,
+ "unsupported number of dmics: %d\n",
+ dmic_num);
+ return -EINVAL;
+ }
+ *tplg_dev = TPLG_DEVICE_INTEL_PCH_DMIC;
+
+ return 0;
+}
+
+static int get_ssp_tplg_dev(struct device *dev, struct snd_soc_dai_link *dai_link,
+ u16 *hdmi_in_mask, int *tplg_dev, char **tplg_dev_name)
+{
+ unsigned int ssp_port;
+
+ if (sscanf(dai_link->name, "SSP%u", &ssp_port) != 1) {
+ dev_err(dev, "Can't get SSP port from dai_link->name %s\n", dai_link->name);
+ return -EINVAL;
+ }
+ if (strstr(dai_link->name, "Codec")) {
+ /*
+ * Assume DAI link 0 is jack which is true in all existing
+ * machine drivers
+ */
+ if (dai_link->id == 0) {
+ *tplg_dev = TPLG_DEVICE_SSP_JACK;
+ *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
+ "ssp%d-jack", ssp_port);
+ } else {
+ *tplg_dev = TPLG_DEVICE_SSP_AMP;
+ *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
+ "ssp%d-amp", ssp_port);
+ }
+ } else if (strstr(dai_link->name, "BT")) {
+ *tplg_dev = TPLG_DEVICE_SSP_BT;
+ *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
+ "ssp%d-bt", ssp_port);
+ } else if (strstr(dai_link->name, "HDMI")) {
+ *hdmi_in_mask |= BIT(ssp_port);
+ /* The number of HDMI in dai link is always 2 right now */
+ if (hweight16(*hdmi_in_mask) != 2)
+ return -EINVAL;
+
+ *tplg_dev = TPLG_DEVICE_SSP_HDMI_IN;
+ *tplg_dev_name = devm_kasprintf(dev, GFP_KERNEL,
+ "ssp%x-hdmiin", *hdmi_in_mask);
+ } else {
+ dev_warn(dev,
+ "unsupported SSP link %s\n", dai_link->name);
+ return -EINVAL;
+ }
+ if (!*tplg_dev_name)
+ return -ENOMEM;
+
+ return 0;
+}
+
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, bool best_effort)
{
@@ -38,20 +173,18 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_
*/
struct snd_soc_acpi_mach_params mach_params = card_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;
+ u16 hdmi_in_mask = 0;
int tplg_num = 0;
+ char *tplg_file;
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;
- }
+ ret = get_platform_name(card, mach, platform);
+ if (ret < 0)
+ return ret;
for_each_card_prelinks(card, i, dai_link) {
char *tplg_dev_name;
@@ -70,23 +203,16 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_
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);
+ if (get_dmic_tplg_dev(card->dev, mach_params.dmic_num,
+ &tplg_dev, &tplg_dev_name) < 0)
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 if (strstr(dai_link->name, "SSP")) {
+ if (get_ssp_tplg_dev(card->dev, dai_link, &hdmi_in_mask,
+ &tplg_dev, &tplg_dev_name) < 0)
+ continue;
} else if (strstr(dai_link->name, "Loopback_Virtual")) {
tplg_dev = TPLG_DEVICE_LOOPBACK_VIRTUAL;
/*
@@ -109,49 +235,100 @@ int sof_sdw_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_
if (tplg_mask & BIT(tplg_dev))
continue;
- tplg_mask |= BIT(tplg_dev);
+ tplg_file = get_tplg_filename(card->dev, prefix, platform, tplg_dev_name,
+ dai_link->id, tplg_dev);
+ if (!tplg_file)
+ return -ENOMEM;
- /*
- * 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;
+ /* Check presence of sub-topologies */
+ if (!tplg_files_exist(card->dev, tplg_file)) {
+ devm_kfree(card->dev, tplg_file);
+ if (best_effort)
+ continue;
+
+ return 0;
}
- if (!(*tplg_files)[tplg_num])
- return -ENOMEM;
+
+ tplg_mask |= BIT(tplg_dev);
+
+ (*tplg_files)[tplg_num] = tplg_file;
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);
+ return tplg_num;
+}
+EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files);
+
+int sof_i2s_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
+ const char *prefix, const char ***tplg_files, bool best_effort)
+{
+ struct snd_soc_acpi_mach_params mach_params = mach->mach_params;
+ struct snd_soc_dai_link *dai_link;
+ char platform[SOF_INTEL_PLATFORM_NAME_MAX];
+ unsigned long tplg_mask = 0;
+ u16 hdmi_in_mask = 0;
+ int tplg_num = 0;
+ char *tplg_file;
+ int tplg_dev;
+ int ret;
+ int i;
+
+ ret = get_platform_name(card, mach, platform);
+ if (ret < 0)
+ return ret;
+
+ 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, "SSP")) {
+ if (get_ssp_tplg_dev(card->dev, dai_link, &hdmi_in_mask,
+ &tplg_dev, &tplg_dev_name) < 0)
+ continue;
+ } else if (strstr(dai_link->name, "dmic")) {
+ if (get_dmic_tplg_dev(card->dev, mach_params.dmic_num,
+ &tplg_dev, &tplg_dev_name) < 0)
+ continue;
+ } else if (strstr(dai_link->name, "iDisp")) {
+ tplg_dev = TPLG_DEVICE_HDMI;
+ tplg_dev_name = "hdmi-pcm5";
} else {
- dev_warn(card->dev,
- "Failed to open topology file: %s, you might need to\n",
- (*tplg_files)[i]);
- dev_warn(card->dev,
- "download it from https://github.com/thesofproject/sof-bin/\n");
+ /* 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);
+ if (best_effort)
+ continue;
+
+ return 0;
+ }
+ if (tplg_mask & BIT(tplg_dev))
+ continue;
+
+ tplg_file = get_tplg_filename(card->dev, prefix, platform, tplg_dev_name,
+ dai_link->id, tplg_dev);
+ if (!tplg_file)
+ return -ENOMEM;
+
+ /* Check presence of sub-topologies */
+ if (!tplg_files_exist(card->dev, tplg_file)) {
+ devm_kfree(card->dev, tplg_file);
+ if (best_effort)
+ continue;
+
return 0;
}
+
+ tplg_mask |= BIT(tplg_dev);
+
+ (*tplg_files)[tplg_num] = tplg_file;
+ tplg_num++;
}
+ dev_dbg(card->dev, "tplg_mask %#lx tplg_num %d\n", tplg_mask, tplg_num);
+
return tplg_num;
}
-EXPORT_SYMBOL_GPL(sof_sdw_get_tplg_files);
+EXPORT_SYMBOL_GPL(sof_i2s_get_tplg_files);
diff --git a/sound/soc/intel/common/sof-function-topology-lib.h b/sound/soc/intel/common/sof-function-topology-lib.h
index f358f8c52d78..9755e9770968 100644
--- a/sound/soc/intel/common/sof-function-topology-lib.h
+++ b/sound/soc/intel/common/sof-function-topology-lib.h
@@ -12,4 +12,7 @@
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, bool best_effort);
+int sof_i2s_get_tplg_files(struct snd_soc_card *card, const struct snd_soc_acpi_mach *mach,
+ const char *prefix, const char ***tplg_files, bool best_effort);
+
#endif
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index d36bbc820618..7a1589cb775a 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -326,6 +326,11 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 jz4740_i2s_selectable_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 jz4740_i2s_dai_ops = {
.probe = jz4740_i2s_dai_probe,
.startup = jz4740_i2s_startup,
@@ -333,6 +338,8 @@ static const struct snd_soc_dai_ops jz4740_i2s_dai_ops = {
.trigger = jz4740_i2s_trigger,
.hw_params = jz4740_i2s_hw_params,
.set_fmt = jz4740_i2s_set_fmt,
+ .auto_selectable_formats = &jz4740_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define JZ4740_I2S_FMTS (SNDRV_PCM_FMTBIT_S8 | \
diff --git a/sound/soc/kirkwood/kirkwood-i2s.c b/sound/soc/kirkwood/kirkwood-i2s.c
index 99bd066c7309..739a819d8494 100644
--- a/sound/soc/kirkwood/kirkwood-i2s.c
+++ b/sound/soc/kirkwood/kirkwood-i2s.c
@@ -544,11 +544,18 @@ static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
}
+static const u64 kirkwood_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
.startup = kirkwood_i2s_startup,
.trigger = kirkwood_i2s_trigger,
.hw_params = kirkwood_i2s_hw_params,
.set_fmt = kirkwood_i2s_set_fmt,
+ .auto_selectable_formats = &kirkwood_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver kirkwood_i2s_dai[2] = {
diff --git a/sound/soc/loongson/loongson_i2s.c b/sound/soc/loongson/loongson_i2s.c
index cfe102a8b604..da0d029c028a 100644
--- a/sound/soc/loongson/loongson_i2s.c
+++ b/sound/soc/loongson/loongson_i2s.c
@@ -220,12 +220,18 @@ static int loongson_i2s_dai_probe(struct snd_soc_dai *cpu_dai)
return 0;
}
+static const u64 loongson_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J;
+
static const struct snd_soc_dai_ops loongson_i2s_dai_ops = {
.probe = loongson_i2s_dai_probe,
.trigger = loongson_i2s_trigger,
.hw_params = loongson_i2s_hw_params,
.set_sysclk = loongson_i2s_set_dai_sysclk,
.set_fmt = loongson_i2s_set_fmt,
+ .auto_selectable_formats = &loongson_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
struct snd_soc_dai_driver loongson_i2s_dai = {
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 224746e7664f..ff2869641a35 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,10 +1,35 @@
# SPDX-License-Identifier: GPL-2.0-only
-menu "Mediatek"
config SND_SOC_MEDIATEK
tristate
select REGMAP_MMIO
+menu "Airoha"
+
+config SND_SOC_AN7581
+ tristate "ASoC support for Airoha AN7581 chip"
+ depends on ARCH_AIROHA || COMPILE_TEST
+ select SND_SOC_MEDIATEK
+ help
+ This adds ASoC platform driver support for Airoha AN7581 chip
+ that can be used with other codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+config SND_SOC_AN7581_WM8960
+ tristate "ASoc Audio driver for Airoha AN7581 with WM8960 codec"
+ depends on SND_SOC_AN7581 && I2C
+ select SND_SOC_WM8960
+ help
+ This adds support for ASoC machine driver for Airoha AN7581
+ boards with the WM8960 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
+endmenu
+
+menu "Mediatek"
+
config SND_SOC_MT2701
tristate "ASoC support for Mediatek MT2701 chip"
depends on ARCH_MEDIATEK || COMPILE_TEST
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index a6815a3c5988..54522e1fa016 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_AN7581) += an7581/
obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
obj-$(CONFIG_SND_SOC_MT6797) += mt6797/
obj-$(CONFIG_SND_SOC_MT7986) += mt7986/
diff --git a/sound/soc/mediatek/an7581/Makefile b/sound/soc/mediatek/an7581/Makefile
new file mode 100644
index 000000000000..f48cd0269bea
--- /dev/null
+++ b/sound/soc/mediatek/an7581/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+
+# platform driver
+snd-soc-an7581-afe-y := \
+ an7581-afe-pcm.o \
+ an7581-dai-etdm.o
+
+obj-$(CONFIG_SND_SOC_AN7581) += snd-soc-an7581-afe.o
+obj-$(CONFIG_SND_SOC_AN7581_WM8960) += an7581-wm8960.o
diff --git a/sound/soc/mediatek/an7581/an7581-afe-common.h b/sound/soc/mediatek/an7581/an7581-afe-common.h
new file mode 100644
index 000000000000..ebc8e2f7fec4
--- /dev/null
+++ b/sound/soc/mediatek/an7581/an7581-afe-common.h
@@ -0,0 +1,49 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * an7581-afe-common.h -- Airoha AN7581 audio driver definitions
+ */
+
+#ifndef _AN7581_AFE_COMMON_H_
+#define _AN7581_AFE_COMMON_H_
+
+#include <linux/atomic.h>
+#include <linux/list.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+
+#include "../../mediatek/common/mtk-base-afe.h"
+
+enum {
+ AN7581_MEMIF_DL1,
+ AN7581_MEMIF_UL1,
+ AN7581_MEMIF_NUM,
+ AN7581_DAI_ETDM = AN7581_MEMIF_NUM,
+ AN7581_DAI_NUM,
+};
+
+enum {
+ AN7581_IRQ_0,
+ AN7581_IRQ_1,
+ AN7581_IRQ_NUM,
+};
+
+struct an7581_memif_irq_desc {
+ u8 irq;
+ u32 status_bit;
+ u32 clear_reg;
+};
+
+struct an7581_afe_private {
+ unsigned int users;
+ /* protect concurrent ETDM user tracking */
+ struct mutex user_lock;
+
+ void *dai_priv[AN7581_DAI_NUM];
+};
+
+unsigned int an7581_afe_rate_transform(struct device *dev,
+ unsigned int rate);
+
+int an7581_dai_etdm_register(struct mtk_base_afe *afe);
+
+#endif
diff --git a/sound/soc/mediatek/an7581/an7581-afe-pcm.c b/sound/soc/mediatek/an7581/an7581-afe-pcm.c
new file mode 100644
index 000000000000..d47edb630452
--- /dev/null
+++ b/sound/soc/mediatek/an7581/an7581-afe-pcm.c
@@ -0,0 +1,515 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Airoha ALSA SoC AFE platform driver for AN7581
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include "an7581-afe-common.h"
+#include "an7581-reg.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+enum {
+ ARH_AFE_RATE_8K = 0,
+ ARH_AFE_RATE_12K = 1,
+ ARH_AFE_RATE_16K = 2,
+ ARH_AFE_RATE_24K = 3,
+ ARH_AFE_RATE_32K = 4,
+ ARH_AFE_RATE_48K = 5,
+ ARH_AFE_RATE_96K = 6,
+ ARH_AFE_RATE_192K = 7,
+ ARH_AFE_RATE_384K = 8,
+ ARH_AFE_RATE_7K = 16,
+ ARH_AFE_RATE_11K = 17,
+ ARH_AFE_RATE_14K = 18,
+ ARH_AFE_RATE_22K = 19,
+ ARH_AFE_RATE_29K = 20,
+ ARH_AFE_RATE_44K = 21,
+ ARH_AFE_RATE_88K = 22,
+ ARH_AFE_RATE_176K = 23,
+ ARH_AFE_RATE_352K = 24,
+};
+
+unsigned int an7581_afe_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 7350:
+ return ARH_AFE_RATE_7K;
+ case 8000:
+ return ARH_AFE_RATE_8K;
+ case 11025:
+ return ARH_AFE_RATE_11K;
+ case 12000:
+ return ARH_AFE_RATE_12K;
+ case 14700:
+ return ARH_AFE_RATE_14K;
+ case 16000:
+ return ARH_AFE_RATE_16K;
+ case 22050:
+ return ARH_AFE_RATE_22K;
+ case 24000:
+ return ARH_AFE_RATE_24K;
+ case 29400:
+ return ARH_AFE_RATE_29K;
+ case 32000:
+ return ARH_AFE_RATE_32K;
+ case 44100:
+ return ARH_AFE_RATE_44K;
+ case 48000:
+ return ARH_AFE_RATE_48K;
+ case 88200:
+ return ARH_AFE_RATE_88K;
+ case 96000:
+ return ARH_AFE_RATE_96K;
+ case 176400:
+ return ARH_AFE_RATE_176K;
+ case 192000:
+ return ARH_AFE_RATE_192K;
+ case 352800:
+ return ARH_AFE_RATE_352K;
+ case 384000:
+ return ARH_AFE_RATE_384K;
+ default:
+ dev_warn_ratelimited(dev, "%s(), rate %u invalid, using %d!\n",
+ __func__, rate, ARH_AFE_RATE_48K);
+ return ARH_AFE_RATE_48K;
+ }
+}
+
+static const struct an7581_memif_irq_desc an7581_memif_irq_descs[AN7581_MEMIF_NUM] = {
+ [AN7581_MEMIF_DL1] = {
+ .irq = AN7581_IRQ_0,
+ .status_bit = AFE_IRQ_STS_PLAY,
+ .clear_reg = AFE_IRQ_CON0,
+ },
+ [AN7581_MEMIF_UL1] = {
+ .irq = AN7581_IRQ_1,
+ .status_bit = AFE_IRQ_STS_RECORD,
+ .clear_reg = AFE_IRQ1_CON0,
+ },
+};
+
+static const struct snd_pcm_hardware an7581_afe_hardware = {
+ .info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ .period_bytes_min = 512,
+ .period_bytes_max = 128 * 1024,
+ .periods_min = 2,
+ .periods_max = 256,
+ .buffer_bytes_max = 256 * 1024,
+};
+
+static int an7581_memif_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, AFE_PCM_NAME);
+ struct mtk_base_afe *afe = snd_soc_component_get_drvdata(component);
+
+ return an7581_afe_rate_transform(afe->dev, rate);
+}
+
+static int an7581_afe_fe_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ int ret;
+
+ ret = mtk_afe_fe_startup(substream, dai);
+ if (ret < 0)
+ return ret;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ ret = snd_pcm_hw_constraint_minmax(runtime,
+ SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+ 0x2000, UINT_MAX);
+ if (ret < 0)
+ dev_err(afe->dev, "hw_constraint_minmax failed\n");
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops an7581_afe_fe_ops = {
+ .startup = an7581_afe_fe_startup,
+ .shutdown = mtk_afe_fe_shutdown,
+ .hw_params = mtk_afe_fe_hw_params,
+ .hw_free = mtk_afe_fe_hw_free,
+ .prepare = mtk_afe_fe_prepare,
+ .trigger = mtk_afe_fe_trigger,
+};
+
+#define ARH_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver an7581_memif_dai_driver[] = {
+ /* FE DAIs: memory intefaces to CPU */
+ {
+ .name = "DL1",
+ .id = AN7581_MEMIF_DL1,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = ARH_PCM_FORMATS,
+ },
+ .ops = &an7581_afe_fe_ops,
+ },
+ {
+ .name = "UL1",
+ .id = AN7581_MEMIF_UL1,
+ .capture = {
+ .stream_name = "UL1",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = ARH_PCM_FORMATS,
+ },
+ .ops = &an7581_afe_fe_ops,
+ },
+};
+
+static const struct snd_soc_dapm_widget an7581_memif_widgets[] = {
+ /* DL */
+ SND_SOC_DAPM_MIXER("I032", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I033", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* UL */
+ SND_SOC_DAPM_MIXER("O018", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("O019", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route an7581_memif_routes[] = {
+ {"I032", NULL, "DL1"},
+ {"I033", NULL, "DL1"},
+ {"UL1", NULL, "O018"},
+ {"UL1", NULL, "O019"},
+ {"O018", NULL, "I150"},
+ {"O019", NULL, "I151"},
+};
+
+static const struct snd_soc_component_driver an7581_afe_pcm_dai_component = {
+ .name = "an7581-afe-pcm-dai",
+};
+
+static const struct mtk_base_memif_data memif_data[AN7581_MEMIF_NUM] = {
+ [AN7581_MEMIF_DL1] = {
+ .name = "DL1",
+ .id = AN7581_MEMIF_DL1,
+ .reg_ofs_base = AFE_DL1_BASE,
+ .reg_ofs_cur = AFE_DL1_CUR,
+ .reg_ofs_end = AFE_DL1_END,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = AFE_DL1_CON0,
+ .hd_shift = AFE_HD_SHIFT,
+ .hd_align_reg = -1,
+ .hd_align_mshift = -1,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = AFE_DL1_ENABLE_SHIFT,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ },
+ [AN7581_MEMIF_UL1] = {
+ .name = "UL1",
+ .id = AN7581_MEMIF_UL1,
+ .reg_ofs_base = AFE_UL1_BASE,
+ .reg_ofs_cur = AFE_UL1_CUR,
+ .reg_ofs_end = AFE_UL1_END,
+ .fs_reg = -1,
+ .fs_shift = -1,
+ .fs_maskbit = -1,
+ .mono_reg = -1,
+ .mono_shift = -1,
+ .hd_reg = AFE_UL1_CON0,
+ .hd_shift = AFE_HD_SHIFT,
+ .hd_align_reg = AFE_UL1_CON0,
+ .hd_align_mshift = AFE_HD_ALIGN_SHIFT,
+ .enable_reg = AFE_DAC_CON0,
+ .enable_shift = AFE_UL1_ENABLE_SHIFT,
+ .msb_reg = -1,
+ .msb_shift = -1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
+ },
+};
+
+static const struct mtk_base_irq_data irq_data[AN7581_IRQ_NUM] = {
+ [AN7581_IRQ_0] = {
+ .id = AN7581_IRQ_0,
+ .irq_cnt_reg = AFE_IRQ_CNT,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_en_reg = AFE_IRQ_CON0,
+ .irq_en_shift = AFE_IRQ_ON_SHIFT,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = -1,
+ .irq_fs_maskbit = -1,
+ .irq_clr_reg = -1,
+ .irq_clr_shift = -1,
+ },
+ [AN7581_IRQ_1] = {
+ .id = AN7581_IRQ_1,
+ .irq_cnt_reg = AFE_IRQ1_CNT,
+ .irq_cnt_shift = AFE_IRQ_CNT_SHIFT,
+ .irq_cnt_maskbit = AFE_IRQ_CNT_MASK,
+ .irq_en_reg = AFE_IRQ1_CON0,
+ .irq_en_shift = AFE_IRQ_ON_SHIFT,
+ .irq_fs_reg = -1,
+ .irq_fs_shift = -1,
+ .irq_fs_maskbit = -1,
+ .irq_clr_reg = -1,
+ .irq_clr_shift = -1,
+ },
+};
+
+static const struct regmap_config an7581_afe_regmap_config = {
+ .name = "afe",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_MAX_REGISTER,
+};
+
+static const struct regmap_config an7581_afe_irq1_regmap_config = {
+ .name = "afe_irq1",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_IRQ1_MAX_REGISTER,
+};
+
+static irqreturn_t an7581_afe_irq_handler(int irq_id, void *dev)
+{
+ const struct an7581_memif_irq_desc *irq_desc;
+ struct mtk_base_afe *afe = dev;
+ u32 status = 0;
+ int i;
+
+ regmap_read(afe->regmap, AFE_IRQ_STS, &status);
+
+ for (i = 0; i < AN7581_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+
+ if (!memif->substream)
+ continue;
+
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq_desc = &an7581_memif_irq_descs[i];
+ if (status & irq_desc->status_bit)
+ snd_pcm_period_elapsed(memif->substream);
+ }
+
+ for (i = 0; i < AN7581_MEMIF_NUM; i++) {
+ struct regmap *irq_regmap;
+
+ irq_desc = &an7581_memif_irq_descs[i];
+ if (!(status & irq_desc->status_bit))
+ continue;
+
+ irq_regmap = afe->irqs[irq_desc->irq].regmap;
+ regmap_set_bits(irq_regmap, irq_desc->clear_reg,
+ BIT(AFE_IRQ_CLR_SHIFT));
+ regmap_clear_bits(irq_regmap, irq_desc->clear_reg,
+ BIT(AFE_IRQ_CLR_SHIFT));
+
+ regmap_set_bits(irq_regmap, irq_desc->clear_reg,
+ BIT(AFE_IRQ_MISS_FLG_CLR_SHIFT));
+ regmap_clear_bits(irq_regmap, irq_desc->clear_reg,
+ BIT(AFE_IRQ_MISS_FLG_CLR_SHIFT));
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int an7581_dai_memif_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = an7581_memif_dai_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(an7581_memif_dai_driver);
+
+ dai->dapm_widgets = an7581_memif_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(an7581_memif_widgets);
+ dai->dapm_routes = an7581_memif_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(an7581_memif_routes);
+
+ return 0;
+}
+
+typedef int (*dai_register_cb)(struct mtk_base_afe *);
+static const dai_register_cb dai_register_cbs[] = {
+ an7581_dai_etdm_register,
+ an7581_dai_memif_register,
+};
+
+static int an7581_afe_pcm_dev_probe(struct platform_device *pdev)
+{
+ struct an7581_afe_private *afe_priv;
+ struct device *dev = &pdev->dev;
+ struct reset_control *reset;
+ struct mtk_base_afe *afe;
+ int i, irq_id, ret;
+ void __iomem *base;
+
+ afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
+ if (!afe)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, afe);
+
+ afe->platform_priv = devm_kzalloc(dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ if (!afe->platform_priv)
+ return -ENOMEM;
+
+ afe->irqs_size = AN7581_IRQ_NUM;
+ afe->irqs = devm_kcalloc(dev, afe->irqs_size, sizeof(*afe->irqs),
+ GFP_KERNEL);
+ if (!afe->irqs)
+ return -ENOMEM;
+
+ afe->memif_size = AN7581_MEMIF_NUM;
+ afe->memif = devm_kcalloc(dev, afe->memif_size, sizeof(*afe->memif),
+ GFP_KERNEL);
+ if (!afe->memif)
+ return -ENOMEM;
+
+ afe_priv = afe->platform_priv;
+ mutex_init(&afe_priv->user_lock);
+ afe->dev = &pdev->dev;
+
+ reset = devm_reset_control_get_exclusive(dev, NULL);
+ if (IS_ERR(reset))
+ return PTR_ERR(reset);
+
+ /* Global reset I2S */
+ reset_control_assert(reset);
+ usleep_range(10, 20);
+ reset_control_deassert(reset);
+
+ afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(afe->base_addr))
+ return PTR_ERR(afe->base_addr);
+
+ base = devm_platform_ioremap_resource(pdev, 1);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
+ &an7581_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ return PTR_ERR(afe->regmap);
+
+ mutex_init(&afe->irq_alloc_lock);
+
+ /* irq initialize */
+ for (i = 0; i < afe->irqs_size; i++)
+ afe->irqs[i].irq_data = &irq_data[i];
+
+ afe->irqs[AN7581_IRQ_0].regmap = afe->regmap;
+ afe->irqs[AN7581_IRQ_1].regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &an7581_afe_irq1_regmap_config);
+ if (IS_ERR(afe->irqs[AN7581_IRQ_1].regmap))
+ return PTR_ERR(afe->irqs[AN7581_IRQ_1].regmap);
+
+ /* request irq */
+ irq_id = platform_get_irq(pdev, 0);
+ if (irq_id < 0)
+ return irq_id;
+
+ /* init memif */
+ for (i = 0; i < afe->memif_size; i++) {
+ int sel_irq = an7581_memif_irq_descs[i].irq;
+
+ afe->memif[i].data = &memif_data[i];
+ afe->memif[i].irq_usage = sel_irq;
+ afe->memif[i].const_irq = 1;
+ afe->irqs[sel_irq].irq_occupyed = true;
+ }
+
+ /* init sub_dais */
+ INIT_LIST_HEAD(&afe->sub_dais);
+
+ ret = devm_request_irq(dev, irq_id, an7581_afe_irq_handler,
+ IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n");
+
+ for (i = 0; i < ARRAY_SIZE(dai_register_cbs); i++) {
+ ret = dai_register_cbs[i](afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "DAI register failed, i: %d\n", i);
+ }
+
+ /* init dai_driver and component_driver */
+ ret = mtk_afe_combine_sub_dai(afe);
+ if (ret)
+ return dev_err_probe(dev, ret, "mtk_afe_combine_sub_dai fail\n");
+
+ afe->mtk_afe_hardware = &an7581_afe_hardware;
+ afe->memif_fs = an7581_memif_irq_fs;
+ afe->irq_fs = an7581_memif_irq_fs;
+
+ /* register component */
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &mtk_afe_pcm_platform,
+ NULL, 0);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register AFE component\n");
+
+ ret = devm_snd_soc_register_component(afe->dev,
+ &an7581_afe_pcm_dai_component,
+ afe->dai_drivers,
+ afe->num_dai_drivers);
+ if (ret)
+ return dev_err_probe(dev, ret, "Cannot register PCM DAI component\n");
+
+ return 0;
+}
+
+static const struct of_device_id an7581_afe_pcm_dt_match[] = {
+ { .compatible = "airoha,an7581-afe" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, an7581_afe_pcm_dt_match);
+
+static struct platform_driver an7581_afe_pcm_driver = {
+ .driver = {
+ .name = "an7581-audio",
+ .of_match_table = an7581_afe_pcm_dt_match,
+ },
+ .probe = an7581_afe_pcm_dev_probe,
+};
+module_platform_driver(an7581_afe_pcm_driver);
+
+MODULE_DESCRIPTION("Airoha SoC AFE platform driver for ALSA AN7581");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/an7581/an7581-dai-etdm.c b/sound/soc/mediatek/an7581/an7581-dai-etdm.c
new file mode 100644
index 000000000000..7fbfd8fe1836
--- /dev/null
+++ b/sound/soc/mediatek/an7581/an7581-dai-etdm.c
@@ -0,0 +1,433 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Airoha ALSA SoC Audio DAI eTDM Control
+ *
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include "an7581-afe-common.h"
+#include "an7581-reg.h"
+
+#define HOPPING_CLK 0
+#define APLL_CLK 1
+#define MTK_DAI_ETDM_FORMAT_I2S 0
+#define MTK_DAI_ETDM_FORMAT_DSPA 4
+#define MTK_DAI_ETDM_FORMAT_DSPB 5
+
+enum {
+ MTK_ETDM_RATE_8K = 0,
+ MTK_ETDM_RATE_12K = 1,
+ MTK_ETDM_RATE_16K = 2,
+ MTK_ETDM_RATE_24K = 3,
+ MTK_ETDM_RATE_32K = 4,
+ MTK_ETDM_RATE_48K = 5,
+ MTK_ETDM_RATE_96K = 6,
+ MTK_ETDM_RATE_192K = 7,
+ MTK_ETDM_RATE_384K = 8,
+ MTK_ETDM_RATE_7K = 16,
+ MTK_ETDM_RATE_11K = 17,
+ MTK_ETDM_RATE_14K = 18,
+ MTK_ETDM_RATE_22K = 19,
+ MTK_ETDM_RATE_29K = 20,
+ MTK_ETDM_RATE_44K = 21,
+ MTK_ETDM_RATE_88K = 22,
+ MTK_ETDM_RATE_176K = 23,
+ MTK_ETDM_RATE_352K = 24,
+};
+
+struct mtk_dai_etdm_priv {
+ bool bck_inv;
+ bool lrck_inv;
+ bool slave_mode;
+ unsigned int format;
+};
+
+static unsigned int an7581_etdm_rate_transform(struct device *dev, unsigned int rate)
+{
+ switch (rate) {
+ case 7350:
+ return MTK_ETDM_RATE_7K;
+ case 8000:
+ return MTK_ETDM_RATE_8K;
+ case 11025:
+ return MTK_ETDM_RATE_11K;
+ case 12000:
+ return MTK_ETDM_RATE_12K;
+ case 14700:
+ return MTK_ETDM_RATE_14K;
+ case 16000:
+ return MTK_ETDM_RATE_16K;
+ case 22050:
+ return MTK_ETDM_RATE_22K;
+ case 24000:
+ return MTK_ETDM_RATE_24K;
+ case 29400:
+ return MTK_ETDM_RATE_29K;
+ case 32000:
+ return MTK_ETDM_RATE_32K;
+ case 44100:
+ return MTK_ETDM_RATE_44K;
+ case 48000:
+ return MTK_ETDM_RATE_48K;
+ case 88200:
+ return MTK_ETDM_RATE_88K;
+ case 96000:
+ return MTK_ETDM_RATE_96K;
+ case 176400:
+ return MTK_ETDM_RATE_176K;
+ case 192000:
+ return MTK_ETDM_RATE_192K;
+ case 352800:
+ return MTK_ETDM_RATE_352K;
+ case 384000:
+ return MTK_ETDM_RATE_384K;
+ default:
+ dev_warn_ratelimited(dev, "%s(), rate %u invalid, using %d!\n",
+ __func__, rate, MTK_ETDM_RATE_48K);
+ return MTK_ETDM_RATE_48K;
+ }
+}
+
+static int get_etdm_wlen(unsigned int bitwidth)
+{
+ return bitwidth <= 16 ? 16 : 32;
+}
+
+static const struct snd_soc_dapm_widget mtk_dai_etdm_widgets[] = {
+ /* DL */
+ SND_SOC_DAPM_MIXER("I150", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("I151", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ /* UL */
+ SND_SOC_DAPM_MIXER("O124", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_MIXER("O125", SND_SOC_NOPM, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route mtk_dai_etdm_routes[] = {
+ {"I150", NULL, "ETDM Capture"},
+ {"I151", NULL, "ETDM Capture"},
+ {"ETDM Playback", NULL, "O124"},
+ {"ETDM Playback", NULL, "O125"},
+ {"O124", NULL, "I032"},
+ {"O125", NULL, "I033"},
+};
+
+/* dai ops */
+static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct an7581_afe_private *afe_priv = afe->platform_priv;
+
+ guard(mutex)(&afe_priv->user_lock);
+
+ if (!afe_priv->users)
+ regmap_set_bits(afe->regmap, AFE_DAC_CON0,
+ BIT(AFE_AFE_ENABLE_SHIFT));
+ afe_priv->users++;
+
+ return 0;
+}
+
+static void mtk_dai_etdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct an7581_afe_private *afe_priv = afe->platform_priv;
+
+ guard(mutex)(&afe_priv->user_lock);
+
+ afe_priv->users--;
+ if (!afe_priv->users)
+ regmap_clear_bits(afe->regmap, AFE_DAC_CON0,
+ BIT(AFE_AFE_ENABLE_SHIFT));
+}
+
+static unsigned int get_etdm_ch_fixup(unsigned int channels)
+{
+ if (channels > 16)
+ return 24;
+
+ if (channels > 8)
+ return 16;
+
+ if (channels > 4)
+ return 8;
+
+ if (channels > 2)
+ return 4;
+
+ return 2;
+}
+
+static int mtk_dai_etdm_config(struct mtk_base_afe *afe,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai,
+ int stream)
+{
+ struct an7581_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data = afe_priv->dai_priv[dai->id];
+ unsigned int rate = params_rate(params);
+ unsigned int etdm_rate = an7581_etdm_rate_transform(afe->dev, rate);
+ unsigned int bit_width = params_width(params);
+ unsigned int mask, mask1;
+ unsigned int val, val1;
+
+ dev_dbg(afe->dev, "%s(), stream %d, rate %u, bitwidth %u\n",
+ __func__, stream, rate, params_width(params));
+
+ /* CON0 */
+ mask = ETDM_SLAVE_MODE | ETDM_BIT_LEN | ETDM_WRD_LEN |
+ ETDM_FMT | ETDM_CH_NUM;
+ val = FIELD_PREP(ETDM_BIT_LEN, params_width(params) - 1) |
+ FIELD_PREP(ETDM_WRD_LEN, get_etdm_wlen(bit_width) - 1) |
+ FIELD_PREP(ETDM_FMT, etdm_data->format) |
+ FIELD_PREP(ETDM_CH_NUM,
+ get_etdm_ch_fixup(params_channels(params)) - 1);
+ if (etdm_data->slave_mode)
+ val |= ETDM_SLAVE_MODE;
+
+ /* CON1 */
+ mask1 = EDTM_LRCK_AUTO_MODE | EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF |
+ EDTM_INITIAL_POINT | EDTM_INITIAL_COUNT;
+ val1 = EDTM_LRCK_AUTO_MODE | EDTM_CKEN_SEL | EDTM_LRCK_AUTO_OFF |
+ FIELD_PREP(EDTM_INITIAL_POINT, 14) | FIELD_PREP(EDTM_INITIAL_COUNT, 14);
+
+ switch (stream) {
+ case SNDRV_PCM_STREAM_PLAYBACK:
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON0, mask, val);
+
+ mask1 |= EDTM_DIRECT_INPUT_MASTER_BCK;
+ val1 |= EDTM_DIRECT_INPUT_MASTER_BCK;
+
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON1, mask1, val1);
+
+ regmap_update_bits(afe->regmap, ETDM_OUT1_CON4, OUT_SEL_FS,
+ FIELD_PREP(OUT_SEL_FS, etdm_rate));
+
+ regmap_update_bits(afe->irqs[AN7581_IRQ_0].regmap,
+ afe->irqs[AN7581_IRQ_0].irq_data->irq_en_reg,
+ AFE_IRQ_EN_SEL, AFE_IRQ_EN_SEL_I2SOUT);
+ break;
+ case SNDRV_PCM_STREAM_CAPTURE:
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON0, mask, val);
+
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON1, mask1, val1);
+
+ regmap_update_bits(afe->regmap, ETDM_IN1_CON3, IN_SEL_FS,
+ FIELD_PREP(IN_SEL_FS, etdm_rate));
+
+ regmap_update_bits(afe->irqs[AN7581_IRQ_1].regmap,
+ afe->irqs[AN7581_IRQ_1].irq_data->irq_en_reg,
+ AFE_IRQ_EN_SEL, AFE_IRQ_EN_SEL_I2SIN);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ unsigned int rate = params_rate(params);
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ regmap_update_bits(afe->regmap, ETDM_COWORK_CON0,
+ EDTM_IN1_SLAVE_SEL,
+ EDTM_IN1_SLAVE_FROM_ETDMIN1_SLAVE);
+ regmap_update_bits(afe->regmap, ETDM_COWORK_CON0,
+ EDTM_OUT1_SLAVE_SEL,
+ EDTM_OUT1_SLAVE_FROM_ETDMOUT1_SLAVE);
+ regmap_update_bits(afe->regmap, ETDM_COWORK_CON1,
+ EDTM_IN1_SDATA0_SEL,
+ EDTM_IN1_SDATA0_FROM_PAD);
+
+ switch (rate) {
+ case 7350:
+ case 8000:
+ case 11025:
+ case 12000:
+ case 14700:
+ case 16000:
+ case 22050:
+ case 24000:
+ case 29400:
+ case 32000:
+ case 44100:
+ case 48000:
+ case 88200:
+ case 96000:
+ case 176400:
+ case 192000:
+ case 352800:
+ case 384000:
+ return mtk_dai_etdm_config(afe, params, dai, substream->stream);
+ default:
+ dev_err(afe->dev, "Sample rate %d invalid\n", rate);
+ return -EINVAL;
+ }
+}
+
+static int mtk_dai_etdm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+
+ dev_dbg(afe->dev, "%s(), cmd %d, dai id %d\n", __func__, cmd, dai->id);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_set_bits(afe->regmap, ETDM_OUT1_CON0,
+ ETDM_OUT_EN);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ regmap_set_bits(afe->regmap, ETDM_IN1_CON0,
+ ETDM_IN_EN);
+
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ regmap_clear_bits(afe->regmap, ETDM_OUT1_CON0,
+ ETDM_OUT_EN);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ regmap_clear_bits(afe->regmap, ETDM_IN1_CON0,
+ ETDM_IN_EN);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int mtk_dai_etdm_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct mtk_base_afe *afe = snd_soc_dai_get_drvdata(dai);
+ struct an7581_afe_private *afe_priv = afe->platform_priv;
+ struct mtk_dai_etdm_priv *etdm_data;
+ void *priv_data;
+
+ priv_data = devm_kzalloc(afe->dev, sizeof(struct mtk_dai_etdm_priv),
+ GFP_KERNEL);
+ if (!priv_data)
+ return -ENOMEM;
+
+ afe_priv->dai_priv[dai->id] = priv_data;
+ etdm_data = afe_priv->dai_priv[dai->id];
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPA;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ etdm_data->format = MTK_DAI_ETDM_FORMAT_DSPB;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ etdm_data->bck_inv = false;
+ etdm_data->lrck_inv = true;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = false;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ etdm_data->bck_inv = true;
+ etdm_data->lrck_inv = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BP_FP:
+ etdm_data->slave_mode = false;
+ break;
+ case SND_SOC_DAIFMT_BC_FC:
+ etdm_data->slave_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops mtk_dai_etdm_ops = {
+ .startup = mtk_dai_etdm_startup,
+ .shutdown = mtk_dai_etdm_shutdown,
+ .hw_params = mtk_dai_etdm_hw_params,
+ .trigger = mtk_dai_etdm_trigger,
+ .set_fmt = mtk_dai_etdm_set_fmt,
+};
+
+/* dai driver */
+#define MTK_ETDM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\
+ SNDRV_PCM_FMTBIT_S24_LE |\
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver mtk_dai_etdm_driver[] = {
+ {
+ .name = "ETDM",
+ .id = AN7581_DAI_ETDM,
+ .capture = {
+ .stream_name = "ETDM Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .playback = {
+ .stream_name = "ETDM Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = MTK_ETDM_FORMATS,
+ },
+ .ops = &mtk_dai_etdm_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+};
+
+int an7581_dai_etdm_register(struct mtk_base_afe *afe)
+{
+ struct mtk_base_afe_dai *dai;
+
+ dai = devm_kzalloc(afe->dev, sizeof(*dai), GFP_KERNEL);
+ if (!dai)
+ return -ENOMEM;
+
+ list_add(&dai->list, &afe->sub_dais);
+
+ dai->dai_drivers = mtk_dai_etdm_driver;
+ dai->num_dai_drivers = ARRAY_SIZE(mtk_dai_etdm_driver);
+
+ dai->dapm_widgets = mtk_dai_etdm_widgets;
+ dai->num_dapm_widgets = ARRAY_SIZE(mtk_dai_etdm_widgets);
+ dai->dapm_routes = mtk_dai_etdm_routes;
+ dai->num_dapm_routes = ARRAY_SIZE(mtk_dai_etdm_routes);
+
+ return 0;
+}
diff --git a/sound/soc/mediatek/an7581/an7581-reg.h b/sound/soc/mediatek/an7581/an7581-reg.h
new file mode 100644
index 000000000000..a6ada3ca97ab
--- /dev/null
+++ b/sound/soc/mediatek/an7581/an7581-reg.h
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * an7581-reg.h -- Airoha AN7581 audio driver reg definition
+ */
+
+#ifndef _AN7581_REG_H_
+#define _AN7581_REG_H_
+
+#define AFE_DAC_CON0 0x0
+#define AFE_DL1_ENABLE_SHIFT 17
+#define AFE_UL1_ENABLE_SHIFT 1
+#define AFE_AFE_ENABLE_SHIFT 0
+
+#define ETDM_COWORK_CON0 0x4c
+#define EDTM_IN1_SLAVE_SEL GENMASK(27, 24)
+#define EDTM_IN1_SLAVE_FROM_ETDMIN1_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x1)
+#define EDTM_IN1_SLAVE_FROM_ETDMIN2_MASTER FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x2)
+#define EDTM_IN1_SLAVE_FROM_ETDMIN2_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x3)
+#define EDTM_IN1_SLAVE_FROM_ETDMOUT1_MASTER FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x8)
+#define EDTM_IN1_SLAVE_FROM_ETDMOUT1_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0x9)
+#define EDTM_IN1_SLAVE_FROM_ETDMOUT2_MASTER FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0xa)
+#define EDTM_IN1_SLAVE_FROM_ETDMOUT2_SLAVE FIELD_PREP_CONST(EDTM_IN1_SLAVE_SEL, 0xb)
+#define EDTM_OUT1_SLAVE_SEL GENMASK(11, 8)
+#define EDTM_OUT1_SLAVE_FROM_ETDMIN1_MASTER FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x0)
+#define EDTM_OUT1_SLAVE_FROM_ETDMIN1_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x1)
+#define EDTM_OUT1_SLAVE_FROM_ETDMIN2_MASTER FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x2)
+#define EDTM_OUT1_SLAVE_FROM_ETDMIN2_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x3)
+#define EDTM_OUT1_SLAVE_FROM_ETDMOUT1_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0x9)
+#define EDTM_OUT1_SLAVE_FROM_ETDMOUT2_MASTER FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0xa)
+#define EDTM_OUT1_SLAVE_FROM_ETDMOUT2_SLAVE FIELD_PREP_CONST(EDTM_OUT1_SLAVE_SEL, 0xb)
+#define ETDM_COWORK_CON1 0x50
+#define EDTM_IN1_SDATA0_SEL GENMASK(3, 0)
+#define EDTM_IN1_SDATA0_FROM_PAD FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0x0)
+#define EDTM_IN1_SDATA0_FROM_ETDMIN2 FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0x2)
+#define EDTM_IN1_SDATA0_FROM_ETDMOUT1 FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0x8)
+#define EDTM_IN1_SDATA0_FROM_ETDMOUT2 FIELD_PREP_CONST(EDTM_IN1_SDATA0_SEL, 0xa)
+#define ETDM_IN1_CON0 0x5c
+#define ETDM_CH_NUM GENMASK(26, 23)
+#define ETDM_WRD_LEN GENMASK(20, 16)
+#define ETDM_BIT_LEN GENMASK(15, 11)
+#define ETDM_FMT GENMASK(8, 6)
+#define ETDM_SLAVE_MODE BIT(5)
+#define ETDM_IN_EN BIT(0)
+#define ETDM_IN1_CON1 0x60
+#define EDTM_LRCK_AUTO_MODE BIT(29)
+#define EDTM_DIRECT_INPUT_MASTER_BCK BIT(30)
+#define EDTM_CKEN_SEL BIT(12)
+#define EDTM_LRCK_AUTO_OFF BIT(10)
+#define EDTM_INITIAL_POINT GENMASK(9, 5)
+#define EDTM_INITIAL_COUNT GENMASK(4, 0)
+#define ETDM_IN1_CON2 0x64
+#define IN_CLK_SRC GENMASK(12, 10)
+#define ETDM_IN1_CON3 0x68
+#define IN_SEL_FS GENMASK(30, 26)
+#define ETDM_IN1_CON4 0x6c
+#define IN_RELATCH GENMASK(24, 20)
+#define IN_CLK_INV BIT(18)
+#define ETDM_IN1_CON5 0x70
+#define ETDM_IN1_CON6 0x74
+#define ETDM_OUT1_CON0 0x7c
+#define ETDM_SYNC_MODE BIT(1)
+#define ETDM_OUT_EN BIT(0)
+#define ETDM_OUT1_CON1 0x80
+#define ETDM_OUT1_CON2 0x84
+#define ETDM_OUT1_CON3 0x88
+#define ETDM_OUT1_CON4 0x8c
+#define OUT_RELATCH GENMASK(28, 24)
+#define OUT_CLK_SRC GENMASK(8, 6)
+#define OUT_SEL_FS GENMASK(4, 0)
+#define ETDM_OUT1_CON5 0x90
+#define ETDM_CLK_DIV BIT(12)
+#define OUT_CLK_INV BIT(9)
+#define ETDM_OUT1_CON6 0x94
+#define ETDM_OUT1_CON7 0x98
+
+#define AFE_DL1_BASE 0xa8
+#define AFE_DL1_END 0xb0
+#define AFE_DL1_CUR 0xac
+#define AFE_DL1_CON0 0xb4
+#define AFE_PBUF_SIZE_SHIFT 16
+#define AFE_PBUF_SIZE_MASK GENMASK(1, 0)
+#define AFE_MINLEN_SHIFT 8
+#define AFE_MINLEN_MASK GENMASK(3, 0)
+#define AFE_HD_SHIFT 5
+
+#define AFE_UL1_BASE 0xc4
+#define AFE_UL1_END 0xc8
+#define AFE_UL1_CUR 0xcc
+#define AFE_UL1_CON0 0xd0
+#define AFE_HD_ALIGN_SHIFT 6
+
+#define AFE_IRQ_CON0 0xe4
+#define AFE_IRQ_EN_SEL BIT(4)
+#define AFE_IRQ_EN_SEL_I2SIN FIELD_PREP_CONST(AFE_IRQ_EN_SEL, 0x0)
+#define AFE_IRQ_EN_SEL_I2SOUT FIELD_PREP_CONST(AFE_IRQ_EN_SEL, 0x1)
+#define AFE_IRQ_MISS_FLG_CLR_SHIFT 3
+#define AFE_IRQ_CLR_SHIFT 2
+#define AFE_IRQ_ON_SHIFT 0
+#define AFE_IRQ_CNT 0xe8
+#define AFE_IRQ_CNT_SHIFT 0
+#define AFE_IRQ_CNT_MASK GENMASK(31, 0)
+
+#define AFE_IRQ_STS 0xf8
+#define AFE_IRQ_STS_PLAY BIT(1)
+#define AFE_IRQ_STS_RECORD BIT(0)
+
+#define AFE_MAX_REGISTER AFE_IRQ_STS
+
+#define AFE_IRQ1_CON0 0x0
+#define AFE_IRQ1_CNT 0x4
+
+#define AFE_IRQ1_MAX_REGISTER AFE_IRQ1_CNT
+
+#endif
diff --git a/sound/soc/mediatek/an7581/an7581-wm8960.c b/sound/soc/mediatek/an7581/an7581-wm8960.c
new file mode 100644
index 000000000000..03ea5732563c
--- /dev/null
+++ b/sound/soc/mediatek/an7581/an7581-wm8960.c
@@ -0,0 +1,161 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Airoha ALSA SoC I2S platform driver for AN7581
+ *
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#include "an7581-afe-common.h"
+
+SND_SOC_DAILINK_DEFS(playback,
+ DAILINK_COMP_ARRAY(COMP_CPU("DL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(capture,
+ DAILINK_COMP_ARRAY(COMP_CPU("UL1")),
+ DAILINK_COMP_ARRAY(COMP_DUMMY()),
+ DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()));
+
+SND_SOC_DAILINK_DEFS(codec,
+ DAILINK_COMP_ARRAY(COMP_CPU("ETDM")),
+ DAILINK_COMP_ARRAY(COMP_CODEC(NULL, "wm8960-hifi")),
+ DAILINK_COMP_ARRAY(COMP_EMPTY(), COMP_EMPTY()));
+
+static struct snd_soc_dai_link an7581_wm8960_dai_links[] = {
+ /* FE */
+ {
+ .name = "wm8960-playback",
+ .stream_name = "wm8960-playback",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .playback_only = 1,
+ SND_SOC_DAILINK_REG(playback),
+ },
+ {
+ .name = "wm8960-capture",
+ .stream_name = "wm8960-capture",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .dynamic = 1,
+ .capture_only = 1,
+ SND_SOC_DAILINK_REG(capture),
+ },
+ /* BE */
+ {
+ .name = "wm8960-codec",
+ .no_pcm = 1,
+ .dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBP_CFP |
+ SND_SOC_DAIFMT_GATED,
+ SND_SOC_DAILINK_REG(codec),
+ },
+};
+
+static struct snd_soc_card an7581_wm8960_card = {
+ .name = "an7581-wm8960",
+ .owner = THIS_MODULE,
+ .dai_link = an7581_wm8960_dai_links,
+ .num_links = ARRAY_SIZE(an7581_wm8960_dai_links),
+};
+
+static int an7581_wm8960_machine_probe(struct platform_device *pdev)
+{
+ struct device_node *platform_dai_node, *codec_dai_node;
+ struct snd_soc_card *card = &an7581_wm8960_card;
+ struct device_node *of_platform, *codec;
+ struct snd_soc_dai_link *dai_link;
+ int i, d, ret;
+
+ card->dev = &pdev->dev;
+
+ of_platform = of_get_child_by_name(pdev->dev.of_node, "platform");
+
+ if (of_platform) {
+ platform_dai_node = of_parse_phandle(of_platform, "sound-dai", 0);
+ of_node_put(of_platform);
+
+ if (!platform_dai_node) {
+ dev_err(&pdev->dev, "Failed to parse platform/sound-dai property\n");
+ return -EINVAL;
+ }
+ } else {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ struct snd_soc_dai_link_component *platform;
+
+ dai_link->num_platforms = 2;
+ for_each_link_platforms(dai_link, d, platform) {
+ if (platform->name)
+ continue;
+ platform->of_node = platform_dai_node;
+ }
+ }
+
+ codec = of_get_child_by_name(pdev->dev.of_node, "codec");
+
+ if (codec) {
+ codec_dai_node = of_parse_phandle(codec, "sound-dai", 0);
+ of_node_put(codec);
+
+ if (!codec_dai_node) {
+ of_node_put(platform_dai_node);
+ dev_err(&pdev->dev, "Failed to parse codec/sound-dai property\n");
+ return -EINVAL;
+ }
+ } else {
+ of_node_put(platform_dai_node);
+ dev_err(&pdev->dev, "Property 'codec' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for_each_card_prelinks(card, i, dai_link) {
+ if (dai_link->codecs->name)
+ continue;
+ dai_link->codecs->of_node = codec_dai_node;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret);
+ goto err_of_node_put;
+ }
+
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret) {
+ dev_err_probe(&pdev->dev, ret, "%s snd_soc_register_card fail\n", __func__);
+ goto err_of_node_put;
+ }
+
+ return 0;
+
+err_of_node_put:
+ of_node_put(platform_dai_node);
+ of_node_put(codec_dai_node);
+ return ret;
+}
+
+static const struct of_device_id an7581_wm8960_machine_dt_match[] = {
+ { .compatible = "airoha,an7581-wm8960-sound" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, an7581_wm8960_machine_dt_match);
+
+static struct platform_driver an7581_wm8960_driver = {
+ .driver = {
+ .name = "an7581-wm8960",
+ .of_match_table = an7581_wm8960_machine_dt_match,
+ },
+ .probe = an7581_wm8960_machine_probe,
+};
+module_platform_driver(an7581_wm8960_driver);
+
+MODULE_DESCRIPTION("Airoha SoC I2S platform driver for ALSA AN7581");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/mediatek/common/mtk-afe-fe-dai.c b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
index 2a20fa5dba49..948f606cbaf2 100644
--- a/sound/soc/mediatek/common/mtk-afe-fe-dai.c
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -204,11 +204,15 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
struct mtk_base_afe_irq *irqs = &afe->irqs[memif->irq_usage];
const struct mtk_base_irq_data *irq_data = irqs->irq_data;
unsigned int counter = runtime->period_size;
+ struct regmap *regmap = afe->regmap;
int fs;
int ret;
dev_dbg(afe->dev, "%s %s cmd=%d\n", __func__, memif->data->name, cmd);
+ if (irqs->regmap)
+ regmap = irqs->regmap;
+
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
@@ -220,7 +224,7 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
}
/* set irq counter */
- mtk_regmap_update_bits(afe->regmap, irq_data->irq_cnt_reg,
+ mtk_regmap_update_bits(regmap, irq_data->irq_cnt_reg,
irq_data->irq_cnt_maskbit, counter,
irq_data->irq_cnt_shift);
@@ -230,12 +234,12 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
if (fs < 0)
return -EINVAL;
- mtk_regmap_update_bits(afe->regmap, irq_data->irq_fs_reg,
+ mtk_regmap_update_bits(regmap, irq_data->irq_fs_reg,
irq_data->irq_fs_maskbit, fs,
irq_data->irq_fs_shift);
/* enable interrupt */
- mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+ mtk_regmap_update_bits(regmap, irq_data->irq_en_reg,
1, 1, irq_data->irq_en_shift);
return 0;
@@ -248,10 +252,10 @@ int mtk_afe_fe_trigger(struct snd_pcm_substream *substream, int cmd,
}
/* disable interrupt */
- mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+ mtk_regmap_update_bits(regmap, irq_data->irq_en_reg,
1, 0, irq_data->irq_en_shift);
/* and clear pending IRQ */
- mtk_regmap_write(afe->regmap, irq_data->irq_clr_reg,
+ mtk_regmap_write(regmap, irq_data->irq_clr_reg,
1 << irq_data->irq_clr_shift);
return ret;
default:
diff --git a/sound/soc/mediatek/common/mtk-base-afe.h b/sound/soc/mediatek/common/mtk-base-afe.h
index a406f2e3e7a8..b5300c9ed415 100644
--- a/sound/soc/mediatek/common/mtk-base-afe.h
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -153,6 +153,8 @@ struct mtk_base_afe_memif {
struct mtk_base_afe_irq {
const struct mtk_base_irq_data *irq_data;
int irq_occupyed;
+
+ struct regmap *regmap;
};
struct mtk_base_afe_dai {
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
index d217f9320ad2..1cf66ca44a33 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -30,10 +30,9 @@ int mt2701_init_clock(struct mtk_base_afe *afe)
for (i = 0; i < MT2701_BASE_CLK_NUM; i++) {
afe_priv->base_ck[i] = devm_clk_get(afe->dev, base_clks[i]);
- if (IS_ERR(afe_priv->base_ck[i])) {
- dev_err(afe->dev, "failed to get %s\n", base_clks[i]);
- return PTR_ERR(afe_priv->base_ck[i]);
- }
+ if (IS_ERR(afe_priv->base_ck[i]))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->base_ck[i]),
+ "failed to get %s\n", base_clks[i]);
}
i2s_num = min(afe_priv->soc->i2s_num, MT2701_BASE_CLK_NUM);
@@ -45,57 +44,49 @@ int mt2701_init_clock(struct mtk_base_afe *afe)
snprintf(name, sizeof(name), "i2s%d_src_sel", i);
i2s_path->sel_ck = devm_clk_get(afe->dev, name);
- if (IS_ERR(i2s_path->sel_ck)) {
- dev_err(afe->dev, "failed to get %s\n", name);
- return PTR_ERR(i2s_path->sel_ck);
- }
+ if (IS_ERR(i2s_path->sel_ck))
+ return dev_err_probe(afe->dev, PTR_ERR(i2s_path->sel_ck),
+ "failed to get %s\n", name);
snprintf(name, sizeof(name), "i2s%d_src_div", i);
i2s_path->div_ck = devm_clk_get(afe->dev, name);
- if (IS_ERR(i2s_path->div_ck)) {
- dev_err(afe->dev, "failed to get %s\n", name);
- return PTR_ERR(i2s_path->div_ck);
- }
+ if (IS_ERR(i2s_path->div_ck))
+ return dev_err_probe(afe->dev, PTR_ERR(i2s_path->div_ck),
+ "failed to get %s\n", name);
snprintf(name, sizeof(name), "i2s%d_mclk_en", i);
i2s_path->mclk_ck = devm_clk_get(afe->dev, name);
- if (IS_ERR(i2s_path->mclk_ck)) {
- dev_err(afe->dev, "failed to get %s\n", name);
- return PTR_ERR(i2s_path->mclk_ck);
- }
+ if (IS_ERR(i2s_path->mclk_ck))
+ return dev_err_probe(afe->dev, PTR_ERR(i2s_path->mclk_ck),
+ "failed to get %s\n", name);
snprintf(name, sizeof(name), "i2so%d_hop_ck", i);
i2s_ck = devm_clk_get(afe->dev, name);
- if (IS_ERR(i2s_ck)) {
- dev_err(afe->dev, "failed to get %s\n", name);
- return PTR_ERR(i2s_ck);
- }
+ if (IS_ERR(i2s_ck))
+ return dev_err_probe(afe->dev, PTR_ERR(i2s_ck),
+ "failed to get %s\n", name);
+
i2s_path->hop_ck[SNDRV_PCM_STREAM_PLAYBACK] = i2s_ck;
snprintf(name, sizeof(name), "i2si%d_hop_ck", i);
i2s_ck = devm_clk_get(afe->dev, name);
- if (IS_ERR(i2s_ck)) {
- dev_err(afe->dev, "failed to get %s\n", name);
- return PTR_ERR(i2s_ck);
- }
+ if (IS_ERR(i2s_ck))
+ return dev_err_probe(afe->dev, PTR_ERR(i2s_ck),
+ "failed to get %s\n", name);
+
i2s_path->hop_ck[SNDRV_PCM_STREAM_CAPTURE] = i2s_ck;
snprintf(name, sizeof(name), "asrc%d_out_ck", i);
i2s_path->asrco_ck = devm_clk_get(afe->dev, name);
- if (IS_ERR(i2s_path->asrco_ck)) {
- dev_err(afe->dev, "failed to get %s\n", name);
- return PTR_ERR(i2s_path->asrco_ck);
- }
+ if (IS_ERR(i2s_path->asrco_ck))
+ return dev_err_probe(afe->dev, PTR_ERR(i2s_path->asrco_ck),
+ "failed to get %s\n", name);
}
/* Some platforms may support BT path */
- afe_priv->mrgif_ck = devm_clk_get(afe->dev, "audio_mrgif_pd");
- if (IS_ERR(afe_priv->mrgif_ck)) {
- if (PTR_ERR(afe_priv->mrgif_ck) == -EPROBE_DEFER)
- return -EPROBE_DEFER;
-
- afe_priv->mrgif_ck = NULL;
- }
+ afe_priv->mrgif_ck = devm_clk_get_optional(afe->dev, "audio_mrgif_pd");
+ if (IS_ERR(afe_priv->mrgif_ck))
+ return PTR_ERR(afe_priv->mrgif_ck);
/*
* Optional HDMI audio clocks. Platforms that do not wire up the
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index d56b498e8c0c..3c5a76591ea7 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -1622,10 +1622,8 @@ static int mt2701_afe_pcm_dev_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_id, mt2701_asys_isr,
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
- if (ret) {
- dev_err(dev, "could not request_irq for asys-isr\n");
+ if (ret)
return ret;
- }
afe->regmap = syscon_node_to_regmap(dev->parent->of_node);
if (IS_ERR(afe->regmap)) {
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
index 778a9dccfcaa..af833a8daa2a 100644
--- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -366,10 +366,8 @@ static int mt2701_cs42448_machine_probe(struct platform_device *pdev)
= codec_node_bt_mrg;
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
- if (ret) {
- dev_err(dev, "failed to parse audio-routing: %d\n", ret);
+ if (ret)
return ret;
- }
priv->i2s1_in_mux_sel_1 = devm_gpiod_get_optional(dev, "i2s1-in-sel-gpio1",
GPIOD_OUT_LOW);
diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
index 84b3d6cd77a5..25ea3e274aa8 100644
--- a/sound/soc/mediatek/mt2701/mt2701-wm8960.c
+++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
@@ -137,10 +137,8 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
}
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
- if (ret) {
- dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+ if (ret)
goto put_codec_node;
- }
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret)
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-clk.c b/sound/soc/mediatek/mt6797/mt6797-afe-clk.c
index 6f3e6acfcfab..2326496f8b27 100644
--- a/sound/soc/mediatek/mt6797/mt6797-afe-clk.c
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-clk.c
@@ -43,12 +43,9 @@ int mt6797_init_clock(struct mtk_base_afe *afe)
for (i = 0; i < CLK_NUM; i++) {
afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe_priv->clk[i])) {
- dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
- __func__, aud_clks[i],
- PTR_ERR(afe_priv->clk[i]));
- return PTR_ERR(afe_priv->clk[i]);
- }
+ if (IS_ERR(afe_priv->clk[i]))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->clk[i]),
+ "failed to get clock %s\n", aud_clks[i]);
}
return 0;
@@ -63,21 +60,21 @@ int mt6797_afe_enable_clock(struct mtk_base_afe *afe)
if (ret) {
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_INFRA_SYS_AUD], ret);
- goto CLK_INFRA_SYS_AUDIO_ERR;
+ return ret;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_SYS_AUD_26M]);
if (ret) {
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_INFRA_SYS_AUD_26M], ret);
- goto CLK_INFRA_SYS_AUD_26M_ERR;
+ goto CLK_INFRA_SYS_AUDIO_ERR;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_TOP_MUX_AUD]);
if (ret) {
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD], ret);
- goto CLK_MUX_AUDIO_ERR;
+ goto CLK_INFRA_SYS_AUD_26M_ERR;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD],
@@ -93,13 +90,11 @@ int mt6797_afe_enable_clock(struct mtk_base_afe *afe)
if (ret) {
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_BUS], ret);
- goto CLK_MUX_AUDIO_INTBUS_ERR;
+ goto CLK_MUX_AUDIO_ERR;
}
- return ret;
+ return 0;
-CLK_MUX_AUDIO_INTBUS_ERR:
- clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_BUS]);
CLK_MUX_AUDIO_ERR:
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD]);
CLK_INFRA_SYS_AUD_26M_ERR:
@@ -107,7 +102,7 @@ CLK_INFRA_SYS_AUD_26M_ERR:
CLK_INFRA_SYS_AUDIO_ERR:
clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUD]);
- return 0;
+ return ret;
}
int mt6797_afe_disable_clock(struct mtk_base_afe *afe)
diff --git a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
index f62a32f2f2b6..dfe67727fe47 100644
--- a/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
+++ b/sound/soc/mediatek/mt6797/mt6797-afe-pcm.c
@@ -754,10 +754,8 @@ static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev)
/* initial audio related clock */
ret = mt6797_init_clock(afe);
- if (ret) {
- dev_err(dev, "init clock error\n");
+ if (ret)
return ret;
- }
/* regmap init */
afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
@@ -800,10 +798,8 @@ static int mt6797_afe_pcm_dev_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_id, mt6797_afe_irq_handler,
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
- if (ret) {
- dev_err(dev, "could not request_irq for asys-isr\n");
+ if (ret)
return ret;
- }
/* init sub_dais */
INIT_LIST_HEAD(&afe->sub_dais);
diff --git a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
index 7a6ad9116e55..e7ecb2761b4d 100644
--- a/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
+++ b/sound/soc/mediatek/mt7986/mt7986-afe-pcm.c
@@ -334,7 +334,7 @@ static int mt7986_init_clock(struct mtk_base_afe *afe)
ret = devm_clk_bulk_get(afe->dev, afe_priv->num_clks, afe_priv->clks);
if (ret)
- return dev_err_probe(afe->dev, ret, "Failed to get clocks\n");
+ return ret;
return 0;
}
@@ -414,7 +414,7 @@ static int mt7986_afe_runtime_resume(struct device *dev)
ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks);
if (ret)
- return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n");
+ return ret;
if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
return 0;
@@ -484,7 +484,7 @@ static int mt7986_afe_pcm_dev_probe(struct platform_device *pdev)
/* initial audio related clock */
ret = mt7986_init_clock(afe);
if (ret)
- return dev_err_probe(dev, ret, "Cannot initialize clocks\n");
+ return ret;
ret = devm_pm_runtime_enable(dev);
if (ret)
@@ -535,7 +535,7 @@ static int mt7986_afe_pcm_dev_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_id, mt7986_afe_irq_handler,
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
if (ret)
- return dev_err_probe(dev, ret, "Failed to request irq for asys-isr\n");
+ return ret;
/* init sub_dais */
INIT_LIST_HEAD(&afe->sub_dais);
diff --git a/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c
index fc55ff47b7bc..49f5b2bf565f 100644
--- a/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c
+++ b/sound/soc/mediatek/mt7986/mt7986-dai-etdm.c
@@ -124,7 +124,7 @@ static int mtk_dai_etdm_startup(struct snd_pcm_substream *substream,
ret = clk_bulk_prepare_enable(afe_priv->num_clks, afe_priv->clks);
if (ret)
- return dev_err_probe(afe->dev, ret, "Failed to enable clocks\n");
+ return ret;
regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_OUT5_PDN_MASK, 0);
regmap_update_bits(afe->regmap, AUDIO_TOP_CON2, CLK_IN5_PDN_MASK, 0);
diff --git a/sound/soc/mediatek/mt7986/mt7986-wm8960.c b/sound/soc/mediatek/mt7986/mt7986-wm8960.c
index f1dc18222be7..4517af9e2d14 100644
--- a/sound/soc/mediatek/mt7986/mt7986-wm8960.c
+++ b/sound/soc/mediatek/mt7986/mt7986-wm8960.c
@@ -135,10 +135,8 @@ static int mt7986_wm8960_machine_probe(struct platform_device *pdev)
}
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
- if (ret) {
- dev_err(&pdev->dev, "Failed to parse audio-routing: %d\n", ret);
+ if (ret)
goto err_of_node_put;
- }
ret = devm_snd_soc_register_card(&pdev->dev, card);
if (ret) {
diff --git a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 69cadc91c97f..c95699f84191 100644
--- a/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -997,10 +997,10 @@ static int mt8173_afe_runtime_resume(struct device *dev)
goto err_bck0;
ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S1_M]);
if (ret)
- goto err_i2s1_m;
+ goto err_bck1;
ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_I2S2_M]);
if (ret)
- goto err_i2s2_m;
+ goto err_i2s1_m;
/* enable AFE clk */
regmap_update_bits(afe->regmap, AUDIO_TOP_CON0, AUD_TCON0_PDN_AFE, 0);
@@ -1018,8 +1018,8 @@ static int mt8173_afe_runtime_resume(struct device *dev)
err_i2s1_m:
clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S1_M]);
-err_i2s2_m:
- clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_I2S2_M]);
+err_bck1:
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK1]);
err_bck0:
clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
err_top_aud:
@@ -1038,11 +1038,9 @@ static int mt8173_afe_init_audio_clk(struct mtk_base_afe *afe)
for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe_priv->clocks[i])) {
- dev_err(afe->dev, "%s devm_clk_get %s fail\n",
- __func__, aud_clks[i]);
- return PTR_ERR(afe_priv->clocks[i]);
- }
+ if (IS_ERR(afe_priv->clocks[i]))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->clocks[i]),
+ "failed to get clock %s\n", aud_clks[i]);
}
clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK0], 22579200); /* 22M */
clk_set_rate(afe_priv->clocks[MT8173_CLK_BCK1], 24576000); /* 24M */
@@ -1072,7 +1070,7 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
afe->dev = dev;
- ret = of_reserved_mem_device_init(dev);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret) {
dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
afe->preallocate_buffers = true;
@@ -1093,10 +1091,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
/* initial audio related clock */
ret = mt8173_afe_init_audio_clk(afe);
- if (ret) {
- dev_err(dev, "mt8173_afe_init_audio_clk fail\n");
+ if (ret)
return ret;
- }
/* memif % irq initialize*/
afe->memif_size = MT8173_AFE_MEMIF_NUM;
@@ -1156,10 +1152,8 @@ static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_id, mt8173_afe_irq_handler,
0, "Afe_ISR_Handle", (void *)afe);
- if (ret) {
- dev_err(dev, "could not request_irq\n");
+ if (ret)
goto err_cleanup_components;
- }
dev_info(dev, "MT8173 AFE driver initialized.\n");
return 0;
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-clk.c b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
index cc4f8f4d3dab..40f9afcdaa16 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-clk.c
@@ -101,12 +101,9 @@ int mt8183_init_clock(struct mtk_base_afe *afe)
for (i = 0; i < CLK_NUM; i++) {
afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe_priv->clk[i])) {
- dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
- __func__, aud_clks[i],
- PTR_ERR(afe_priv->clk[i]));
- return PTR_ERR(afe_priv->clk[i]);
- }
+ if (IS_ERR(afe_priv->clk[i]))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->clk[i]),
+ "failed to get clock %s\n", aud_clks[i]);
}
return 0;
@@ -137,7 +134,7 @@ int mt8183_afe_enable_clock(struct mtk_base_afe *afe)
dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_MUX_AUDIO],
aud_clks[CLK_CLK26M], ret);
- goto CLK_MUX_AUDIO_ERR;
+ goto CLK_MUX_AUDIO_INTBUS_ERR;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
@@ -153,7 +150,7 @@ int mt8183_afe_enable_clock(struct mtk_base_afe *afe)
dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_MUX_AUDIOINTBUS],
aud_clks[CLK_TOP_SYSPLL_D2_D4], ret);
- goto CLK_MUX_AUDIO_INTBUS_ERR;
+ goto CLK_AFE_ERR;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_AFE]);
@@ -271,7 +268,6 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
@@ -281,7 +277,6 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_1],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
}
@@ -298,7 +293,6 @@ ERR_SELECT_CLK_TOP_MUX_AUD_1:
afe_priv->clk[CLK_CLK26M]);
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
ERR_ENABLE_CLK_TOP_MUX_AUD_1:
-EXIT:
return ret;
}
@@ -345,7 +339,6 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
@@ -355,7 +348,6 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_2],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
}
@@ -372,7 +364,6 @@ ERR_SELECT_CLK_TOP_MUX_AUD_2:
afe_priv->clk[CLK_CLK26M]);
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
ERR_ENABLE_CLK_TOP_MUX_AUD_2:
-EXIT:
return ret;
}
@@ -382,7 +373,9 @@ int mt8183_apll1_enable(struct mtk_base_afe *afe)
int ret;
/* setting for APLL */
- apll1_mux_setting(afe, true);
+ ret = apll1_mux_setting(afe, true);
+ if (ret)
+ return ret;
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]);
if (ret) {
@@ -411,6 +404,8 @@ int mt8183_apll1_enable(struct mtk_base_afe *afe)
ERR_CLK_APLL1_TUNER:
clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
ERR_CLK_APLL22M:
+ apll1_mux_setting(afe, false);
+
return ret;
}
@@ -436,7 +431,9 @@ int mt8183_apll2_enable(struct mtk_base_afe *afe)
int ret;
/* setting for APLL */
- apll2_mux_setting(afe, true);
+ ret = apll2_mux_setting(afe, true);
+ if (ret)
+ return ret;
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]);
if (ret) {
@@ -465,6 +462,8 @@ int mt8183_apll2_enable(struct mtk_base_afe *afe)
ERR_CLK_APLL2_TUNER:
clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
ERR_CLK_APLL24M:
+ apll2_mux_setting(afe, false);
+
return ret;
}
diff --git a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
index 2634699534db..d8938542b828 100644
--- a/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
+++ b/sound/soc/mediatek/mt8183/mt8183-afe-pcm.c
@@ -766,11 +766,6 @@ static const dai_register_cb dai_register_cbs[] = {
mt8183_dai_memif_register,
};
-static void mt8183_afe_release_reserved_mem(void *data)
-{
- of_reserved_mem_device_release(data);
-}
-
static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
@@ -795,24 +790,16 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
afe_priv = afe->platform_priv;
afe->dev = dev;
- ret = of_reserved_mem_device_init(dev);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret) {
dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
afe->preallocate_buffers = true;
- } else {
- ret = devm_add_action_or_reset(dev,
- mt8183_afe_release_reserved_mem,
- dev);
- if (ret)
- return ret;
}
/* initial audio related clock */
ret = mt8183_init_clock(afe);
- if (ret) {
- dev_err(dev, "init clock error\n");
+ if (ret)
return ret;
- }
pm_runtime_enable(dev);
@@ -903,10 +890,8 @@ static int mt8183_afe_pcm_dev_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_id, mt8183_afe_irq_handler,
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
- if (ret) {
- dev_err(dev, "could not request_irq for asys-isr\n");
+ if (ret)
goto err_pm_disable;
- }
/* init sub_dais */
INIT_LIST_HEAD(&afe->sub_dais);
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
index daaca36a2d08..62b008b7caca 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-clk.c
@@ -98,7 +98,7 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_1], ret);
- return ret;
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_1;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
afe_priv->clk[CLK_TOP_APLL1_CK]);
@@ -106,7 +106,7 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_1],
aud_clks[CLK_TOP_APLL1_CK], ret);
- return ret;
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_1;
}
/* 180.6336 / 8 = 22.5792MHz */
@@ -114,7 +114,7 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], ret);
- return ret;
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
afe_priv->clk[CLK_TOP_APLL1_D8]);
@@ -122,7 +122,7 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
aud_clks[CLK_TOP_APLL1_D8], ret);
- return ret;
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG1;
}
} else {
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
@@ -131,7 +131,6 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
aud_clks[CLK_CLK26M], ret);
- return ret;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
@@ -141,12 +140,23 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_1],
aud_clks[CLK_CLK26M], ret);
- return ret;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
}
return 0;
+
+ERR_SELECT_CLK_TOP_MUX_AUD_ENG1:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_ENG1:
+ERR_SELECT_CLK_TOP_MUX_AUD_1:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_1:
+ return ret;
}
static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
@@ -159,7 +169,7 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_2], ret);
- return ret;
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_2;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
afe_priv->clk[CLK_TOP_APLL2_CK]);
@@ -167,7 +177,7 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_2],
aud_clks[CLK_TOP_APLL2_CK], ret);
- return ret;
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_2;
}
/* 196.608 / 8 = 24.576MHz */
@@ -175,7 +185,7 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], ret);
- return ret;
+ goto ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
afe_priv->clk[CLK_TOP_APLL2_D8]);
@@ -183,7 +193,7 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
aud_clks[CLK_TOP_APLL2_D8], ret);
- return ret;
+ goto ERR_SELECT_CLK_TOP_MUX_AUD_ENG2;
}
} else {
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
@@ -192,7 +202,6 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
aud_clks[CLK_CLK26M], ret);
- return ret;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
@@ -202,12 +211,23 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_2],
aud_clks[CLK_CLK26M], ret);
- return ret;
}
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
}
return 0;
+
+ERR_SELECT_CLK_TOP_MUX_AUD_ENG2:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_ENG2:
+ERR_SELECT_CLK_TOP_MUX_AUD_2:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_CLK26M]);
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+ERR_ENABLE_CLK_TOP_MUX_AUD_2:
+ return ret;
}
int mt8186_afe_enable_cgs(struct mtk_base_afe *afe)
@@ -268,7 +288,7 @@ int mt8186_afe_enable_clock(struct mtk_base_afe *afe)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_MUX_AUDIO],
aud_clks[CLK_CLK26M], ret);
- goto clk_mux_audio_err;
+ goto clk_mux_audio_intbus_err;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
@@ -295,24 +315,22 @@ int mt8186_afe_enable_clock(struct mtk_base_afe *afe)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_AFE], ret);
- goto clk_afe_err;
+ goto clk_mux_audio_h_parent_err;
}
return 0;
-clk_afe_err:
- clk_disable_unprepare(afe_priv->clk[CLK_AFE]);
clk_mux_audio_h_parent_err:
-clk_mux_audio_intbus_parent_err:
mt8186_set_audio_int_bus_parent(afe, CLK_CLK26M);
-clk_mux_audio_intbus_err:
+clk_mux_audio_intbus_parent_err:
clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
-clk_mux_audio_err:
+clk_mux_audio_intbus_err:
clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
-clk_infra_sys_audio_err:
- clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
-clk_infra_audio_26m_err:
+clk_mux_audio_err:
clk_disable_unprepare(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
+clk_infra_audio_26m_err:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+clk_infra_sys_audio_err:
return ret;
}
@@ -335,7 +353,9 @@ int mt8186_apll1_enable(struct mtk_base_afe *afe)
int ret;
/* setting for APLL */
- apll1_mux_setting(afe, true);
+ ret = apll1_mux_setting(afe, true);
+ if (ret)
+ return ret;
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]);
if (ret) {
@@ -360,9 +380,9 @@ int mt8186_apll1_enable(struct mtk_base_afe *afe)
return 0;
err_clk_apll1_tuner:
- clk_disable_unprepare(afe_priv->clk[CLK_APLL1_TUNER]);
-err_clk_apll22m:
clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
+err_clk_apll22m:
+ apll1_mux_setting(afe, false);
return ret;
}
@@ -388,7 +408,9 @@ int mt8186_apll2_enable(struct mtk_base_afe *afe)
int ret;
/* setting for APLL */
- apll2_mux_setting(afe, true);
+ ret = apll2_mux_setting(afe, true);
+ if (ret)
+ return ret;
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]);
if (ret) {
@@ -413,9 +435,9 @@ int mt8186_apll2_enable(struct mtk_base_afe *afe)
return 0;
err_clk_apll2_tuner:
- clk_disable_unprepare(afe_priv->clk[CLK_APLL2_TUNER]);
-err_clk_apll24m:
clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
+err_clk_apll24m:
+ apll2_mux_setting(afe, false);
return ret;
}
@@ -498,7 +520,7 @@ int mt8186_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
if (ret) {
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
__func__, aud_clks[m_sel_id], ret);
- return ret;
+ goto ERR_ENABLE_MCLK;
}
ret = clk_set_parent(afe_priv->clk[m_sel_id],
afe_priv->clk[apll_clk_id]);
@@ -506,7 +528,7 @@ int mt8186_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[m_sel_id],
aud_clks[apll_clk_id], ret);
- return ret;
+ goto ERR_SELECT_MCLK;
}
}
@@ -515,16 +537,25 @@ int mt8186_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
if (ret) {
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
__func__, aud_clks[div_clk_id], ret);
- return ret;
+ goto ERR_ENABLE_MCLK_DIV;
}
ret = clk_set_rate(afe_priv->clk[div_clk_id], rate);
if (ret) {
dev_err(afe->dev, "%s(), clk_set_rate %s, rate %d, fail %d\n",
__func__, aud_clks[div_clk_id], rate, ret);
- return ret;
+ goto ERR_SET_MCLK_RATE;
}
return 0;
+
+ERR_SET_MCLK_RATE:
+ clk_disable_unprepare(afe_priv->clk[div_clk_id]);
+ERR_ENABLE_MCLK_DIV:
+ERR_SELECT_MCLK:
+ if (m_sel_id >= 0)
+ clk_disable_unprepare(afe_priv->clk[m_sel_id]);
+ERR_ENABLE_MCLK:
+ return ret;
}
void mt8186_mck_disable(struct mtk_base_afe *afe, int mck_id)
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-gpio.c b/sound/soc/mediatek/mt8186/mt8186-afe-gpio.c
index aced8e7e920c..1cff2a63ef37 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-gpio.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-gpio.c
@@ -143,6 +143,7 @@ static int mt8186_afe_gpio_adda_dl(struct device *dev, bool enable)
ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MOSI_ON);
if (ret) {
dev_dbg(dev, "%s(), MOSI DAT ON select fail!\n", __func__);
+ mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MOSI_OFF);
return ret;
}
} else {
@@ -154,7 +155,8 @@ static int mt8186_afe_gpio_adda_dl(struct device *dev, bool enable)
ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MOSI_OFF);
if (ret) {
- dev_dbg(dev, "%s(), MOSI CLK ON select fail!\n", __func__);
+ dev_dbg(dev, "%s(), MOSI CLK OFF select fail!\n", __func__);
+ mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MOSI_ON);
return ret;
}
}
@@ -176,6 +178,7 @@ static int mt8186_afe_gpio_adda_ul(struct device *dev, bool enable)
ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MISO_ON);
if (ret) {
dev_dbg(dev, "%s(), MISO DAT ON select fail!\n", __func__);
+ mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MISO_OFF);
return ret;
}
} else {
@@ -188,6 +191,7 @@ static int mt8186_afe_gpio_adda_ul(struct device *dev, bool enable)
ret = mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_CLK_MISO_OFF);
if (ret) {
dev_dbg(dev, "%s(), MISO CLK OFF select fail!\n", __func__);
+ mt8186_afe_gpio_select(dev, MT8186_AFE_GPIO_DAT_MISO_ON);
return ret;
}
}
diff --git a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
index 44a521c3a610..2f50f78a6f8c 100644
--- a/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
+++ b/sound/soc/mediatek/mt8186/mt8186-afe-pcm.c
@@ -2743,7 +2743,11 @@ static int mt8186_afe_runtime_resume(struct device *dev)
goto skip_regmap;
regcache_cache_only(afe->regmap, false);
- regcache_sync(afe->regmap);
+ ret = regcache_sync(afe->regmap);
+ if (ret) {
+ regcache_cache_only(afe->regmap, true);
+ return ret;
+ }
/* enable audio sys DCM for power saving */
regmap_update_bits(afe_priv->infracfg, PERI_BUS_DCM_CTRL, BIT(29), BIT(29));
@@ -2836,7 +2840,7 @@ 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);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret) {
dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
afe->preallocate_buffers = true;
@@ -2848,10 +2852,8 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev)
/* init audio related clock */
ret = mt8186_init_clock(afe);
- if (ret) {
- dev_err(dev, "init clock error, ret %d\n", ret);
+ if (ret)
return ret;
- }
/* init memif */
afe->memif_32bit_supported = 0;
@@ -2881,15 +2883,14 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev)
/* request irq */
irq_id = platform_get_irq(pdev, 0);
- if (irq_id <= 0)
- return dev_err_probe(dev, irq_id < 0 ? irq_id : -ENXIO,
- "no irq found");
+ if (irq_id < 0)
+ return irq_id;
ret = devm_request_irq(dev, irq_id, mt8186_afe_irq_handler,
IRQF_TRIGGER_NONE,
"Afe_ISR_Handle", (void *)afe);
if (ret)
- return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+ return ret;
ret = enable_irq_wake(irq_id);
if (ret < 0)
@@ -2953,10 +2954,8 @@ static int mt8186_afe_pcm_dev_probe(struct platform_device *pdev)
&mt8186_afe_component,
afe->dai_drivers,
afe->num_dai_drivers);
- if (ret) {
- dev_err(dev, "err_dai_component\n");
+ if (ret)
goto err_pm_disable;
- }
ret = pm_runtime_put_sync(dev);
if (ret) {
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
index fc6cb3f0469e..8b6dca22c8ed 100644
--- a/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-clk.c
@@ -260,15 +260,28 @@ static int mt8188_afe_enable_tuner_clk(struct mtk_base_afe *afe,
unsigned int id)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
+ int ret;
switch (id) {
case MT8188_AUD_PLL1:
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]);
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]);
+ if (ret)
+ return ret;
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL1_TUNER]);
+ if (ret) {
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL]);
+ return ret;
+ }
break;
case MT8188_AUD_PLL2:
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]);
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]);
+ if (ret)
+ return ret;
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2_TUNER]);
+ if (ret) {
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_APLL2]);
+ return ret;
+ }
break;
default:
return -EINVAL;
@@ -416,12 +429,9 @@ int mt8188_afe_init_clock(struct mtk_base_afe *afe)
for (i = 0; i < MT8188_CLK_NUM; i++) {
afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe_priv->clk[i])) {
- dev_err(afe->dev, "%s(), devm_clk_get %s fail, ret %ld\n",
- __func__, aud_clks[i],
- PTR_ERR(afe_priv->clk[i]));
- return PTR_ERR(afe_priv->clk[i]);
- }
+ if (IS_ERR(afe_priv->clk[i]))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->clk[i]),
+ "failed to get clock %s\n", aud_clks[i]);
}
/* initial tuner */
@@ -430,7 +440,7 @@ int mt8188_afe_init_clock(struct mtk_base_afe *afe)
if (ret) {
dev_info(afe->dev, "%s(), init apll_tuner%d failed",
__func__, (i + 1));
- return -EINVAL;
+ return ret;
}
}
@@ -554,9 +564,7 @@ static int mt8188_afe_enable_top_cg(struct mtk_base_afe *afe, unsigned int cg_ty
unsigned int mask = get_top_cg_mask(cg_type);
unsigned int val = get_top_cg_on_val(cg_type);
- regmap_update_bits(afe->regmap, reg, mask, val);
-
- return 0;
+ return regmap_update_bits(afe->regmap, reg, mask, val);
}
static int mt8188_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_type)
@@ -565,30 +573,53 @@ static int mt8188_afe_disable_top_cg(struct mtk_base_afe *afe, unsigned int cg_t
unsigned int mask = get_top_cg_mask(cg_type);
unsigned int val = get_top_cg_off_val(cg_type);
- regmap_update_bits(afe->regmap, reg, mask, val);
-
- return 0;
+ return regmap_update_bits(afe->regmap, reg, mask, val);
}
int mt8188_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
{
struct mt8188_afe_private *afe_priv = afe->platform_priv;
-
+ int ret;
/* bus clock for AFE external access, like DRAM */
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]);
+ if (ret)
+ goto err_local_bus;
/* bus clock for AFE internal access, like AFE SRAM */
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]);
+ if (ret)
+ goto err_intbus;
/* audio 26m clock source */
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]);
+ if (ret)
+ goto err_26m;
/* AFE hw clock */
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]);
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]);
- mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]);
+ if (ret)
+ goto err_afe;
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]);
+ if (ret)
+ goto err_a1sys_hp;
+ ret = mt8188_afe_enable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ if (ret)
+ goto err_a1sys;
return 0;
+
+err_a1sys:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS_HP]);
+err_a1sys_hp:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_AFE]);
+err_afe:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_ADSP_AUDIO_26M]);
+err_26m:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUD_INTBUS_SEL]);
+err_intbus:
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_TOP_AUDIO_LOCAL_BUS_SEL]);
+err_local_bus:
+ return ret;
}
int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
@@ -607,14 +638,12 @@ int mt8188_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
static int mt8188_afe_enable_afe_on(struct mtk_base_afe *afe)
{
- regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
- return 0;
+ return regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x1);
}
static int mt8188_afe_disable_afe_on(struct mtk_base_afe *afe)
{
- regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0);
- return 0;
+ return regmap_update_bits(afe->regmap, AFE_DAC_CON0, 0x1, 0x0);
}
static int mt8188_afe_enable_a1sys(struct mtk_base_afe *afe)
@@ -626,7 +655,13 @@ static int mt8188_afe_enable_a1sys(struct mtk_base_afe *afe)
if (ret)
return ret;
- return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+ ret = mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A1SYS_TIMING);
+ if (ret) {
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A1SYS]);
+ return ret;
+ }
+
+ return 0;
}
static int mt8188_afe_disable_a1sys(struct mtk_base_afe *afe)
@@ -647,7 +682,13 @@ static int mt8188_afe_enable_a2sys(struct mtk_base_afe *afe)
if (ret)
return ret;
- return mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+ ret = mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_A2SYS_TIMING);
+ if (ret) {
+ mt8188_afe_disable_clk(afe, afe_priv->clk[MT8188_CLK_AUD_A2SYS]);
+ return ret;
+ }
+
+ return 0;
}
static int mt8188_afe_disable_a2sys(struct mtk_base_afe *afe)
@@ -735,8 +776,18 @@ int mt8188_apll2_disable(struct mtk_base_afe *afe)
int mt8188_afe_enable_main_clock(struct mtk_base_afe *afe)
{
- mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
- mt8188_afe_enable_afe_on(afe);
+ int ret;
+
+ ret = mt8188_afe_enable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+ if (ret)
+ return ret;
+
+ ret = mt8188_afe_enable_afe_on(afe);
+ if (ret) {
+ mt8188_afe_disable_top_cg(afe, MT8188_TOP_CG_26M_TIMING);
+ return ret;
+ }
+
return 0;
}
diff --git a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
index 7b1f5d05f4d6..4010879ad1d3 100644
--- a/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
+++ b/sound/soc/mediatek/mt8188/mt8188-afe-pcm.c
@@ -3030,22 +3030,33 @@ static int mt8188_afe_runtime_resume(struct device *dev)
struct mtk_base_afe *afe = dev_get_drvdata(dev);
struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct arm_smccc_res res;
+ int ret;
arm_smccc_smc(MTK_SIP_AUDIO_CONTROL,
MTK_AUDIO_SMC_OP_DOMAIN_SIDEBANDS,
0, 0, 0, 0, 0, 0, &res);
- mt8188_afe_enable_reg_rw_clk(afe);
+ ret = mt8188_afe_enable_reg_rw_clk(afe);
+ if (ret)
+ return ret;
if (!afe->regmap || afe_priv->pm_runtime_bypass_reg_ctl)
- goto skip_regmap;
+ return 0;
regcache_cache_only(afe->regmap, false);
- regcache_sync(afe->regmap);
+ ret = regcache_sync(afe->regmap);
+ if (ret)
+ goto err;
+
+ ret = mt8188_afe_enable_main_clock(afe);
+ if (ret)
+ goto err;
- mt8188_afe_enable_main_clock(afe);
-skip_regmap:
return 0;
+err:
+ mt8188_afe_disable_reg_rw_clk(afe);
+ regcache_cache_only(afe->regmap, true);
+ return ret;
}
static int init_memif_priv_data(struct mtk_base_afe *afe)
@@ -3199,7 +3210,7 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
struct regmap *infra_ao;
int i, irq_id, ret;
- ret = of_reserved_mem_device_init(dev);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret)
dev_dbg(dev, "failed to assign memory region: %d\n", ret);
@@ -3221,8 +3232,7 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(afe->base_addr))
- return dev_err_probe(dev, PTR_ERR(afe->base_addr),
- "AFE base_addr not found\n");
+ return PTR_ERR(afe->base_addr);
infra_ao = syscon_regmap_lookup_by_phandle(dev->of_node,
"mediatek,infracfg");
@@ -3258,7 +3268,7 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
/* initial audio related clock */
ret = mt8188_afe_init_clock(afe);
if (ret)
- return dev_err_probe(dev, ret, "init clock error");
+ return ret;
spin_lock_init(&afe_priv->afe_ctrl_lock);
@@ -3291,12 +3301,12 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
/* request irq */
irq_id = platform_get_irq(pdev, 0);
if (irq_id < 0)
- return dev_err_probe(dev, irq_id, "no irq found");
+ return irq_id;
ret = devm_request_irq(dev, irq_id, mt8188_afe_irq_handler,
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
if (ret)
- return dev_err_probe(dev, ret, "could not request_irq for asys-isr\n");
+ return ret;
/* init sub_dais */
INIT_LIST_HEAD(&afe->sub_dais);
@@ -3352,10 +3362,8 @@ static int mt8188_afe_pcm_dev_probe(struct platform_device *pdev)
/* register component */
ret = devm_snd_soc_register_component(dev, &mtk_afe_pcm_platform,
afe->dai_drivers, afe->num_dai_drivers);
- if (ret) {
- dev_warn(dev, "err_platform\n");
+ if (ret)
goto err_pm_put;
- }
mt8188_afe_init_registers(afe);
diff --git a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c
index 972f097a13ca..9f3b3a777577 100644
--- a/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c
+++ b/sound/soc/mediatek/mt8188/mt8188-audsys-clk.c
@@ -170,7 +170,7 @@ int mt8188_audsys_clk_register(struct mtk_base_afe *afe)
struct mt8188_afe_private *afe_priv = afe->platform_priv;
struct clk *clk;
struct clk_lookup *cl;
- int i;
+ int i, ret;
afe_priv->lookup = devm_kcalloc(afe->dev, CLK_AUD_NR_CLK,
sizeof(*afe_priv->lookup),
@@ -179,6 +179,10 @@ int mt8188_audsys_clk_register(struct mtk_base_afe *afe)
if (!afe_priv->lookup)
return -ENOMEM;
+ ret = devm_add_action_or_reset(afe->dev, mt8188_audsys_clk_unregister, afe);
+ if (ret)
+ return ret;
+
for (i = 0; i < ARRAY_SIZE(aud_clks); i++) {
const struct afe_gate *gate = &aud_clks[i];
@@ -194,8 +198,10 @@ int mt8188_audsys_clk_register(struct mtk_base_afe *afe)
/* add clk_lookup for devm_clk_get(SND_SOC_DAPM_CLOCK_SUPPLY) */
cl = kzalloc_obj(*cl);
- if (!cl)
+ if (!cl) {
+ clk_unregister_gate(clk);
return -ENOMEM;
+ }
cl->clk = clk;
cl->con_id = gate->name;
@@ -206,5 +212,5 @@ int mt8188_audsys_clk_register(struct mtk_base_afe *afe)
afe_priv->lookup[i] = cl;
}
- return devm_add_action_or_reset(afe->dev, mt8188_audsys_clk_unregister, afe);
+ return 0;
}
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
index ac547fc864a6..395f4ab972f8 100644
--- a/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-adda.c
@@ -123,10 +123,7 @@ static void mtk_adda_ul_mictype(struct mtk_base_afe *afe, bool dmic)
UL_MODE_3P25M_CH2_CTL);
/* turn on dmic, ch1, ch2 */
- if (dmic)
- regmap_set_bits(afe->regmap, reg, val);
- else
- regmap_clear_bits(afe->regmap, reg, val);
+ regmap_assign_bits(afe->regmap, reg, val, dmic);
}
static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
diff --git a/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c
index a9515d7fb70a..7ac847a5925c 100644
--- a/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c
+++ b/sound/soc/mediatek/mt8188/mt8188-dai-dmic.c
@@ -143,10 +143,7 @@ static void mtk_dai_dmic_hw_gain_bypass(struct mtk_base_afe *afe,
return;
}
- if (bypass)
- regmap_set_bits(afe->regmap, reg->bypass, msk);
- else
- regmap_clear_bits(afe->regmap, reg->bypass, msk);
+ regmap_assign_bits(afe->regmap, reg->bypass, msk, bypass);
}
static void mtk_dai_dmic_hw_gain_on(struct mtk_base_afe *afe, unsigned int id,
@@ -157,10 +154,7 @@ static void mtk_dai_dmic_hw_gain_on(struct mtk_base_afe *afe, unsigned int id,
if (!reg)
return;
- if (on)
- regmap_set_bits(afe->regmap, reg->con0, DMIC_GAIN_CON0_GAIN_ON);
- else
- regmap_clear_bits(afe->regmap, reg->con0, DMIC_GAIN_CON0_GAIN_ON);
+ regmap_assign_bits(afe->regmap, reg->con0, DMIC_GAIN_CON0_GAIN_ON, on);
}
static const struct reg_sequence mtk_dai_dmic_iir_coeff_reg_defaults[] = {
diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-clk.c b/sound/soc/mediatek/mt8189/mt8189-afe-clk.c
index fc7a7a73b0cf..594c214fa697 100644
--- a/sound/soc/mediatek/mt8189/mt8189-afe-clk.c
+++ b/sound/soc/mediatek/mt8189/mt8189-afe-clk.c
@@ -454,30 +454,47 @@ int mt8189_apll1_enable(struct mtk_base_afe *afe)
ret = mt8189_afe_enable_top_cg(afe, MT8189_CG_APLL1_CK);
if (ret)
- return ret;
+ goto err_clear_mux_setting;
ret = mt8189_afe_enable_top_cg(afe, MT8189_PDN_APLL_TUNER1);
if (ret)
- return ret;
+ goto err_disable_apll1_ck;
/* sel 44.1kHz:1, apll_div:7, upper bound:3 */
- regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
- XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT |
- UPPER_BOUND_MASK_SFT,
- (0x1 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
- (3 << UPPER_BOUND_SFT));
+ ret = regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT |
+ UPPER_BOUND_MASK_SFT,
+ (0x1 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
+ (3 << UPPER_BOUND_SFT));
+ if (ret)
+ goto err_disable_apll_tuner1;
/* apll1 freq tuner enable */
- regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
- FREQ_TUNER_EN_MASK_SFT,
- 0x1 << FREQ_TUNER_EN_SFT);
+ ret = regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x1 << FREQ_TUNER_EN_SFT);
+ if (ret)
+ goto err_disable_apll_tuner1;
/* audio apll1 on */
ret = mt8189_afe_enable_top_cg(afe, MT8189_AUDIO_APLL1_EN_ON);
if (ret)
- return ret;
+ goto err_clear_freq_tuner_en;
return 0;
+
+err_clear_freq_tuner_en:
+ regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x0);
+err_disable_apll_tuner1:
+ mt8189_afe_disable_top_cg(afe, MT8189_PDN_APLL_TUNER1);
+err_disable_apll1_ck:
+ mt8189_afe_disable_top_cg(afe, MT8189_CG_APLL1_CK);
+err_clear_mux_setting:
+ apll1_mux_setting(afe, false);
+
+ return ret;
}
void mt8189_apll1_disable(struct mtk_base_afe *afe)
@@ -506,30 +523,47 @@ int mt8189_apll2_enable(struct mtk_base_afe *afe)
ret = mt8189_afe_enable_top_cg(afe, MT8189_CG_APLL2_CK);
if (ret)
- return ret;
+ goto err_clear_mux_setting;
ret = mt8189_afe_enable_top_cg(afe, MT8189_PDN_APLL_TUNER2);
if (ret)
- return ret;
+ goto err_disable_apll2_ck;
/* sel 48kHz: 2, apll_div: 7, upper bound: 3*/
- regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
- XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT |
- UPPER_BOUND_MASK_SFT,
- (0x2 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
- (3 << UPPER_BOUND_SFT));
+ ret = regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ XTAL_EN_128FS_SEL_MASK_SFT | APLL_DIV_MASK_SFT |
+ UPPER_BOUND_MASK_SFT,
+ (0x2 << XTAL_EN_128FS_SEL_SFT) | (7 << APLL_DIV_SFT) |
+ (3 << UPPER_BOUND_SFT));
+ if (ret)
+ goto err_disable_apll_tuner2;
/* apll2 freq tuner enable */
- regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
- FREQ_TUNER_EN_MASK_SFT,
- 0x1 << FREQ_TUNER_EN_SFT);
+ ret = regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x1 << FREQ_TUNER_EN_SFT);
+ if (ret)
+ goto err_disable_apll_tuner2;
/* audio apll2 on */
ret = mt8189_afe_enable_top_cg(afe, MT8189_AUDIO_APLL2_EN_ON);
if (ret)
- return ret;
+ goto err_clear_freq_tuner_en;
return 0;
+
+err_clear_freq_tuner_en:
+ regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
+ FREQ_TUNER_EN_MASK_SFT,
+ 0x0);
+err_disable_apll_tuner2:
+ mt8189_afe_disable_top_cg(afe, MT8189_PDN_APLL_TUNER2);
+err_disable_apll2_ck:
+ mt8189_afe_disable_top_cg(afe, MT8189_CG_APLL2_CK);
+err_clear_mux_setting:
+ apll2_mux_setting(afe, false);
+
+ return ret;
}
void mt8189_apll2_disable(struct mtk_base_afe *afe)
@@ -605,7 +639,7 @@ int mt8189_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[m_sel_id],
afe_priv->clk[apll_clk_id]);
if (ret)
- return ret;
+ goto err_disable_m_sel_clk;
}
/* enable div, set rate */
@@ -616,13 +650,21 @@ int mt8189_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
ret = mt8189_afe_enable_clk(afe, afe_priv->clk[div_clk_id]);
if (ret)
- return ret;
+ goto err_disable_m_sel_clk;
ret = mt8189_afe_set_clk_rate(afe, afe_priv->clk[div_clk_id], rate);
if (ret)
- return ret;
+ goto err_disable_div_clk;
return 0;
+
+err_disable_div_clk:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[div_clk_id]);
+err_disable_m_sel_clk:
+ if (m_sel_id >= 0)
+ mt8189_afe_disable_clk(afe, afe_priv->clk[m_sel_id]);
+
+ return ret;
}
int mt8189_mck_disable(struct mtk_base_afe *afe, int mck_id)
@@ -633,8 +675,8 @@ int mt8189_mck_disable(struct mtk_base_afe *afe, int mck_id)
dev_dbg(afe->dev, "mck_id: %d.\n", mck_id);
- if (mck_id < 0) {
- dev_err(afe->dev, "mck_id = %d < 0\n", mck_id);
+ if (mck_id >= MT8189_MCK_NUM || mck_id < 0) {
+ dev_err(afe->dev, "mck_id = %d\n", mck_id);
return -EINVAL;
}
@@ -658,17 +700,36 @@ int mt8189_mck_disable(struct mtk_base_afe *afe, int mck_id)
int mt8189_afe_enable_reg_rw_clk(struct mtk_base_afe *afe)
{
struct mt8189_afe_private *afe_priv = afe->platform_priv;
+ int ret;
/* bus clock for AFE internal access, like AFE SRAM */
- mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS]);
- mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS],
- afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS]);
+ if (ret)
+ return ret;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ if (ret)
+ goto err_disable_audiointbus_clk;
+
/* enable audio clock source */
- mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
- mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
- afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ ret = mt8189_afe_enable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+ if (ret)
+ goto err_disable_audiointbus_clk;
+
+ ret = mt8189_afe_set_clk_parent(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H],
+ afe_priv->clk[MT8189_CLK_TOP_CLK26M]);
+ if (ret)
+ goto err_disable_audio_h_clk;
return 0;
+
+err_disable_audio_h_clk:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIO_H]);
+err_disable_audiointbus_clk:
+ mt8189_afe_disable_clk(afe, afe_priv->clk[MT8189_CLK_TOP_MUX_AUDIOINTBUS]);
+
+ return ret;
}
int mt8189_afe_disable_reg_rw_clk(struct mtk_base_afe *afe)
@@ -732,10 +793,9 @@ int mt8189_init_clock(struct mtk_base_afe *afe)
for (i = 0; i < MT8189_CLK_NUM; i++) {
afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe_priv->clk[i])) {
- dev_err(afe->dev, "devm_clk_get %s fail\n", aud_clks[i]);
- return PTR_ERR(afe_priv->clk[i]);
- }
+ if (IS_ERR(afe_priv->clk[i]))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->clk[i]),
+ "failed to get clock %s\n", aud_clks[i]);
}
ret = mt8189_afe_disable_apll(afe);
diff --git a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c
index 77cf2b604f6c..c0f340a6cc09 100644
--- a/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c
+++ b/sound/soc/mediatek/mt8189/mt8189-afe-pcm.c
@@ -2332,20 +2332,40 @@ static int mt8189_afe_runtime_resume(struct device *dev)
}
regcache_cache_only(afe->regmap, false);
- regcache_sync(afe->regmap);
+ ret = regcache_sync(afe->regmap);
+ if (ret)
+ goto err_set_cache_only;
/* set audio 26M request */
- regmap_update_bits(afe->regmap, AFE_SPM_CONTROL_REQ, 0x1, 0x1);
- regmap_update_bits(afe->regmap, AFE_CBIP_CFG0, 0x1, 0x1);
+ ret = regmap_update_bits(afe->regmap, AFE_SPM_CONTROL_REQ, 0x1, 0x1);
+ if (ret)
+ goto err_set_cache_only;
+
+ ret = regmap_update_bits(afe->regmap, AFE_CBIP_CFG0, 0x1, 0x1);
+ if (ret)
+ goto err_clear_26m_req;
/* force cpu use 8_24 format when writing 32bit data */
- regmap_update_bits(afe->regmap, AFE_MEMIF_CON0,
- CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT);
+ ret = regmap_update_bits(afe->regmap, AFE_MEMIF_CON0,
+ CPU_HD_ALIGN_MASK_SFT, 0 << CPU_HD_ALIGN_SFT);
+ if (ret)
+ goto err_clear_26m_req;
/* enable AFE */
- mt8189_afe_enable_main_clock(afe);
+ ret = mt8189_afe_enable_main_clock(afe);
+ if (ret)
+ goto err_clear_26m_req;
return 0;
+
+err_clear_26m_req:
+ regmap_update_bits(afe->regmap,
+ AFE_SPM_CONTROL_REQ, 0x1, 0x0);
+err_set_cache_only:
+ regcache_cache_only(afe->regmap, true);
+ mt8189_afe_disable_reg_rw_clk(afe);
+
+ return ret;
}
static int mt8189_afe_component_probe(struct snd_soc_component *component)
@@ -2421,11 +2441,6 @@ static const struct reg_sequence mt8189_cg_patch[] = {
{ AUDIO_TOP_CON4, 0x361c },
};
-static void mt8189_afe_release_reserved_mem(void *data)
-{
- of_reserved_mem_device_release(data);
-}
-
static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev)
{
int ret, i;
@@ -2439,16 +2454,9 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = of_reserved_mem_device_init(dev);
- if (ret) {
+ ret = devm_of_reserved_mem_device_init(dev);
+ if (ret)
dev_warn(dev, "failed to assign memory region: %d\n", ret);
- } else {
- ret = devm_add_action_or_reset(dev,
- mt8189_afe_release_reserved_mem,
- dev);
- if (ret)
- return ret;
- }
afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
if (!afe)
@@ -2466,13 +2474,12 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev)
afe->base_addr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(afe->base_addr))
- return dev_err_probe(dev, PTR_ERR(afe->base_addr),
- "AFE base_addr not found\n");
+ return PTR_ERR(afe->base_addr);
/* init audio related clock */
ret = mt8189_init_clock(afe);
if (ret)
- return dev_err_probe(dev, ret, "init clock error.\n");
+ return ret;
/* init memif */
/* IPM2.0 no need banding */
@@ -2506,13 +2513,13 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev)
/* request irq */
irq_id = platform_get_irq(pdev, 0);
if (irq_id < 0)
- return dev_err_probe(dev, irq_id, "no irq found");
+ return irq_id;
ret = devm_request_irq(dev, irq_id, mt8189_afe_irq_handler,
IRQF_TRIGGER_NONE,
"Afe_ISR_Handle", afe);
if (ret)
- return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+ return ret;
/* init sub_dais */
INIT_LIST_HEAD(&afe->sub_dais);
@@ -2581,10 +2588,8 @@ static int mt8189_afe_pcm_dev_probe(struct platform_device *pdev)
&mt8189_afe_component,
afe->dai_drivers,
afe->num_dai_drivers);
- if (ret) {
- dev_err(dev, "afe component err: %d\n", ret);
+ if (ret)
return ret;
- }
return 0;
diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-i2s.c b/sound/soc/mediatek/mt8189/mt8189-dai-i2s.c
index 94c49a662e2d..c9e80ef42f60 100644
--- a/sound/soc/mediatek/mt8189/mt8189-dai-i2s.c
+++ b/sound/soc/mediatek/mt8189/mt8189-dai-i2s.c
@@ -485,6 +485,7 @@ static int mtk_apll_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ int ret;
dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
__func__, w->name, event);
@@ -492,9 +493,11 @@ static int mtk_apll_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (strcmp(w->name, APLL1_W_NAME) == 0)
- mt8189_apll1_enable(afe);
+ ret = mt8189_apll1_enable(afe);
else
- mt8189_apll2_enable(afe);
+ ret = mt8189_apll2_enable(afe);
+ if (ret)
+ return ret;
break;
case SND_SOC_DAPM_POST_PMD:
if (strcmp(w->name, APLL1_W_NAME) == 0)
@@ -516,6 +519,7 @@ static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
struct mtk_afe_i2s_priv *i2s_priv;
+ int ret;
dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
__func__, w->name, event);
@@ -526,17 +530,18 @@ static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8189_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
+ ret = mt8189_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
break;
case SND_SOC_DAPM_POST_PMD:
i2s_priv->mclk_rate = 0;
- mt8189_mck_disable(afe, i2s_priv->mclk_id);
+ ret = mt8189_mck_disable(afe, i2s_priv->mclk_id);
break;
default:
+ ret = 0;
break;
}
- return 0;
+ return ret;
}
static const struct snd_soc_dapm_widget mtk_dai_i2s_widgets[] = {
@@ -1282,6 +1287,9 @@ static int mtk_dai_i2s_set_sysclk(struct snd_soc_dai *dai,
dir != SND_SOC_CLOCK_OUT)
return -EINVAL;
+ if (!freq)
+ return -EINVAL;
+
i2s_priv = afe_priv->dai_priv[dai->id];
if (!i2s_priv)
return -EINVAL;
diff --git a/sound/soc/mediatek/mt8189/mt8189-dai-tdm.c b/sound/soc/mediatek/mt8189/mt8189-dai-tdm.c
index 5d68a55ccc45..e113c52fe06a 100644
--- a/sound/soc/mediatek/mt8189/mt8189-dai-tdm.c
+++ b/sound/soc/mediatek/mt8189/mt8189-dai-tdm.c
@@ -261,22 +261,24 @@ static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
struct mt8189_afe_private *afe_priv = afe->platform_priv;
int dai_id = get_tdm_id_by_name(w->name);
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int ret;
dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d, bck: %d\n",
w->name, event, dai_id, tdm_priv->bck_rate);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8189_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
+ ret = mt8189_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
break;
case SND_SOC_DAPM_POST_PMD:
- mt8189_mck_disable(afe, tdm_priv->bck_id);
+ ret = mt8189_mck_disable(afe, tdm_priv->bck_id);
break;
default:
+ ret = 0;
break;
}
- return 0;
+ return ret;
}
static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
@@ -288,23 +290,25 @@ static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
struct mt8189_afe_private *afe_priv = afe->platform_priv;
int dai_id = get_tdm_id_by_name(w->name);
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int ret;
dev_dbg(cmpnt->dev, "name %s, event 0x%x, dai_id %d, mclk %d\n",
w->name, event, dai_id, tdm_priv->mclk_rate);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8189_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ ret = mt8189_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
break;
case SND_SOC_DAPM_POST_PMD:
tdm_priv->mclk_rate = 0;
- mt8189_mck_disable(afe, tdm_priv->mclk_id);
+ ret = mt8189_mck_disable(afe, tdm_priv->mclk_id);
break;
default:
+ ret = 0;
break;
}
- return 0;
+ return ret;
}
static const struct snd_soc_dapm_widget mtk_dai_tdm_widgets[] = {
@@ -379,6 +383,9 @@ static int mtk_dai_tdm_cal_mclk(struct mtk_base_afe *afe,
int apll;
int apll_rate;
+ if (freq <= 0)
+ return -EINVAL;
+
apll = mt8189_get_apll_by_rate(afe, freq);
apll_rate = mt8189_get_apll_rate(afe, apll);
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-clk.c b/sound/soc/mediatek/mt8192/mt8192-afe-clk.c
index 416aff726253..c6ca4fe7fb19 100644
--- a/sound/soc/mediatek/mt8192/mt8192-afe-clk.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-clk.c
@@ -87,7 +87,7 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_1], ret);
- goto EXIT;
+ return ret;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
afe_priv->clk[CLK_TOP_APLL1_CK]);
@@ -95,7 +95,7 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_1],
aud_clks[CLK_TOP_APLL1_CK], ret);
- goto EXIT;
+ goto err_disable_mux_aud_1;
}
/* 180.6336 / 4 = 45.1584MHz */
@@ -103,7 +103,7 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1], ret);
- goto EXIT;
+ goto err_set_parent_mux_aud_1;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
afe_priv->clk[CLK_TOP_APLL1_D4]);
@@ -111,31 +111,36 @@ static int apll1_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
aud_clks[CLK_TOP_APLL1_D4], ret);
- goto EXIT;
+ goto err_disable_mux_aud_eng1;
}
} else {
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1],
afe_priv->clk[CLK_CLK26M]);
- if (ret) {
+ if (ret)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG1],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
- }
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
afe_priv->clk[CLK_CLK26M]);
- if (ret) {
+ if (ret)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_1],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
- }
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
}
-EXIT:
+ return 0;
+
+err_disable_mux_aud_eng1:
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG1]);
+err_set_parent_mux_aud_1:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_1],
+ afe_priv->clk[CLK_CLK26M]);
+err_disable_mux_aud_1:
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_1]);
+
return ret;
}
@@ -149,7 +154,7 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_2], ret);
- goto EXIT;
+ return ret;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
afe_priv->clk[CLK_TOP_APLL2_CK]);
@@ -157,7 +162,7 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_2],
aud_clks[CLK_TOP_APLL2_CK], ret);
- goto EXIT;
+ goto err_disable_mux_aud_2;
}
/* 196.608 / 4 = 49.152MHz */
@@ -165,7 +170,7 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2], ret);
- goto EXIT;
+ goto err_set_parent_mux_aud_2;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
afe_priv->clk[CLK_TOP_APLL2_D4]);
@@ -173,31 +178,36 @@ static int apll2_mux_setting(struct mtk_base_afe *afe, bool enable)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
aud_clks[CLK_TOP_APLL2_D4], ret);
- goto EXIT;
+ goto err_disable_mux_aud_eng2;
}
} else {
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2],
afe_priv->clk[CLK_CLK26M]);
- if (ret) {
+ if (ret)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_ENG2],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
- }
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
afe_priv->clk[CLK_CLK26M]);
- if (ret) {
+ if (ret)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUD_2],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
- }
clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
}
-EXIT:
+ return 0;
+
+err_disable_mux_aud_eng2:
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_ENG2]);
+err_set_parent_mux_aud_2:
+ clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUD_2],
+ afe_priv->clk[CLK_CLK26M]);
+err_disable_mux_aud_2:
+ clk_disable_unprepare(afe_priv->clk[CLK_TOP_MUX_AUD_2]);
+
return ret;
}
@@ -210,21 +220,21 @@ int mt8192_afe_enable_clock(struct mtk_base_afe *afe)
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_INFRA_SYS_AUDIO], ret);
- goto EXIT;
+ return ret;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_INFRA_AUDIO_26M], ret);
- goto EXIT;
+ goto err_disable_infra_sys_audio;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIO]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_MUX_AUDIO], ret);
- goto EXIT;
+ goto err_disable_infra_audio_26m;
}
ret = clk_set_parent(afe_priv->clk[CLK_MUX_AUDIO],
afe_priv->clk[CLK_CLK26M]);
@@ -232,14 +242,14 @@ int mt8192_afe_enable_clock(struct mtk_base_afe *afe)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_MUX_AUDIO],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
+ goto err_disable_mux_audio;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_MUX_AUDIOINTBUS], ret);
- goto EXIT;
+ goto err_disable_mux_audio;
}
ret = mt8192_set_audio_int_bus_parent(afe, CLK_CLK26M);
@@ -247,7 +257,7 @@ int mt8192_afe_enable_clock(struct mtk_base_afe *afe)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_MUX_AUDIOINTBUS],
aud_clks[CLK_CLK26M], ret);
- goto EXIT;
+ goto err_disable_mux_audiointbus;
}
ret = clk_set_parent(afe_priv->clk[CLK_TOP_MUX_AUDIO_H],
@@ -256,17 +266,27 @@ int mt8192_afe_enable_clock(struct mtk_base_afe *afe)
dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[CLK_TOP_MUX_AUDIO_H],
aud_clks[CLK_TOP_APLL2_CK], ret);
- goto EXIT;
+ goto err_disable_mux_audiointbus;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_AFE]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_AFE], ret);
- goto EXIT;
+ goto err_disable_mux_audiointbus;
}
-EXIT:
+ return 0;
+
+err_disable_mux_audiointbus:
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIOINTBUS]);
+err_disable_mux_audio:
+ clk_disable_unprepare(afe_priv->clk[CLK_MUX_AUDIO]);
+err_disable_infra_audio_26m:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_AUDIO_26M]);
+err_disable_infra_sys_audio:
+ clk_disable_unprepare(afe_priv->clk[CLK_INFRA_SYS_AUDIO]);
+
return ret;
}
@@ -288,20 +308,22 @@ int mt8192_apll1_enable(struct mtk_base_afe *afe)
int ret;
/* setting for APLL */
- apll1_mux_setting(afe, true);
+ ret = apll1_mux_setting(afe, true);
+ if (ret)
+ return ret;
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL22M]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_APLL22M], ret);
- goto EXIT;
+ goto err_disable_mux_setting;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL1_TUNER]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_APLL1_TUNER], ret);
- goto EXIT;
+ goto err_disable_apll22m;
}
regmap_update_bits(afe->regmap, AFE_APLL1_TUNER_CFG,
@@ -312,7 +334,13 @@ int mt8192_apll1_enable(struct mtk_base_afe *afe)
AFE_22M_ON_MASK_SFT,
0x1 << AFE_22M_ON_SFT);
-EXIT:
+ return 0;
+
+err_disable_apll22m:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL22M]);
+err_disable_mux_setting:
+ apll1_mux_setting(afe, false);
+
return ret;
}
@@ -338,20 +366,22 @@ int mt8192_apll2_enable(struct mtk_base_afe *afe)
int ret;
/* setting for APLL */
- apll2_mux_setting(afe, true);
+ ret = apll2_mux_setting(afe, true);
+ if (ret)
+ return ret;
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL24M]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_APLL24M], ret);
- goto EXIT;
+ goto err_disable_mux_setting;
}
ret = clk_prepare_enable(afe_priv->clk[CLK_APLL2_TUNER]);
if (ret) {
dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
__func__, aud_clks[CLK_APLL2_TUNER], ret);
- goto EXIT;
+ goto err_disable_apll24m;
}
regmap_update_bits(afe->regmap, AFE_APLL2_TUNER_CFG,
@@ -362,7 +392,13 @@ int mt8192_apll2_enable(struct mtk_base_afe *afe)
AFE_24M_ON_MASK_SFT,
0x1 << AFE_24M_ON_SFT);
-EXIT:
+ return 0;
+
+err_disable_apll24m:
+ clk_disable_unprepare(afe_priv->clk[CLK_APLL24M]);
+err_disable_mux_setting:
+ apll2_mux_setting(afe, false);
+
return ret;
}
@@ -583,7 +619,7 @@ int mt8192_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
dev_err(afe->dev, "%s(), clk_set_parent %s-%s fail %d\n",
__func__, aud_clks[m_sel_id],
aud_clks[apll_clk_id], ret);
- return ret;
+ goto err_disable_m_sel;
}
}
@@ -592,17 +628,25 @@ int mt8192_mck_enable(struct mtk_base_afe *afe, int mck_id, int rate)
if (ret) {
dev_err(afe->dev, "%s(), clk_prepare_enable %s fail %d\n",
__func__, aud_clks[div_clk_id], ret);
- return ret;
+ goto err_disable_m_sel;
}
ret = clk_set_rate(afe_priv->clk[div_clk_id], rate);
if (ret) {
dev_err(afe->dev, "%s(), clk_set_rate %s, rate %d, fail %d\n",
__func__, aud_clks[div_clk_id],
rate, ret);
- return ret;
+ goto err_disable_div_clk;
}
return 0;
+
+err_disable_div_clk:
+ clk_disable_unprepare(afe_priv->clk[div_clk_id]);
+err_disable_m_sel:
+ if (m_sel_id >= 0)
+ clk_disable_unprepare(afe_priv->clk[m_sel_id]);
+
+ return ret;
}
void mt8192_mck_disable(struct mtk_base_afe *afe, int mck_id)
@@ -628,38 +672,29 @@ int mt8192_init_clock(struct mtk_base_afe *afe)
return -ENOMEM;
for (i = 0; i < CLK_NUM; i++) {
- afe_priv->clk[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe_priv->clk[i])) {
- dev_warn(afe->dev, "%s devm_clk_get %s fail, ret %ld\n",
- __func__,
- aud_clks[i], PTR_ERR(afe_priv->clk[i]));
- afe_priv->clk[i] = NULL;
- }
+ afe_priv->clk[i] = devm_clk_get_optional(afe->dev, aud_clks[i]);
+ if (IS_ERR(afe_priv->clk[i]))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->clk[i]),
+ "failed to get clock %s\n", aud_clks[i]);
}
afe_priv->apmixedsys = syscon_regmap_lookup_by_phandle(of_node,
"mediatek,apmixedsys");
- if (IS_ERR(afe_priv->apmixedsys)) {
- dev_err(afe->dev, "%s() Cannot find apmixedsys controller: %ld\n",
- __func__, PTR_ERR(afe_priv->apmixedsys));
- return PTR_ERR(afe_priv->apmixedsys);
- }
+ if (IS_ERR(afe_priv->apmixedsys))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->apmixedsys),
+ "Cannot find apmixedsys controller\n");
afe_priv->topckgen = syscon_regmap_lookup_by_phandle(of_node,
"mediatek,topckgen");
- if (IS_ERR(afe_priv->topckgen)) {
- dev_err(afe->dev, "%s() Cannot find topckgen controller: %ld\n",
- __func__, PTR_ERR(afe_priv->topckgen));
- return PTR_ERR(afe_priv->topckgen);
- }
+ if (IS_ERR(afe_priv->topckgen))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->topckgen),
+ "Cannot find topckgen controller\n");
afe_priv->infracfg = syscon_regmap_lookup_by_phandle(of_node,
"mediatek,infracfg");
- if (IS_ERR(afe_priv->infracfg)) {
- dev_err(afe->dev, "%s() Cannot find infracfg: %ld\n",
- __func__, PTR_ERR(afe_priv->infracfg));
- return PTR_ERR(afe_priv->infracfg);
- }
+ if (IS_ERR(afe_priv->infracfg))
+ return dev_err_probe(afe->dev, PTR_ERR(afe_priv->infracfg),
+ "Cannot find infracfg\n");
return 0;
}
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-gpio.c b/sound/soc/mediatek/mt8192/mt8192-afe-gpio.c
index b993ca2dbd7c..d2e99c55b8c6 100644
--- a/sound/soc/mediatek/mt8192/mt8192-afe-gpio.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-gpio.c
@@ -132,12 +132,8 @@ int mt8192_afe_gpio_init(struct device *dev)
int i, ret;
aud_pinctrl = devm_pinctrl_get(dev);
- if (IS_ERR(aud_pinctrl)) {
- ret = PTR_ERR(aud_pinctrl);
- dev_err(dev, "%s(), ret %d, cannot get aud_pinctrl!\n",
- __func__, ret);
- return ret;
- }
+ if (IS_ERR(aud_pinctrl))
+ return dev_err_probe(dev, PTR_ERR(aud_pinctrl), "cannot get aud_pinctrl!\n");
for (i = 0; i < ARRAY_SIZE(aud_gpios); i++) {
aud_gpios[i].gpioctrl = pinctrl_lookup_state(aud_pinctrl,
@@ -151,13 +147,17 @@ int mt8192_afe_gpio_init(struct device *dev)
}
}
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_CLK_MOSI_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_CLK_MOSI_ON);
+ if (ret)
+ return ret;
/* gpio status init */
- mt8192_afe_gpio_request(dev, false, MT8192_DAI_ADDA, 0);
- mt8192_afe_gpio_request(dev, false, MT8192_DAI_ADDA, 1);
+ ret = mt8192_afe_gpio_request(dev, false, MT8192_DAI_ADDA, 0);
+ if (ret)
+ return ret;
+ ret = mt8192_afe_gpio_request(dev, false, MT8192_DAI_ADDA, 1);
- return 0;
+ return ret;
}
EXPORT_SYMBOL(mt8192_afe_gpio_init);
@@ -208,98 +208,104 @@ static int mt8192_afe_gpio_adda_ch34_ul(struct device *dev, bool enable)
int mt8192_afe_gpio_request(struct device *dev, bool enable,
int dai, int uplink)
{
+ int ret;
+
guard(mutex)(&gpio_request_mutex);
switch (dai) {
case MT8192_DAI_ADDA:
if (uplink)
- mt8192_afe_gpio_adda_ul(dev, enable);
+ ret = mt8192_afe_gpio_adda_ul(dev, enable);
else
- mt8192_afe_gpio_adda_dl(dev, enable);
+ ret = mt8192_afe_gpio_adda_dl(dev, enable);
break;
case MT8192_DAI_ADDA_CH34:
if (uplink)
- mt8192_afe_gpio_adda_ch34_ul(dev, enable);
+ ret = mt8192_afe_gpio_adda_ch34_ul(dev, enable);
else
- mt8192_afe_gpio_adda_ch34_dl(dev, enable);
+ ret = mt8192_afe_gpio_adda_ch34_dl(dev, enable);
break;
case MT8192_DAI_I2S_0:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S0_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S0_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S0_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S0_OFF);
break;
case MT8192_DAI_I2S_1:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S1_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S1_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S1_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S1_OFF);
break;
case MT8192_DAI_I2S_2:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S2_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S2_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S2_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S2_OFF);
break;
case MT8192_DAI_I2S_3:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S3_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S3_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S3_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S3_OFF);
break;
case MT8192_DAI_I2S_5:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S5_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S5_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S5_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S5_OFF);
break;
case MT8192_DAI_I2S_6:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S6_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S6_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S6_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S6_OFF);
break;
case MT8192_DAI_I2S_7:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S7_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S7_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S7_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S7_OFF);
break;
case MT8192_DAI_I2S_8:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S8_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S8_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S8_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S8_OFF);
break;
case MT8192_DAI_I2S_9:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S9_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S9_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S9_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_I2S9_OFF);
break;
case MT8192_DAI_TDM:
if (enable)
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_TDM_ON);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_TDM_ON);
else
- mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_TDM_OFF);
+ ret = mt8192_afe_gpio_select(dev, MT8192_AFE_GPIO_TDM_OFF);
break;
case MT8192_DAI_VOW:
if (enable) {
- mt8192_afe_gpio_select(dev,
- MT8192_AFE_GPIO_VOW_CLK_ON);
- mt8192_afe_gpio_select(dev,
- MT8192_AFE_GPIO_VOW_DAT_ON);
+ ret = mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_CLK_ON);
+ if (ret)
+ break;
+ ret = mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_DAT_ON);
} else {
- mt8192_afe_gpio_select(dev,
- MT8192_AFE_GPIO_VOW_CLK_OFF);
- mt8192_afe_gpio_select(dev,
- MT8192_AFE_GPIO_VOW_DAT_OFF);
+ ret = mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_CLK_OFF);
+ if (ret)
+ break;
+ ret = mt8192_afe_gpio_select(dev,
+ MT8192_AFE_GPIO_VOW_DAT_OFF);
}
break;
default:
dev_warn(dev, "%s(), invalid dai %d\n", __func__, dai);
- return -EINVAL;
+ ret = -EINVAL;
}
- return 0;
+ return ret;
}
EXPORT_SYMBOL(mt8192_afe_gpio_request);
diff --git a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
index db0ae44a86af..45addf9f3790 100644
--- a/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-afe-pcm.c
@@ -2104,7 +2104,11 @@ static int mt8192_afe_runtime_resume(struct device *dev)
goto skip_regmap;
regcache_cache_only(afe->regmap, false);
- regcache_sync(afe->regmap);
+ ret = regcache_sync(afe->regmap);
+ if (ret) {
+ regcache_cache_only(afe->regmap, true);
+ mt8192_afe_disable_clock(afe);
+ }
/* enable audio sys DCM for power saving */
regmap_update_bits(afe_priv->infracfg,
@@ -2155,11 +2159,6 @@ static const dai_register_cb dai_register_cbs[] = {
mt8192_dai_memif_register,
};
-static void mt8192_afe_release_reserved_mem(void *data)
-{
- of_reserved_mem_device_release(data);
-}
-
static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
{
struct mtk_base_afe *afe;
@@ -2185,22 +2184,16 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
afe->dev = dev;
- ret = of_reserved_mem_device_init(dev);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret) {
dev_info(dev, "no reserved memory found, pre-allocating buffers instead\n");
afe->preallocate_buffers = true;
- } else {
- ret = devm_add_action_or_reset(dev, mt8192_afe_release_reserved_mem, dev);
- if (ret)
- return ret;
}
/* init audio related clock */
ret = mt8192_init_clock(afe);
- if (ret) {
- dev_err(dev, "init clock error\n");
+ if (ret)
return ret;
- }
/* reset controller to reset audio regs before regmap cache */
rstc = devm_reset_control_get_exclusive(dev, "audiosys");
@@ -2276,7 +2269,7 @@ static int mt8192_afe_pcm_dev_probe(struct platform_device *pdev)
ret = devm_request_irq(dev, irq_id, mt8192_afe_irq_handler,
IRQF_TRIGGER_NONE, "asys-isr", (void *)afe);
if (ret)
- return dev_err_probe(dev, ret, "could not request_irq for Afe_ISR_Handle\n");
+ return ret;
/* init sub_dais */
INIT_LIST_HEAD(&afe->sub_dais);
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-adda.c b/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
index f8cb84621d38..c5c726a6e423 100644
--- a/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-adda.c
@@ -218,10 +218,13 @@ static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
struct mt8192_afe_private *afe_priv = afe->platform_priv;
int mtkaif_dmic = afe_priv->mtkaif_dmic;
+ int ret;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 1);
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 1);
+ if (ret)
+ return ret;
/* update setting to dmic */
if (mtkaif_dmic) {
@@ -239,7 +242,9 @@ static int mtk_adda_ul_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMD:
/* should delayed 1/fs(smallest is 8k) = 125us before afe off */
usleep_range(125, 135);
- mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 1);
+ ret = mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 1);
+ if (ret)
+ return ret;
break;
default:
break;
@@ -257,11 +262,14 @@ static int mtk_adda_ch34_ul_event(struct snd_soc_dapm_widget *w,
struct mt8192_afe_private *afe_priv = afe->platform_priv;
int mtkaif_dmic = afe_priv->mtkaif_dmic_ch34;
int mtkaif_adda6_only = afe_priv->mtkaif_adda6_only;
+ int ret;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34,
- 1);
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34,
+ 1);
+ if (ret)
+ return ret;
/* update setting to dmic */
if (mtkaif_dmic) {
@@ -291,8 +299,10 @@ static int mtk_adda_ch34_ul_event(struct snd_soc_dapm_widget *w,
case SND_SOC_DAPM_POST_PMD:
/* should delayed 1/fs(smallest is 8k) = 125us before afe off */
usleep_range(125, 135);
- mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34,
- 1);
+ ret = mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34,
+ 1);
+ if (ret)
+ return ret;
/* reset dmic */
afe_priv->mtkaif_dmic_ch34 = 0;
@@ -446,15 +456,20 @@ static int mtk_adda_dl_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ int ret;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 0);
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 0);
+ if (ret)
+ return ret;
break;
case SND_SOC_DAPM_POST_PMD:
/* should delayed 1/fs(smallest is 8k) = 125us before afe off */
usleep_range(125, 135);
- mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 0);
+ ret = mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 0);
+ if (ret)
+ return ret;
break;
default:
break;
@@ -469,17 +484,22 @@ static int mtk_adda_ch34_dl_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ int ret;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34,
- 0);
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34,
+ 0);
+ if (ret)
+ return ret;
break;
case SND_SOC_DAPM_POST_PMD:
/* should delayed 1/fs(smallest is 8k) = 125us before afe off */
usleep_range(125, 135);
- mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34,
- 0);
+ ret = mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34,
+ 0);
+ if (ret)
+ return ret;
break;
default:
break;
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-i2s.c b/sound/soc/mediatek/mt8192/mt8192-dai-i2s.c
index 1632fc94776d..5608b534af0d 100644
--- a/sound/soc/mediatek/mt8192/mt8192-dai-i2s.c
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-i2s.c
@@ -586,6 +586,7 @@ static int mtk_i2s_en_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
struct mtk_afe_i2s_priv *i2s_priv;
+ int ret;
i2s_priv = get_i2s_priv_by_name(afe, w->name);
@@ -599,16 +600,17 @@ static int mtk_i2s_en_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_afe_gpio_request(afe->dev, true, i2s_priv->id, 0);
+ ret = mt8192_afe_gpio_request(afe->dev, true, i2s_priv->id, 0);
break;
case SND_SOC_DAPM_POST_PMD:
- mt8192_afe_gpio_request(afe->dev, false, i2s_priv->id, 0);
+ ret = mt8192_afe_gpio_request(afe->dev, false, i2s_priv->id, 0);
break;
default:
+ ret = 0;
break;
}
- return 0;
+ return ret;
}
static int mtk_apll_event(struct snd_soc_dapm_widget *w,
@@ -617,6 +619,7 @@ static int mtk_apll_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
+ int ret;
dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
__func__, w->name, event);
@@ -624,9 +627,11 @@ static int mtk_apll_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
- mt8192_apll1_enable(afe);
+ ret = mt8192_apll1_enable(afe);
else
- mt8192_apll2_enable(afe);
+ ret = mt8192_apll2_enable(afe);
+ if (ret)
+ return ret;
break;
case SND_SOC_DAPM_POST_PMD:
if (snd_soc_dapm_widget_name_cmp(w, APLL1_W_NAME) == 0)
@@ -704,6 +709,7 @@ static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
struct mtk_base_afe *afe = snd_soc_component_get_drvdata(cmpnt);
struct mtk_afe_i2s_priv *i2s_priv;
+ int ret;
dev_dbg(cmpnt->dev, "%s(), name %s, event 0x%x\n",
__func__, w->name, event);
@@ -716,7 +722,9 @@ static int mtk_mclk_en_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
+ ret = mt8192_mck_enable(afe, i2s_priv->mclk_id, i2s_priv->mclk_rate);
+ if (ret)
+ return ret;
break;
case SND_SOC_DAPM_POST_PMD:
i2s_priv->mclk_rate = 0;
diff --git a/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c b/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
index 49440db370af..36fa0f32ed1c 100644
--- a/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
+++ b/sound/soc/mediatek/mt8192/mt8192-dai-tdm.c
@@ -250,6 +250,7 @@ static int mtk_tdm_en_event(struct snd_soc_dapm_widget *w,
struct mt8192_afe_private *afe_priv = afe->platform_priv;
int dai_id = get_tdm_id_by_name(w->name);
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int ret;
if (!tdm_priv) {
dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
@@ -261,16 +262,17 @@ static int mtk_tdm_en_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_afe_gpio_request(afe->dev, true, tdm_priv->id, 0);
+ ret = mt8192_afe_gpio_request(afe->dev, true, tdm_priv->id, 0);
break;
case SND_SOC_DAPM_POST_PMD:
- mt8192_afe_gpio_request(afe->dev, false, tdm_priv->id, 0);
+ ret = mt8192_afe_gpio_request(afe->dev, false, tdm_priv->id, 0);
break;
default:
+ ret = 0;
break;
}
- return 0;
+ return ret;
}
static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
@@ -282,6 +284,7 @@ static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
struct mt8192_afe_private *afe_priv = afe->platform_priv;
int dai_id = get_tdm_id_by_name(w->name);
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int ret;
if (!tdm_priv) {
dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
@@ -293,7 +296,9 @@ static int mtk_tdm_bck_en_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
+ ret = mt8192_mck_enable(afe, tdm_priv->bck_id, tdm_priv->bck_rate);
+ if (ret)
+ return ret;
break;
case SND_SOC_DAPM_POST_PMD:
mt8192_mck_disable(afe, tdm_priv->bck_id);
@@ -314,6 +319,7 @@ static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
struct mt8192_afe_private *afe_priv = afe->platform_priv;
int dai_id = get_tdm_id_by_name(w->name);
struct mtk_afe_tdm_priv *tdm_priv = afe_priv->dai_priv[dai_id];
+ int ret;
if (!tdm_priv) {
dev_warn(afe->dev, "%s(), tdm_priv == NULL", __func__);
@@ -325,7 +331,9 @@ static int mtk_tdm_mck_en_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- mt8192_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ ret = mt8192_mck_enable(afe, tdm_priv->mclk_id, tdm_priv->mclk_rate);
+ if (ret)
+ return ret;
break;
case SND_SOC_DAPM_POST_PMD:
tdm_priv->mclk_rate = 0;
diff --git a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
index c86348691641..b010726e9b57 100644
--- a/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
+++ b/sound/soc/mediatek/mt8192/mt8192-mt6359-rt1015-rt5682.c
@@ -163,12 +163,23 @@ static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
int chosen_phase_1, chosen_phase_2, chosen_phase_3;
int counter;
int mtkaif_calib_ok;
+ int ret = 0;
- pm_runtime_get_sync(afe->dev);
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 1);
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 0);
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34, 1);
- mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34, 0);
+ ret = pm_runtime_resume_and_get(afe->dev);
+ if (ret < 0)
+ return ret;
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 1);
+ if (ret)
+ goto err_pm_put;
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA, 0);
+ if (ret)
+ goto err_disable_adda_1;
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34, 1);
+ if (ret)
+ goto err_disable_adda_0;
+ ret = mt8192_afe_gpio_request(afe->dev, true, MT8192_DAI_ADDA_CH34, 0);
+ if (ret)
+ goto err_disable_adda_ch34_1;
mt6359_mtkaif_calibration_enable(cmpnt_codec);
@@ -289,10 +300,14 @@ static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
mt6359_mtkaif_calibration_disable(cmpnt_codec);
- mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 1);
- mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 0);
- mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34, 1);
mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34, 0);
+err_disable_adda_ch34_1:
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA_CH34, 1);
+err_disable_adda_0:
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 0);
+err_disable_adda_1:
+ mt8192_afe_gpio_request(afe->dev, false, MT8192_DAI_ADDA, 1);
+err_pm_put:
pm_runtime_put(afe->dev);
dev_dbg(afe->dev, "%s(), mtkaif_chosen_phase[0/1/2]:%d/%d/%d\n",
@@ -301,7 +316,7 @@ static int mt8192_mt6359_mtkaif_calibration(struct snd_soc_pcm_runtime *rtd)
afe_priv->mtkaif_chosen_phase[1],
afe_priv->mtkaif_chosen_phase[2]);
- return 0;
+ return ret;
}
static int mt8192_mt6359_init(struct snd_soc_pcm_runtime *rtd)
@@ -319,9 +334,7 @@ static int mt8192_mt6359_init(struct snd_soc_pcm_runtime *rtd)
afe_priv->mtkaif_protocol = MTKAIF_PROTOCOL_2_CLK_P2;
/* mtkaif calibration */
- mt8192_mt6359_mtkaif_calibration(rtd);
-
- return 0;
+ return mt8192_mt6359_mtkaif_calibration(rtd);
}
static int mt8192_rt5682_init(struct snd_soc_pcm_runtime *rtd)
diff --git a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
index bc0a63457cd7..52c3381e6766 100644
--- a/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
+++ b/sound/soc/mediatek/mt8195/mt8195-afe-pcm.c
@@ -3013,7 +3013,7 @@ static int mt8195_afe_pcm_dev_probe(struct platform_device *pdev)
struct reset_control *rstc;
int i, irq_id, ret;
- ret = of_reserved_mem_device_init(dev);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret)
return dev_err_probe(dev, ret, "failed to assign memory region\n");
diff --git a/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c b/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
index a1ae8322d8b6..dcee037991aa 100644
--- a/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
+++ b/sound/soc/mediatek/mt8196/mt8196-afe-pcm.c
@@ -2309,11 +2309,6 @@ static const struct reg_sequence mt8196_cg_patch[] = {
{ AUDIO_TOP_CON4, 0x361c },
};
-static void mt8196_afe_release_reserved_mem(void *data)
-{
- of_reserved_mem_device_release(data);
-}
-
static int mt8196_afe_pcm_dev_probe(struct platform_device *pdev)
{
int ret, i;
@@ -2327,14 +2322,9 @@ static int mt8196_afe_pcm_dev_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = of_reserved_mem_device_init(dev);
- if (ret) {
+ ret = devm_of_reserved_mem_device_init(dev);
+ if (ret)
dev_err(dev, "failed to assign memory region: %d\n", ret);
- } else {
- ret = devm_add_action_or_reset(dev, mt8196_afe_release_reserved_mem, dev);
- if (ret)
- return ret;
- }
afe = devm_kzalloc(dev, sizeof(*afe), GFP_KERNEL);
if (!afe)
diff --git a/sound/soc/mxs/mxs-saif.c b/sound/soc/mxs/mxs-saif.c
index a77cd516a1bf..e46896ac9669 100644
--- a/sound/soc/mxs/mxs-saif.c
+++ b/sound/soc/mxs/mxs-saif.c
@@ -691,6 +691,14 @@ static int mxs_saif_trigger(struct snd_pcm_substream *substream, int cmd,
(SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE)
+static const u64 mxs_saif_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops mxs_saif_dai_ops = {
.startup = mxs_saif_startup,
.shutdown = mxs_saif_shutdown,
@@ -699,6 +707,8 @@ static const struct snd_soc_dai_ops mxs_saif_dai_ops = {
.hw_params = mxs_saif_hw_params,
.set_sysclk = mxs_saif_set_dai_sysclk,
.set_fmt = mxs_saif_set_dai_fmt,
+ .auto_selectable_formats = &mxs_saif_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver mxs_saif_dai = {
diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c
index fbbce81680cf..f9066fbdd571 100644
--- a/sound/soc/pxa/mmp-sspa.c
+++ b/sound/soc/pxa/mmp-sspa.c
@@ -339,6 +339,10 @@ static int mmp_sspa_probe(struct snd_soc_dai *dai)
SNDRV_PCM_FMTBIT_S24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 mmp_sspa_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops mmp_sspa_dai_ops = {
.probe = mmp_sspa_probe,
.startup = mmp_sspa_startup,
@@ -348,6 +352,8 @@ static const struct snd_soc_dai_ops mmp_sspa_dai_ops = {
.set_sysclk = mmp_sspa_set_dai_sysclk,
.set_pll = mmp_sspa_set_dai_pll,
.set_fmt = mmp_sspa_set_dai_fmt,
+ .auto_selectable_formats = &mmp_sspa_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver mmp_sspa_dai = {
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 3a0abcb0bfbd..9a98246ad19c 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -809,6 +809,15 @@ static int pxa_ssp_remove(struct snd_soc_dai *dai)
#define PXA_SSP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const u64 pxa_ssp_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops pxa_ssp_dai_ops = {
.probe = pxa_ssp_probe,
.remove = pxa_ssp_remove,
@@ -820,6 +829,8 @@ static const struct snd_soc_dai_ops pxa_ssp_dai_ops = {
.set_fmt = pxa_ssp_set_dai_fmt,
.set_tdm_slot = pxa_ssp_set_dai_tdm_slot,
.set_tristate = pxa_ssp_set_dai_tristate,
+ .auto_selectable_formats = &pxa_ssp_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver pxa_ssp_dai = {
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index fe1df78926f5..008cf5ae2607 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -328,6 +328,10 @@ static int pxa2xx_i2s_remove(struct snd_soc_dai *dai)
SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050 | SNDRV_PCM_RATE_44100 | \
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
+static const u64 pxa_i2s_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
.probe = pxa2xx_i2s_probe,
.remove = pxa2xx_i2s_remove,
@@ -337,6 +341,8 @@ static const struct snd_soc_dai_ops pxa_i2s_dai_ops = {
.hw_params = pxa2xx_i2s_hw_params,
.set_fmt = pxa2xx_i2s_set_dai_fmt,
.set_sysclk = pxa2xx_i2s_set_dai_sysclk,
+ .auto_selectable_formats = &pxa_i2s_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver pxa_i2s_dai = {
diff --git a/sound/soc/pxa/pxa2xx-pcm-lib.c b/sound/soc/pxa/pxa2xx-pcm-lib.c
index 88a9d3226302..4b5b07f0c444 100644
--- a/sound/soc/pxa/pxa2xx-pcm-lib.c
+++ b/sound/soc/pxa/pxa2xx-pcm-lib.c
@@ -79,6 +79,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_dmaengine_dai_dma_data *dma_params;
+ struct dma_chan *chan;
int ret;
runtime->hw = pxa2xx_pcm_hardware;
@@ -107,9 +108,11 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
if (ret < 0)
return ret;
- return snd_dmaengine_pcm_open(
- substream, dma_request_slave_channel(snd_soc_rtd_to_cpu(rtd, 0)->dev,
- dma_params->chan_name));
+ chan = dma_request_chan(snd_soc_rtd_to_cpu(rtd, 0)->dev, dma_params->chan_name);
+ if (IS_ERR(chan))
+ return -ENXIO;
+
+ return snd_dmaengine_pcm_open(substream, chan);
}
static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
diff --git a/sound/soc/qcom/common.c b/sound/soc/qcom/common.c
index d9f256d51973..83b745f617a8 100644
--- a/sound/soc/qcom/common.c
+++ b/sound/soc/qcom/common.c
@@ -339,13 +339,11 @@ static struct snd_soc_jack_pin qcom_headset_jack_pins[] = {
},
};
-int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
- struct snd_soc_jack *jack, bool *jack_setup)
+static int qcom_snd_headset_jack_init(struct snd_soc_card *card,
+ 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);
- struct snd_soc_card *card = rtd->card;
- int rval, i;
+ int rval;
if (!*jack_setup) {
rval = snd_soc_card_jack_new_pins(card, "Headset Jack",
@@ -369,6 +367,55 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
*jack_setup = true;
}
+ return 0;
+}
+
+int qcom_snd_headset_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup)
+{
+ struct snd_soc_dai *codec_dai;
+ struct snd_soc_card *card = rtd->card;
+ int rval, i;
+
+ rval = qcom_snd_headset_jack_init(card, jack, jack_setup);
+ if (rval)
+ return rval;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai) {
+ rval = snd_soc_component_set_jack(codec_dai->component, jack, NULL);
+ if (rval != 0 && rval != -ENOTSUPP) {
+ dev_warn(card->dev, "Failed to set jack: %d\n", rval);
+ qcom_snd_headset_jack_cleanup(rtd);
+ return rval;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qcom_snd_headset_jack_setup);
+
+void qcom_snd_headset_jack_cleanup(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *codec_dai;
+ int i;
+
+ for_each_rtd_codec_dais(rtd, i, codec_dai)
+ snd_soc_component_set_jack(codec_dai->component, NULL, NULL);
+}
+EXPORT_SYMBOL_GPL(qcom_snd_headset_jack_cleanup);
+
+int qcom_snd_wcd_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);
+ struct snd_soc_card *card = rtd->card;
+ int rval, i;
+
+ rval = qcom_snd_headset_jack_init(card, jack, jack_setup);
+ if (rval)
+ return rval;
+
switch (cpu_dai->id) {
case LPI_MI2S_RX_0:
case TX_CODEC_DMA_TX_0:
@@ -388,8 +435,6 @@ int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
default:
break;
}
-
-
return 0;
}
EXPORT_SYMBOL_GPL(qcom_snd_wcd_jack_setup);
diff --git a/sound/soc/qcom/common.h b/sound/soc/qcom/common.h
index c1deac109f24..37b185a4968f 100644
--- a/sound/soc/qcom/common.h
+++ b/sound/soc/qcom/common.h
@@ -7,7 +7,7 @@
#include <dt-bindings/sound/qcom,q6afe.h>
#include <sound/soc.h>
-#define LPASS_MAX_PORT (LPI_MI2S_TX_6 + 1)
+#define LPASS_MAX_PORT (AIF_TDM_TX_12 + 1)
struct qcom_snd_tdm_slot_cfg {
unsigned int tx_mask;
@@ -24,6 +24,9 @@ int qcom_snd_apply_dai_tdm_slots_cfg(struct snd_soc_pcm_runtime *rtd,
const struct qcom_snd_tdm_slot_cfg *cpu_cfg,
const struct qcom_snd_tdm_slot_cfg *codec_cfg);
int qcom_snd_apply_dai_tdm_slots(struct snd_soc_pcm_runtime *rtd);
+int qcom_snd_headset_jack_setup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_soc_jack *jack, bool *jack_setup);
+void qcom_snd_headset_jack_cleanup(struct snd_soc_pcm_runtime *rtd);
int qcom_snd_wcd_jack_setup(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_jack *jack, bool *jack_setup);
int qcom_snd_dp_jack_setup(struct snd_soc_pcm_runtime *rtd,
diff --git a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
index e204fd59e512..83f2711bceea 100644
--- a/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
+++ b/sound/soc/qcom/qdsp6/q6apm-lpass-dais.c
@@ -386,6 +386,7 @@ static int q6tdm_set_tdm_slot(struct snd_soc_dai *dai,
switch (dai->id) {
case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ case AIF_TDM_RX_0 ... AIF_TDM_TX_12:
slot_mask = (dai->id & 0x1) ? tx_mask : rx_mask;
if (slot_mask & ~cap_mask) {
dev_err(dai->dev, "%s: invalid slot mask 0x%x for %d slots\n",
@@ -474,6 +475,7 @@ static int of_q6apm_parse_dai_data(struct device *dev,
case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
case SENARY_MI2S_RX ... SENARY_MI2S_TX:
case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ case AIF_MI2S_RX_0 ... AIF_TDM_TX_12:
priv = &data->priv[id];
priv->mclk = of_clk_get_by_name(node, "mclk");
if (IS_ERR(priv->mclk)) {
diff --git a/sound/soc/qcom/qdsp6/q6asm-dai.c b/sound/soc/qcom/qdsp6/q6asm-dai.c
index 4f09fdd40905..cd4f82bb8cc7 100644
--- a/sound/soc/qcom/qdsp6/q6asm-dai.c
+++ b/sound/soc/qcom/qdsp6/q6asm-dai.c
@@ -551,7 +551,7 @@ static void compress_event_handler(uint32_t opcode, uint32_t token,
prtd->stream_id,
CMD_CLOSE);
/*
- * vaild stream ids start from 1, So we are
+ * valid stream ids start from 1, So we are
* toggling this between 1 and 2.
*/
prtd->stream_id = (prtd->stream_id == 1 ? 2 : 1);
diff --git a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
index c3d8116ad503..f422346a50c6 100644
--- a/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
+++ b/sound/soc/qcom/qdsp6/q6dsp-lpass-ports.c
@@ -548,6 +548,32 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
.id = SENARY_MI2S_TX,
.name = "SEN_MI2S_TX",
},
+ Q6AFE_MI2S_RX_DAI("AIF RX0", AIF_MI2S_RX_0),
+ Q6AFE_MI2S_RX_DAI("AIF RX1", AIF_MI2S_RX_1),
+ Q6AFE_MI2S_RX_DAI("AIF RX2", AIF_MI2S_RX_2),
+ Q6AFE_MI2S_RX_DAI("AIF RX3", AIF_MI2S_RX_3),
+ Q6AFE_MI2S_RX_DAI("AIF RX4", AIF_MI2S_RX_4),
+ Q6AFE_MI2S_RX_DAI("AIF RX5", AIF_MI2S_RX_5),
+ Q6AFE_MI2S_RX_DAI("AIF RX6", AIF_MI2S_RX_6),
+ Q6AFE_MI2S_RX_DAI("AIF RX7", AIF_MI2S_RX_7),
+ Q6AFE_MI2S_RX_DAI("AIF RX8", AIF_MI2S_RX_8),
+ Q6AFE_MI2S_RX_DAI("AIF RX9", AIF_MI2S_RX_9),
+ Q6AFE_MI2S_RX_DAI("AIF RX10", AIF_MI2S_RX_10),
+ Q6AFE_MI2S_RX_DAI("AIF RX11", AIF_MI2S_RX_11),
+ Q6AFE_MI2S_RX_DAI("AIF RX12", AIF_MI2S_RX_12),
+ Q6AFE_MI2S_TX_DAI("AIF TX0", AIF_MI2S_TX_0),
+ Q6AFE_MI2S_TX_DAI("AIF TX1", AIF_MI2S_TX_1),
+ Q6AFE_MI2S_TX_DAI("AIF TX2", AIF_MI2S_TX_2),
+ Q6AFE_MI2S_TX_DAI("AIF TX3", AIF_MI2S_TX_3),
+ Q6AFE_MI2S_TX_DAI("AIF TX4", AIF_MI2S_TX_4),
+ Q6AFE_MI2S_TX_DAI("AIF TX5", AIF_MI2S_TX_5),
+ Q6AFE_MI2S_TX_DAI("AIF TX6", AIF_MI2S_TX_6),
+ Q6AFE_MI2S_TX_DAI("AIF TX7", AIF_MI2S_TX_7),
+ Q6AFE_MI2S_TX_DAI("AIF TX8", AIF_MI2S_TX_8),
+ Q6AFE_MI2S_TX_DAI("AIF TX9", AIF_MI2S_TX_9),
+ Q6AFE_MI2S_TX_DAI("AIF TX10", AIF_MI2S_TX_10),
+ Q6AFE_MI2S_TX_DAI("AIF TX11", AIF_MI2S_TX_11),
+ Q6AFE_MI2S_TX_DAI("AIF TX12", AIF_MI2S_TX_12),
Q6AFE_MI2S_RX_DAI("LPI RX0", LPI_MI2S_RX_0),
Q6AFE_MI2S_RX_DAI("LPI RX1", LPI_MI2S_RX_1),
Q6AFE_MI2S_RX_DAI("LPI RX2", LPI_MI2S_RX_2),
@@ -562,6 +588,32 @@ static struct snd_soc_dai_driver q6dsp_audio_fe_dais[] = {
Q6AFE_MI2S_TX_DAI("LPI TX4", LPI_MI2S_TX_4),
Q6AFE_MI2S_TX_DAI("LPI TX5", LPI_MI2S_TX_5),
Q6AFE_MI2S_TX_DAI("LPI TX6", LPI_MI2S_TX_6),
+ Q6AFE_TDM_PB_DAI("AIF RX0", 0, AIF_TDM_RX_0),
+ Q6AFE_TDM_PB_DAI("AIF RX1", 1, AIF_TDM_RX_1),
+ Q6AFE_TDM_PB_DAI("AIF RX2", 2, AIF_TDM_RX_2),
+ Q6AFE_TDM_PB_DAI("AIF RX3", 3, AIF_TDM_RX_3),
+ Q6AFE_TDM_PB_DAI("AIF RX4", 4, AIF_TDM_RX_4),
+ Q6AFE_TDM_PB_DAI("AIF RX5", 5, AIF_TDM_RX_5),
+ Q6AFE_TDM_PB_DAI("AIF RX6", 6, AIF_TDM_RX_6),
+ Q6AFE_TDM_PB_DAI("AIF RX7", 7, AIF_TDM_RX_7),
+ Q6AFE_TDM_PB_DAI("AIF RX8", 8, AIF_TDM_RX_8),
+ Q6AFE_TDM_PB_DAI("AIF RX9", 9, AIF_TDM_RX_9),
+ Q6AFE_TDM_PB_DAI("AIF RX10", 10, AIF_TDM_RX_10),
+ Q6AFE_TDM_PB_DAI("AIF RX11", 11, AIF_TDM_RX_11),
+ Q6AFE_TDM_PB_DAI("AIF RX12", 12, AIF_TDM_RX_12),
+ Q6AFE_TDM_CAP_DAI("AIF TX0", 0, AIF_TDM_TX_0),
+ Q6AFE_TDM_CAP_DAI("AIF TX1", 1, AIF_TDM_TX_1),
+ Q6AFE_TDM_CAP_DAI("AIF TX2", 2, AIF_TDM_TX_2),
+ Q6AFE_TDM_CAP_DAI("AIF TX3", 3, AIF_TDM_TX_3),
+ Q6AFE_TDM_CAP_DAI("AIF TX4", 4, AIF_TDM_TX_4),
+ Q6AFE_TDM_CAP_DAI("AIF TX5", 5, AIF_TDM_TX_5),
+ Q6AFE_TDM_CAP_DAI("AIF TX6", 6, AIF_TDM_TX_6),
+ Q6AFE_TDM_CAP_DAI("AIF TX7", 7, AIF_TDM_TX_7),
+ Q6AFE_TDM_CAP_DAI("AIF TX8", 8, AIF_TDM_TX_8),
+ Q6AFE_TDM_CAP_DAI("AIF TX9", 9, AIF_TDM_TX_9),
+ Q6AFE_TDM_CAP_DAI("AIF TX10", 10, AIF_TDM_TX_10),
+ Q6AFE_TDM_CAP_DAI("AIF TX11", 11, AIF_TDM_TX_11),
+ Q6AFE_TDM_CAP_DAI("AIF TX12", 12, AIF_TDM_TX_12),
Q6AFE_TDM_PB_DAI("Primary", 0, PRIMARY_TDM_RX_0),
Q6AFE_TDM_PB_DAI("Primary", 1, PRIMARY_TDM_RX_1),
Q6AFE_TDM_PB_DAI("Primary", 2, PRIMARY_TDM_RX_2),
@@ -717,9 +769,11 @@ struct snd_soc_dai_driver *q6dsp_audio_ports_set_config(struct device *dev,
case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4:
case LPI_MI2S_RX_5 ... LPI_MI2S_TX_6:
+ case AIF_MI2S_RX_0 ... AIF_MI2S_TX_12:
q6dsp_audio_fe_dais[i].ops = cfg->q6i2s_ops;
break;
case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ case AIF_TDM_RX_0 ... AIF_TDM_TX_12:
q6dsp_audio_fe_dais[i].ops = cfg->q6tdm_ops;
break;
case WSA_CODEC_DMA_RX_0 ... RX_CODEC_DMA_RX_7:
diff --git a/sound/soc/qcom/sc8280xp.c b/sound/soc/qcom/sc8280xp.c
index 4d48e1012cd4..b5d367486d56 100644
--- a/sound/soc/qcom/sc8280xp.c
+++ b/sound/soc/qcom/sc8280xp.c
@@ -58,6 +58,25 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Speaker", NULL),
};
+enum sc8280xp_jack_setup {
+ SC8280XP_JACK_SETUP_NONE,
+ SC8280XP_JACK_SETUP_CODEC,
+ SC8280XP_JACK_SETUP_WCD,
+};
+
+struct sc8280xp_dai_data {
+ unsigned int id;
+ unsigned int mclk_rate;
+ bool codec_sysclk_set;
+ bool mi2s_mclk_enable;
+ bool mi2s_bclk_enable;
+ enum sc8280xp_jack_setup jack_setup;
+};
+
+#define SC8280XP_DAI_DATA(...) \
+ .dai_data = (const struct sc8280xp_dai_data[]) { __VA_ARGS__ }, \
+ .num_dai_data = ARRAY_SIZE(((const struct sc8280xp_dai_data[]) { __VA_ARGS__ }))
+
struct qcom_snd_soc_common {
const char *driver_name;
const struct snd_soc_dapm_widget *dapm_widgets;
@@ -71,6 +90,8 @@ struct qcom_snd_soc_common {
bool mi2s_mclk_enable;
bool mi2s_bclk_enable;
bool wcd_jack;
+ const struct sc8280xp_dai_data *dai_data;
+ size_t num_dai_data;
int (*snd_prepare)(struct snd_pcm_substream *substream);
};
@@ -83,6 +104,20 @@ struct sc8280xp_snd_data {
bool jack_setup;
};
+static const struct sc8280xp_dai_data *
+sc8280xp_get_dai_data(const struct qcom_snd_soc_common *common,
+ unsigned int id)
+{
+ size_t i;
+
+ for (i = 0; i < common->num_dai_data; i++) {
+ if (common->dai_data[i].id == id)
+ return &common->dai_data[i];
+ }
+
+ return NULL;
+}
+
static inline int sc8280xp_get_mclk_freq(struct snd_pcm_hw_params *params)
{
int rate = params_rate(params);
@@ -112,13 +147,22 @@ static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ const struct sc8280xp_dai_data *dai_data;
struct snd_soc_dai *codec_dai;
struct qcom_snd_tdm_slot_cfg cpu_cfg;
struct qcom_snd_tdm_slot_cfg codec_cfg;
+ bool codec_sysclk_set = data->priv->codec_sysclk_set;
+ bool mi2s_bclk_enable = data->priv->mi2s_bclk_enable;
int bclk_freq;
int ret;
int i;
+ dai_data = sc8280xp_get_dai_data(data->priv, cpu_dai->id);
+ if (dai_data) {
+ codec_sysclk_set = dai_data->codec_sysclk_set;
+ mi2s_bclk_enable = dai_data->mi2s_bclk_enable;
+ }
+
ret = qcom_snd_get_dai_tdm_slots(rtd, &cpu_cfg, &codec_cfg);
if (ret)
return ret == -ENOENT ? 0 : ret;
@@ -147,7 +191,7 @@ static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream,
if (bclk_freq <= 0)
return -EINVAL;
- if (data->priv->mi2s_bclk_enable) {
+ if (mi2s_bclk_enable) {
ret = snd_soc_dai_set_sysclk(cpu_dai, LPAIF_MI2S_BCLK, bclk_freq,
SND_SOC_CLOCK_IN);
if (ret && ret != -ENOTSUPP) {
@@ -157,7 +201,7 @@ static int sc8280xp_tdm_hw_params(struct snd_pcm_substream *substream,
}
}
- if (data->priv->codec_sysclk_set) {
+ if (codec_sysclk_set) {
for_each_rtd_codec_dais(rtd, i, codec_dai) {
ret = snd_soc_dai_set_sysclk(codec_dai, 0, bclk_freq,
SND_SOC_CLOCK_IN);
@@ -176,10 +220,13 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
{
struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ const struct sc8280xp_dai_data *dai_data;
struct snd_soc_card *card = rtd->card;
struct snd_soc_jack *dp_jack = NULL;
int dp_pcm_id = 0;
+ dai_data = sc8280xp_get_dai_data(data->priv, cpu_dai->id);
+
switch (cpu_dai->id) {
case WSA_CODEC_DMA_RX_0:
case WSA_CODEC_DMA_RX_1:
@@ -209,10 +256,24 @@ static int sc8280xp_snd_init(struct snd_soc_pcm_runtime *rtd)
if (dp_jack)
return qcom_snd_dp_jack_setup(rtd, dp_jack, dp_pcm_id);
- if (data->priv->wcd_jack)
- return qcom_snd_wcd_jack_setup(rtd, &data->jack, &data->jack_setup);
+ if (!dai_data) {
+ if (data->priv->wcd_jack)
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack,
+ &data->jack_setup);
- return 0;
+ return 0;
+ }
+
+ switch (dai_data->jack_setup) {
+ case SC8280XP_JACK_SETUP_CODEC:
+ return qcom_snd_headset_jack_setup(rtd, &data->jack,
+ &data->jack_setup);
+ case SC8280XP_JACK_SETUP_WCD:
+ return qcom_snd_wcd_jack_setup(rtd, &data->jack,
+ &data->jack_setup);
+ default:
+ return 0;
+ }
}
static int sc8280xp_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -251,15 +312,30 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
- int mclk_freq = sc8280xp_get_mclk_freq(params);
+ const struct sc8280xp_dai_data *dai_data;
+ bool codec_sysclk_set = data->priv->codec_sysclk_set;
+ bool mi2s_mclk_enable = data->priv->mi2s_mclk_enable;
+ bool mi2s_bclk_enable = data->priv->mi2s_bclk_enable;
+ int mclk_freq;
int bclk_freq = sc8280xp_get_bclk_freq(params);
int ret;
+ dai_data = sc8280xp_get_dai_data(data->priv, cpu_dai->id);
+ mclk_freq = sc8280xp_get_mclk_freq(params);
+ if (dai_data) {
+ codec_sysclk_set = dai_data->codec_sysclk_set;
+ mi2s_mclk_enable = dai_data->mi2s_mclk_enable;
+ mi2s_bclk_enable = dai_data->mi2s_bclk_enable;
+ if (dai_data->mclk_rate)
+ mclk_freq = dai_data->mclk_rate;
+ }
+
switch (cpu_dai->id) {
case PRIMARY_MI2S_RX ... QUATERNARY_MI2S_TX:
case QUINARY_MI2S_RX ... QUINARY_MI2S_TX:
case SENARY_MI2S_RX ... SENARY_MI2S_TX:
case LPI_MI2S_RX_0 ... LPI_MI2S_TX_4:
+ case AIF_MI2S_RX_0 ... AIF_MI2S_TX_12:
ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_BP_FP);
if (ret && ret != -ENOTSUPP)
return ret;
@@ -271,7 +347,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- if (data->priv->mi2s_mclk_enable) {
+ if (mi2s_mclk_enable) {
ret = snd_soc_dai_set_sysclk(cpu_dai,
LPAIF_MI2S_MCLK, mclk_freq,
SND_SOC_CLOCK_OUT);
@@ -279,7 +355,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- if (data->priv->mi2s_bclk_enable) {
+ if (mi2s_bclk_enable) {
ret = snd_soc_dai_set_sysclk(cpu_dai,
LPAIF_MI2S_BCLK, bclk_freq,
SND_SOC_CLOCK_OUT);
@@ -287,7 +363,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
return ret;
}
- if (data->priv->codec_sysclk_set) {
+ if (codec_sysclk_set) {
ret = snd_soc_dai_set_sysclk(codec_dai,
0, mclk_freq,
SND_SOC_CLOCK_IN);
@@ -296,6 +372,7 @@ static int sc8280xp_snd_hw_params(struct snd_pcm_substream *substream,
}
break;
case PRIMARY_TDM_RX_0 ... QUINARY_TDM_TX_7:
+ case AIF_TDM_RX_0 ... AIF_TDM_TX_12:
return sc8280xp_tdm_hw_params(substream, params);
default:
break;
@@ -368,6 +445,19 @@ static int sc8280xp_snd_hw_free(struct snd_pcm_substream *substream)
return qcom_snd_sdw_hw_free(substream, &data->stream_prepared[cpu_dai->id]);
}
+static void sc8280xp_snd_exit(struct snd_soc_pcm_runtime *rtd)
+{
+ struct sc8280xp_snd_data *data = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
+ const struct sc8280xp_dai_data *dai_data;
+
+ dai_data = sc8280xp_get_dai_data(data->priv, cpu_dai->id);
+ if (dai_data && dai_data->jack_setup == SC8280XP_JACK_SETUP_CODEC) {
+ qcom_snd_headset_jack_cleanup(rtd);
+ data->jack_setup = false;
+ }
+}
+
static const struct snd_soc_ops sc8280xp_be_ops = {
.startup = qcom_snd_sdw_startup,
.shutdown = qcom_snd_sdw_shutdown,
@@ -384,6 +474,7 @@ static void sc8280xp_add_be_ops(struct snd_soc_card *card)
for_each_card_prelinks(card, i, link) {
if (link->no_pcm == 1) {
link->init = sc8280xp_snd_init;
+ link->exit = sc8280xp_snd_exit;
link->be_hw_params_fixup = sc8280xp_be_hw_params_fixup;
link->ops = &sc8280xp_be_ops;
}
@@ -488,6 +579,28 @@ static const struct qcom_snd_soc_common qcs6490_priv_data = {
.wcd_jack = true,
};
+static const struct qcom_snd_soc_common rubikpi3_priv_data = {
+ .driver_name = "qcs6490",
+ .dapm_widgets = sc8280xp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sc8280xp_dapm_widgets),
+ .codec_dai_fmt = SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_BC_FC,
+ SC8280XP_DAI_DATA({
+ .id = PRIMARY_MI2S_RX,
+ .mclk_rate = 19200000,
+ .codec_sysclk_set = true,
+ .mi2s_bclk_enable = true,
+ .jack_setup = SC8280XP_JACK_SETUP_CODEC,
+ }, {
+ .id = PRIMARY_MI2S_TX,
+ .mclk_rate = 19200000,
+ .codec_sysclk_set = true,
+ .mi2s_bclk_enable = true,
+ }
+ ),
+};
+
static const struct qcom_snd_soc_common qcs8275_priv_data = {
.driver_name = "qcs8300",
.dapm_widgets = max98090_dapm_widgets,
@@ -567,6 +680,7 @@ static const struct of_device_id snd_sc8280xp_dt_match[] = {
{ .compatible = "qcom,qcm6490-idp-sndcard", .data = &qcm6490_priv_data },
{ .compatible = "qcom,qcs615-sndcard", .data = &qcs615_priv_data },
{ .compatible = "qcom,qcs6490-rb3gen2-sndcard", .data = &qcs6490_priv_data },
+ { .compatible = "thundercomm,qcs6490-rubikpi3-sndcard", .data = &rubikpi3_priv_data },
{ .compatible = "qcom,qcs8275-sndcard", .data = &qcs8275_priv_data },
{ .compatible = "qcom,qcs9075-sndcard", .data = &qcs9100_priv_data },
{ .compatible = "qcom,qcs9100-sndcard", .data = &qcs9100_priv_data },
diff --git a/sound/soc/qcom/sdm845.c b/sound/soc/qcom/sdm845.c
index 6843ab8ba017..f0fbec9c8df2 100644
--- a/sound/soc/qcom/sdm845.c
+++ b/sound/soc/qcom/sdm845.c
@@ -23,9 +23,14 @@
#define DEFAULT_MCLK_RATE 24576000
#define TDM_BCLK_RATE 6144000
#define MI2S_BCLK_RATE 1536000
-#define LEFT_SPK_TDM_TX_MASK 0x30
-#define RIGHT_SPK_TDM_TX_MASK 0xC0
-#define SPK_TDM_RX_MASK 0x03
+#define LEFT_SPK_TDM_RX_MASK BIT(0)
+#define RIGHT_SPK_TDM_RX_MASK BIT(1)
+#define SPK_TDM_RX_MASK (LEFT_SPK_TDM_RX_MASK | RIGHT_SPK_TDM_RX_MASK)
+#define MIC1_TDM_RX_MASK BIT(2)
+#define MIC2_TDM_RX_MASK BIT(3)
+#define MIC_TDM_RX_MASK (MIC1_TDM_RX_MASK | MIC2_TDM_RX_MASK)
+#define LEFT_SPK_TDM_TX_MASK (BIT(4) | BIT(5))
+#define RIGHT_SPK_TDM_TX_MASK (BIT(6) | BIT(7))
#define NUM_TDM_SLOTS 8
#define SLIM_MAX_TX_PORTS 16
#define SLIM_MAX_RX_PORTS 13
@@ -112,8 +117,8 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream,
channels = params_channels(params);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, 0x3,
- 8, slot_width);
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0, SPK_TDM_RX_MASK,
+ NUM_TDM_SLOTS, slot_width);
if (ret < 0) {
dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n",
__func__, ret);
@@ -128,8 +133,10 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream,
goto end;
}
} else {
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, 0xf, 0,
- 8, slot_width);
+ ret = snd_soc_dai_set_tdm_slot(cpu_dai,
+ SPK_TDM_RX_MASK |
+ MIC_TDM_RX_MASK, 0,
+ NUM_TDM_SLOTS, slot_width);
if (ret < 0) {
dev_err(rtd->dev, "%s: failed to set tdm slot, err:%d\n",
__func__, ret);
@@ -150,7 +157,7 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream,
if (!strcmp(codec_dai->component->name_prefix, "Left")) {
ret = snd_soc_dai_set_tdm_slot(
codec_dai, LEFT_SPK_TDM_TX_MASK,
- SPK_TDM_RX_MASK, NUM_TDM_SLOTS,
+ LEFT_SPK_TDM_RX_MASK, NUM_TDM_SLOTS,
slot_width);
if (ret < 0) {
dev_err(rtd->dev,
@@ -162,7 +169,7 @@ static int sdm845_tdm_snd_hw_params(struct snd_pcm_substream *substream,
if (!strcmp(codec_dai->component->name_prefix, "Right")) {
ret = snd_soc_dai_set_tdm_slot(
codec_dai, RIGHT_SPK_TDM_TX_MASK,
- SPK_TDM_RX_MASK, NUM_TDM_SLOTS,
+ RIGHT_SPK_TDM_RX_MASK, NUM_TDM_SLOTS,
slot_width);
if (ret < 0) {
dev_err(rtd->dev,
@@ -380,7 +387,7 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
TDM_BCLK_RATE, SNDRV_PCM_STREAM_PLAYBACK);
}
- codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_B;
+ codec_dai_fmt |= SND_SOC_DAIFMT_IB_NF | SND_SOC_DAIFMT_DSP_A;
for_each_rtd_codec_dais(rtd, j, codec_dai) {
@@ -405,6 +412,28 @@ static int sdm845_snd_startup(struct snd_pcm_substream *substream)
return ret;
}
}
+
+ /* Set codec sysclk needed by codecs like cs35l36. */
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0,
+ TDM_BCLK_RATE,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0 && ret != -ENOTSUPP) {
+ dev_err(codec_dai->dev,
+ "Failed to set codec dai sysclk: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = snd_soc_component_set_sysclk(codec_dai->component,
+ 0, 0,
+ TDM_BCLK_RATE,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0 && ret != -ENOTSUPP) {
+ dev_err(codec_dai->dev,
+ "Failed to set codec component sysclk: %d\n",
+ ret);
+ return ret;
+ }
}
break;
case SLIMBUS_0_RX...SLIMBUS_6_TX:
diff --git a/sound/soc/renesas/rz-ssi.c b/sound/soc/renesas/rz-ssi.c
index 9fe8a639c47c..b1587f499cb8 100644
--- a/sound/soc/renesas/rz-ssi.c
+++ b/sound/soc/renesas/rz-ssi.c
@@ -900,6 +900,13 @@ static int rz_ssi_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 rz_ssi_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops rz_ssi_dai_ops = {
.probe = rz_ssi_dai_probe,
.startup = rz_ssi_startup,
@@ -907,6 +914,8 @@ static const struct snd_soc_dai_ops rz_ssi_dai_ops = {
.trigger = rz_ssi_dai_trigger,
.set_fmt = rz_ssi_dai_set_fmt,
.hw_params = rz_ssi_dai_hw_params,
+ .auto_selectable_formats = &rz_ssi_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_pcm_hardware rz_ssi_pcm_hardware = {
diff --git a/sound/soc/renesas/siu_dai.c b/sound/soc/renesas/siu_dai.c
index 85dee9025710..fff6ef870f0d 100644
--- a/sound/soc/renesas/siu_dai.c
+++ b/sound/soc/renesas/siu_dai.c
@@ -688,12 +688,18 @@ epclkget:
return ret;
}
+static const u64 siu_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops siu_dai_ops = {
.startup = siu_dai_startup,
.shutdown = siu_dai_shutdown,
.prepare = siu_dai_prepare,
.set_sysclk = siu_dai_set_sysclk,
.set_fmt = siu_dai_set_fmt,
+ .auto_selectable_formats = &siu_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver siu_i2s_dai = {
diff --git a/sound/soc/renesas/ssi.c b/sound/soc/renesas/ssi.c
index 96cf523c2273..f00067bafe8d 100644
--- a/sound/soc/renesas/ssi.c
+++ b/sound/soc/renesas/ssi.c
@@ -329,6 +329,17 @@ static int ssi_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_U24_3LE | \
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE)
+static const u64 ssi_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_GATED |
+ SND_SOC_POSSIBLE_DAIFMT_CONT |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops ssi_dai_ops = {
.startup = ssi_startup,
.shutdown = ssi_shutdown,
@@ -337,6 +348,8 @@ static const struct snd_soc_dai_ops ssi_dai_ops = {
.set_sysclk = ssi_set_sysclk,
.set_clkdiv = ssi_set_clkdiv,
.set_fmt = ssi_set_fmt,
+ .auto_selectable_formats = &ssi_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver sh4_ssi_dai[] = {
diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c
index b80acb221d24..04e7bed8275d 100644
--- a/sound/soc/rockchip/rk3399_gru_sound.c
+++ b/sound/soc/rockchip/rk3399_gru_sound.c
@@ -505,8 +505,8 @@ static int rockchip_sound_of_parse_dais(struct device *dev,
int i, index;
int num_routes;
- card->dai_link = devm_kzalloc(dev, sizeof(rockchip_dais),
- GFP_KERNEL);
+ card->dai_link = devm_kcalloc(dev, ARRAY_SIZE(rockchip_dais),
+ sizeof(*card->dai_link), GFP_KERNEL);
if (!card->dai_link)
return -ENOMEM;
diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
index 261f36d4c2fd..ef3f6586af77 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -537,13 +537,26 @@ static int rockchip_i2s_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 rockchip_i2s_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops rockchip_i2s_dai_ops = {
- .probe = rockchip_i2s_dai_probe,
- .hw_params = rockchip_i2s_hw_params,
- .set_bclk_ratio = rockchip_i2s_set_bclk_ratio,
- .set_sysclk = rockchip_i2s_set_sysclk,
- .set_fmt = rockchip_i2s_set_fmt,
- .trigger = rockchip_i2s_trigger,
+ .probe = rockchip_i2s_dai_probe,
+ .hw_params = rockchip_i2s_hw_params,
+ .set_bclk_ratio = rockchip_i2s_set_bclk_ratio,
+ .set_sysclk = rockchip_i2s_set_sysclk,
+ .set_fmt = rockchip_i2s_set_fmt,
+ .trigger = rockchip_i2s_trigger,
+ .auto_selectable_formats = &rockchip_i2s_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver rockchip_i2s_dai = {
diff --git a/sound/soc/rockchip/rockchip_i2s_tdm.c b/sound/soc/rockchip/rockchip_i2s_tdm.c
index 5ef7f109706d..983319a538b1 100644
--- a/sound/soc/rockchip/rockchip_i2s_tdm.c
+++ b/sound/soc/rockchip/rockchip_i2s_tdm.c
@@ -826,14 +826,27 @@ static int rockchip_i2s_tdm_set_bclk_ratio(struct snd_soc_dai *dai,
return 0;
}
+static const u64 rockchip_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops rockchip_i2s_tdm_dai_ops = {
- .probe = rockchip_i2s_tdm_dai_probe,
- .hw_params = rockchip_i2s_tdm_hw_params,
- .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio,
- .set_fmt = rockchip_i2s_tdm_set_fmt,
- .set_sysclk = rockchip_i2s_tdm_set_sysclk,
- .set_tdm_slot = rockchip_dai_tdm_slot,
- .trigger = rockchip_i2s_tdm_trigger,
+ .probe = rockchip_i2s_tdm_dai_probe,
+ .hw_params = rockchip_i2s_tdm_hw_params,
+ .set_bclk_ratio = rockchip_i2s_tdm_set_bclk_ratio,
+ .set_fmt = rockchip_i2s_tdm_set_fmt,
+ .set_sysclk = rockchip_i2s_tdm_set_sysclk,
+ .set_tdm_slot = rockchip_dai_tdm_slot,
+ .trigger = rockchip_i2s_tdm_trigger,
+ .auto_selectable_formats = &rockchip_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_component_driver rockchip_i2s_tdm_component = {
diff --git a/sound/soc/rockchip/rockchip_pdm.c b/sound/soc/rockchip/rockchip_pdm.c
index 343fda478901..0720748c7c8d 100644
--- a/sound/soc/rockchip/rockchip_pdm.c
+++ b/sound/soc/rockchip/rockchip_pdm.c
@@ -381,11 +381,17 @@ static int rockchip_pdm_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 rockchip_pdm_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF;
+
static const struct snd_soc_dai_ops rockchip_pdm_dai_ops = {
- .probe = rockchip_pdm_dai_probe,
- .set_fmt = rockchip_pdm_set_fmt,
- .trigger = rockchip_pdm_trigger,
- .hw_params = rockchip_pdm_hw_params,
+ .probe = rockchip_pdm_dai_probe,
+ .set_fmt = rockchip_pdm_set_fmt,
+ .trigger = rockchip_pdm_trigger,
+ .hw_params = rockchip_pdm_hw_params,
+ .auto_selectable_formats = &rockchip_pdm_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define ROCKCHIP_PDM_RATES SNDRV_PCM_RATE_8000_192000
diff --git a/sound/soc/rockchip/rockchip_sai.c b/sound/soc/rockchip/rockchip_sai.c
index 30b5e71d0937..9dfe46ffe0ff 100644
--- a/sound/soc/rockchip/rockchip_sai.c
+++ b/sound/soc/rockchip/rockchip_sai.c
@@ -957,16 +957,29 @@ static int rockchip_sai_set_sysclk(struct snd_soc_dai *dai, int stream,
return 0;
}
+static const u64 rockchip_sai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops 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,
+ .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,
+ .auto_selectable_formats = &rockchip_sai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_driver rockchip_sai_dai = {
diff --git a/sound/soc/samsung/aries_wm8994.c b/sound/soc/samsung/aries_wm8994.c
index d8659973540f..1a5b53f47571 100644
--- a/sound/soc/samsung/aries_wm8994.c
+++ b/sound/soc/samsung/aries_wm8994.c
@@ -374,17 +374,15 @@ static int aries_late_probe(struct snd_soc_card *card)
irq = gpiod_to_irq(priv->gpio_headset_detect);
if (irq < 0) {
dev_err(card->dev, "Failed to map headset detect gpio to irq");
- return -EINVAL;
+ return irq;
}
ret = devm_request_threaded_irq(card->dev, irq, NULL,
headset_det_irq_thread,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING |
IRQF_ONESHOT, "headset_detect", priv);
- if (ret) {
- dev_err(card->dev, "Failed to request headset detect irq");
+ if (ret)
return ret;
- }
headset_button_gpio[0].data = priv;
headset_button_gpio[0].desc = priv->gpio_headset_key;
@@ -623,10 +621,8 @@ static int aries_audio_probe(struct platform_device *pdev)
if (ret < 0) {
/* Backwards compatible way */
ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
- if (ret < 0) {
- dev_err(dev, "Audio routing invalid/unspecified\n");
+ if (ret < 0)
return ret;
- }
}
aries_dai[1].dai_fmt = priv->variant->modem_dai_fmt;
@@ -671,10 +667,8 @@ static int aries_audio_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_component(dev, &aries_component,
aries_ext_dai, ARRAY_SIZE(aries_ext_dai));
- if (ret < 0) {
- dev_err(dev, "Failed to register component: %d\n", ret);
+ if (ret < 0)
goto out;
- }
ret = devm_snd_soc_register_card(dev, card);
if (ret)
diff --git a/sound/soc/samsung/pcm.c b/sound/soc/samsung/pcm.c
index 309f024bf2a4..8430686760d7 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -499,10 +499,9 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
return PTR_ERR(pcm->regs);
pcm->cclk = devm_clk_get(&pdev->dev, "audio-bus");
- if (IS_ERR(pcm->cclk)) {
- dev_err(&pdev->dev, "failed to get audio-bus clock\n");
- return PTR_ERR(pcm->cclk);
- }
+ if (IS_ERR(pcm->cclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(pcm->cclk),
+ "failed to get audio-bus clock\n");
ret = clk_prepare_enable(pcm->cclk);
if (ret)
return ret;
@@ -512,8 +511,8 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
pcm->pclk = devm_clk_get(&pdev->dev, "pcm");
if (IS_ERR(pcm->pclk)) {
- dev_err(&pdev->dev, "failed to get pcm clock\n");
- ret = PTR_ERR(pcm->pclk);
+ ret = dev_err_probe(&pdev->dev, PTR_ERR(pcm->pclk),
+ "failed to get pcm clock\n");
goto err_dis_cclk;
}
ret = clk_prepare_enable(pcm->pclk);
@@ -535,19 +534,15 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
NULL, NULL, NULL);
- if (ret) {
- dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
+ if (ret)
goto err_dis_pclk;
- }
pm_runtime_enable(&pdev->dev);
ret = devm_snd_soc_register_component(&pdev->dev, &s3c_pcm_component,
&s3c_pcm_dai[pdev->id], 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "failed to get register DAI: %d\n", ret);
+ if (ret != 0)
goto err_dis_pm;
- }
return 0;
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index 53eaabaf8956..719083e8e89d 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -415,19 +415,15 @@ static int spdif_probe(struct platform_device *pdev)
ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
NULL, NULL, NULL);
- if (ret) {
- dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
+ if (ret)
goto err2;
- }
dev_set_drvdata(&pdev->dev, spdif);
ret = devm_snd_soc_register_component(&pdev->dev,
&samsung_spdif_component, &samsung_spdif_dai, 1);
- if (ret != 0) {
- dev_err(&pdev->dev, "fail to register dai\n");
+ if (ret != 0)
goto err2;
- }
return 0;
err2:
diff --git a/sound/soc/samsung/tm2_wm5110.c b/sound/soc/samsung/tm2_wm5110.c
index f1f59e059f5d..f4912826dfd4 100644
--- a/sound/soc/samsung/tm2_wm5110.c
+++ b/sound/soc/samsung/tm2_wm5110.c
@@ -518,19 +518,15 @@ static int tm2_probe(struct platform_device *pdev)
}
ret = snd_soc_of_parse_card_name(card, "model");
- if (ret < 0) {
- dev_err(dev, "Card name is not specified\n");
+ if (ret < 0)
return ret;
- }
ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
if (ret < 0) {
/* Backwards compatible way */
ret = snd_soc_of_parse_audio_routing(card, "samsung,audio-routing");
- if (ret < 0) {
- dev_err(dev, "Audio routing is not specified or invalid\n");
+ if (ret < 0)
return ret;
- }
}
card->aux_dev[0].dlc.of_node = of_parse_phandle(dev->of_node,
@@ -609,10 +605,8 @@ static int tm2_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_component(dev, &tm2_component,
tm2_ext_dai, ARRAY_SIZE(tm2_ext_dai));
- if (ret < 0) {
- dev_err(dev, "Failed to register component: %d\n", ret);
+ if (ret < 0)
goto dai_node_put;
- }
ret = devm_snd_soc_register_card(dev, card);
if (ret < 0) {
diff --git a/sound/soc/sdca/Kconfig b/sound/soc/sdca/Kconfig
index 4c0dcb9ff3b9..c371323cc36e 100644
--- a/sound/soc/sdca/Kconfig
+++ b/sound/soc/sdca/Kconfig
@@ -3,11 +3,13 @@ menu "SoundWire (SDCA)"
config SND_SOC_SDCA
tristate "SDCA core support"
- depends on ACPI
select AUXILIARY_BUS
help
This option enables support for the MIPI SoundWire Device
- Class for Audio (SDCA).
+ Class for Audio (SDCA). The ACPI-specific enumeration
+ helpers are only built when CONFIG_ACPI is enabled; on
+ non-ACPI platforms the SDCA function data is supplied
+ statically by the codec driver.
config SND_SOC_SDCA_HID
bool "SDCA HID support"
diff --git a/sound/soc/sdca/sdca_asoc.c b/sound/soc/sdca/sdca_asoc.c
index ce2e7c270765..ccc579f878ff 100644
--- a/sound/soc/sdca/sdca_asoc.c
+++ b/sound/soc/sdca/sdca_asoc.c
@@ -1507,7 +1507,7 @@ int sdca_asoc_set_constraints(struct device *dev, struct regmap *regmap,
return ret;
}
- dai->priv = constraint;
+ snd_soc_dai_set_priv(dai, constraint);
return 0;
}
@@ -1523,7 +1523,7 @@ EXPORT_SYMBOL_NS(sdca_asoc_set_constraints, "SND_SOC_SDCA");
void sdca_asoc_free_constraints(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct snd_pcm_hw_constraint_list *constraint = dai->priv;
+ struct snd_pcm_hw_constraint_list *constraint = snd_soc_dai_to_priv(dai);
kfree(constraint);
}
diff --git a/sound/soc/sdca/sdca_class.c b/sound/soc/sdca/sdca_class.c
index d7444f442c71..79fdb672be1d 100644
--- a/sound/soc/sdca/sdca_class.c
+++ b/sound/soc/sdca/sdca_class.c
@@ -20,7 +20,7 @@
#include <sound/sdca_function.h>
#include <sound/sdca_interrupts.h>
#include <sound/sdca_regmap.h>
-#include "sdca_class.h"
+#include <sound/sdca_class.h>
#define CLASS_SDW_ATTACH_TIMEOUT_MS 5000
@@ -136,18 +136,35 @@ err:
pm_runtime_put_sync(drv->dev);
}
-static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
+/**
+ * sdca_class_probe - SDCA class SoundWire slave probe helper
+ * @sdw: SoundWire slave
+ * @drv: caller-allocated sdca_class_drv storage. The caller (a codec
+ * driver, or the built-in class_sdw_driver in this file) owns the
+ * allocation and sets its own dev_set_drvdata() -- the framework
+ * does not touch drvdata. Typically embedded in the codec's own
+ * priv struct so codec drivers can keep per-slave state.
+ * @ops: optional codec-provided class callbacks (may be NULL for
+ * pure-generic SDCA parts that need no quirks)
+ *
+ * Codec-specific SoundWire drivers call this from their .probe after
+ * allocating a struct sdca_class_drv (usually embedded in their own
+ * priv) and setting drvdata to their priv. The framework fills in the
+ * sdca_class_drv fields, sets up the class regmap, and queues the
+ * deferred boot work.
+ */
+int sdca_class_probe(struct sdw_slave *sdw,
+ struct sdca_class_drv *drv,
+ const struct sdca_class_ops *ops)
{
struct device *dev = &sdw->dev;
struct regmap_config *dev_config;
- struct sdca_class_drv *drv;
int ret;
sdca_lookup_swft(sdw);
- drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
if (!drv)
- return -ENOMEM;
+ return -EINVAL;
dev_config = devm_kmemdup(dev, &class_dev_regmap_config,
sizeof(*dev_config), GFP_KERNEL);
@@ -156,11 +173,10 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
drv->dev = dev;
drv->sdw = sdw;
+ drv->ops = ops;
mutex_init(&drv->regmap_lock);
mutex_init(&drv->init_lock);
- dev_set_drvdata(drv->dev, drv);
-
INIT_WORK(&drv->boot_work, class_boot_work);
dev_config->lock_arg = &drv->regmap_lock;
@@ -185,39 +201,83 @@ static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id
return 0;
}
+EXPORT_SYMBOL_NS_GPL(sdca_class_probe, "SND_SOC_SDCA_CLASS");
-static void class_sdw_remove(struct sdw_slave *sdw)
+static int class_sdw_probe(struct sdw_slave *sdw, const struct sdw_device_id *id)
{
- struct device *dev = &sdw->dev;
- struct sdca_class_drv *drv = dev_get_drvdata(dev);
+ struct sdca_class_drv *drv;
+
+ /*
+ * Pure-generic SDCA parts: no codec priv to embed, so allocate a
+ * bare sdca_class_drv here and stash it in drvdata for the
+ * built-in PM ops to fetch.
+ */
+ drv = devm_kzalloc(&sdw->dev, sizeof(*drv), GFP_KERNEL);
+ if (!drv)
+ return -ENOMEM;
+
+ dev_set_drvdata(&sdw->dev, drv);
+
+ return sdca_class_probe(sdw, drv, NULL);
+}
+/**
+ * sdca_class_remove - SDCA class SoundWire slave remove helper
+ * @drv: caller-owned sdca_class_drv (the one handed to sdca_class_probe()).
+ *
+ * Cancels the deferred boot work so devres can safely free @drv and the
+ * embedding codec priv without racing class_boot_work. Codec-specific
+ * SoundWire drivers that call sdca_class_probe() must call this from
+ * their .remove with the same drv pointer they passed to probe.
+ */
+void sdca_class_remove(struct sdca_class_drv *drv)
+{
cancel_work_sync(&drv->boot_work);
}
+EXPORT_SYMBOL_NS_GPL(sdca_class_remove, "SND_SOC_SDCA_CLASS");
-static int class_suspend(struct device *dev)
+static void class_sdw_remove(struct sdw_slave *sdw)
+{
+ struct sdca_class_drv *drv = dev_get_drvdata(&sdw->dev);
+
+ sdca_class_remove(drv);
+}
+
+/**
+ * sdca_class_system_suspend - SDCA class system suspend helper
+ * @drv: caller-owned sdca_class_drv.
+ *
+ * Codec drivers compose this into their own dev_pm_ops. Disables the
+ * SoundWire interrupt and forces runtime suspend of the underlying
+ * class regmap.
+ */
+int sdca_class_system_suspend(struct sdca_class_drv *drv)
{
- struct sdca_class_drv *drv = dev_get_drvdata(dev);
int ret;
disable_irq(drv->sdw->irq);
- ret = pm_runtime_force_suspend(dev);
+ ret = pm_runtime_force_suspend(drv->dev);
if (ret) {
- dev_err(dev, "failed to force suspend: %d\n", ret);
+ dev_err(drv->dev, "failed to force suspend: %d\n", ret);
return ret;
}
return 0;
}
+EXPORT_SYMBOL_NS_GPL(sdca_class_system_suspend, "SND_SOC_SDCA_CLASS");
-static int class_resume(struct device *dev)
+/**
+ * sdca_class_system_resume - SDCA class system resume helper
+ * @drv: caller-owned sdca_class_drv.
+ */
+int sdca_class_system_resume(struct sdca_class_drv *drv)
{
- struct sdca_class_drv *drv = dev_get_drvdata(dev);
int ret;
- ret = pm_runtime_force_resume(dev);
+ ret = pm_runtime_force_resume(drv->dev);
if (ret) {
- dev_err(dev, "failed to force resume: %d\n", ret);
+ dev_err(drv->dev, "failed to force resume: %d\n", ret);
return ret;
}
@@ -225,11 +285,14 @@ static int class_resume(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_NS_GPL(sdca_class_system_resume, "SND_SOC_SDCA_CLASS");
-static int class_runtime_suspend(struct device *dev)
+/**
+ * sdca_class_runtime_suspend - SDCA class runtime suspend helper
+ * @drv: caller-owned sdca_class_drv.
+ */
+int sdca_class_runtime_suspend(struct sdca_class_drv *drv)
{
- struct sdca_class_drv *drv = dev_get_drvdata(dev);
-
/*
* Whilst the driver doesn't power the chip down here, going into runtime
* suspend lets the SoundWire bus power down, which means the driver
@@ -239,10 +302,14 @@ static int class_runtime_suspend(struct device *dev)
return 0;
}
+EXPORT_SYMBOL_NS_GPL(sdca_class_runtime_suspend, "SND_SOC_SDCA_CLASS");
-static int class_runtime_resume(struct device *dev)
+/**
+ * sdca_class_runtime_resume - SDCA class runtime resume helper
+ * @drv: caller-owned sdca_class_drv.
+ */
+int sdca_class_runtime_resume(struct sdca_class_drv *drv)
{
- struct sdca_class_drv *drv = dev_get_drvdata(dev);
int ret;
ret = sdw_slave_wait_for_init(drv->sdw, CLASS_SDW_ATTACH_TIMEOUT_MS);
@@ -265,10 +332,37 @@ err:
return ret;
}
+EXPORT_SYMBOL_NS_GPL(sdca_class_runtime_resume, "SND_SOC_SDCA_CLASS");
+
+/*
+ * Convenience dev_pm_ops used by the built-in class_sdw_driver, which
+ * stashes its sdca_class_drv in drvdata directly. Codec drivers that
+ * embed sdca_class_drv in their own priv compose their own dev_pm_ops
+ * using the sdca_class_*_suspend/resume helpers above.
+ */
+static int class_pm_system_suspend(struct device *dev)
+{
+ return sdca_class_system_suspend(dev_get_drvdata(dev));
+}
+
+static int class_pm_system_resume(struct device *dev)
+{
+ return sdca_class_system_resume(dev_get_drvdata(dev));
+}
+
+static int class_pm_runtime_suspend(struct device *dev)
+{
+ return sdca_class_runtime_suspend(dev_get_drvdata(dev));
+}
+
+static int class_pm_runtime_resume(struct device *dev)
+{
+ return sdca_class_runtime_resume(dev_get_drvdata(dev));
+}
-static const struct dev_pm_ops class_pm_ops = {
- SYSTEM_SLEEP_PM_OPS(class_suspend, class_resume)
- RUNTIME_PM_OPS(class_runtime_suspend, class_runtime_resume, NULL)
+static const struct dev_pm_ops sdca_class_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(class_pm_system_suspend, class_pm_system_resume)
+ RUNTIME_PM_OPS(class_pm_runtime_suspend, class_pm_runtime_resume, NULL)
};
static const struct sdw_device_id class_sdw_id[] = {
@@ -282,7 +376,7 @@ MODULE_DEVICE_TABLE(sdw, class_sdw_id);
static struct sdw_driver class_sdw_driver = {
.driver = {
.name = "sdca_class",
- .pm = pm_ptr(&class_pm_ops),
+ .pm = pm_ptr(&sdca_class_pm_ops),
},
.probe = class_sdw_probe,
diff --git a/sound/soc/sdca/sdca_class.h b/sound/soc/sdca/sdca_class.h
deleted file mode 100644
index 57f7f8d08f49..000000000000
--- a/sound/soc/sdca/sdca_class.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/*
- * The MIPI SDCA specification is available for public downloads at
- * https://www.mipi.org/mipi-sdca-v1-0-download
- *
- * Copyright (C) 2025 Cirrus Logic, Inc. and
- * Cirrus Logic International Semiconductor Ltd.
- */
-
-#ifndef __SDCA_CLASS_H__
-#define __SDCA_CLASS_H__
-
-#include <linux/completion.h>
-#include <linux/mutex.h>
-#include <linux/workqueue.h>
-
-struct device;
-struct regmap;
-struct sdw_slave;
-struct sdca_function_data;
-
-struct sdca_class_drv {
- struct device *dev;
- struct regmap *dev_regmap;
- struct sdw_slave *sdw;
-
- struct sdca_interrupt_info *irq_info;
-
- struct mutex regmap_lock;
- /* Serialise function initialisations */
- struct mutex init_lock;
- struct work_struct boot_work;
-};
-
-#endif /* __SDCA_CLASS_H__ */
diff --git a/sound/soc/sdca/sdca_class_function.c b/sound/soc/sdca/sdca_class_function.c
index 610931ae5531..13b70f0f0dbf 100644
--- a/sound/soc/sdca/sdca_class_function.c
+++ b/sound/soc/sdca/sdca_class_function.c
@@ -26,7 +26,7 @@
#include <sound/soc-component.h>
#include <sound/soc-dai.h>
#include <sound/soc.h>
-#include "sdca_class.h"
+#include <sound/sdca_class.h>
#include "sdca_function_device.h"
struct class_function_drv {
@@ -216,9 +216,43 @@ static int class_function_set_jack(struct snd_soc_component *component,
return sdca_jack_set_jack(core->irq_info, jack);
}
+/*
+ * DT phandle cell is the SDCA entity index (matches dais[].id), not the
+ * positional DAI index the default xlate assumes.
+ */
+static int class_function_of_xlate_dai_name(struct snd_soc_component *component,
+ const struct of_phandle_args *args,
+ const char **dai_name)
+{
+ struct class_function_drv *drv = snd_soc_component_get_drvdata(component);
+ struct sdca_function_data *function = drv->function;
+ struct sdca_entity *entity;
+ u32 target;
+
+ if (args->args_count != 1)
+ return -EINVAL;
+
+ target = args->args[0];
+ if (target >= function->num_entities)
+ goto err;
+
+ entity = &function->entities[target];
+ if ((entity->type != SDCA_ENTITY_TYPE_IT &&
+ entity->type != SDCA_ENTITY_TYPE_OT) || !entity->iot.is_dataport)
+ goto err;
+
+ *dai_name = entity->label;
+ return 0;
+err:
+ dev_err(component->dev, "xlate: no dataport entity at index %u (num_entities=%d)\n",
+ target, function->num_entities);
+ return -EINVAL;
+}
+
static const struct snd_soc_component_driver class_function_component_drv = {
.fixup_controls = class_function_component_fixup_controls,
.remove = class_function_component_remove,
+ .of_xlate_dai_name = class_function_of_xlate_dai_name,
.endianness = 1,
};
@@ -329,7 +363,14 @@ static int class_function_probe(struct auxiliary_device *auxdev,
drv->core = core;
drv->function = &sdev->function;
- ret = sdca_parse_function(dev, drv->function);
+ if (core->ops && core->ops->populate_function) {
+ ret = core->ops->populate_function(dev, drv->function);
+ } else if (drv->function->desc->node) {
+ ret = sdca_parse_function(dev, drv->function);
+ } else {
+ dev_err(dev, "no firmware node and no populate_function hook\n");
+ return -ENOENT;
+ }
if (ret)
return ret;
@@ -367,6 +408,7 @@ static int class_function_probe(struct auxiliary_device *auxdev,
switch (drv->function->desc->type) {
case SDCA_FUNCTION_TYPE_UAJ:
case SDCA_FUNCTION_TYPE_RJ:
+ case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
cmp_drv->set_jack = class_function_set_jack;
break;
default:
@@ -559,6 +601,10 @@ static const struct auxiliary_device_id class_function_id_table[] = {
.name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_RJ_NAME,
.driver_data = SDCA_FUNCTION_TYPE_RJ,
},
+ {
+ .name = "snd_soc_sdca." SDCA_FUNCTION_TYPE_SIMPLE_NAME,
+ .driver_data = SDCA_FUNCTION_TYPE_SIMPLE_JACK,
+ },
{},
};
MODULE_DEVICE_TABLE(auxiliary, class_function_id_table);
diff --git a/sound/soc/sdca/sdca_device.c b/sound/soc/sdca/sdca_device.c
index 4bcd8d1fdff8..3f302bfa5457 100644
--- a/sound/soc/sdca/sdca_device.c
+++ b/sound/soc/sdca/sdca_device.c
@@ -15,6 +15,7 @@
#include <sound/sdca.h>
#include <sound/sdca_function.h>
+#if IS_ENABLED(CONFIG_ACPI)
void sdca_lookup_interface_revision(struct sdw_slave *slave)
{
struct fwnode_handle *fwnode = slave->dev.fwnode;
@@ -45,6 +46,9 @@ void sdca_lookup_swft(struct sdw_slave *slave)
devm_add_action_or_reset(&slave->dev, devm_acpi_table_put,
slave->sdca_data.swft);
}
+#else
+void sdca_lookup_swft(struct sdw_slave *slave) { }
+#endif
EXPORT_SYMBOL_NS(sdca_lookup_swft, "SND_SOC_SDCA");
static bool sdca_device_quirk_rt712_vb(struct sdw_slave *slave)
diff --git a/sound/soc/sdca/sdca_fdl.c b/sound/soc/sdca/sdca_fdl.c
index 150e36ed24bc..7fc809707944 100644
--- a/sound/soc/sdca/sdca_fdl.c
+++ b/sound/soc/sdca/sdca_fdl.c
@@ -199,7 +199,6 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
struct sdca_fdl_file *fdl_file;
char *disk_filename;
int ret;
- int i;
if (!set) {
dev_err(dev, "request to load SWF with no set\n");
@@ -209,9 +208,18 @@ static int fdl_load_file(struct sdca_interrupt *interrupt,
fdl_file = &set->files[file_index];
if (fdl_data->swft) {
- tmp = fdl_data->swft->files;
- for (i = 0; i < fdl_data->swft->header.length; i += tmp->file_length,
- tmp = ACPI_ADD_PTR(struct acpi_sw_file, tmp, tmp->file_length)) {
+ struct acpi_sw_file *table_end, *next;
+
+ table_end = ACPI_ADD_PTR(struct acpi_sw_file, fdl_data->swft,
+ fdl_data->swft->header.length);
+ for (tmp = fdl_data->swft->files; tmp + 1 <= table_end; tmp = next) {
+ next = ACPI_ADD_PTR(struct acpi_sw_file, tmp, tmp->file_length);
+
+ if (tmp->file_length < sizeof(*tmp) || next > table_end) {
+ dev_err(dev, "bad file length in SWFT: %u\n", tmp->file_length);
+ break;
+ }
+
if (tmp->vendor_id == fdl_file->vendor_id &&
tmp->file_id == fdl_file->file_id) {
dev_dbg(dev, "located SWF in ACPI: %x-%x-%x\n",
@@ -293,6 +301,8 @@ static struct sdca_fdl_set *fdl_get_set(struct sdca_interrupt *interrupt)
range = sdca_selector_find_range(dev, xu, SDCA_CTL_XU_FDL_SET_INDEX,
SDCA_FDL_SET_INDEX_NCOLS, 0);
+ if (!range)
+ return NULL;
val = sdca_range_search(range, SDCA_FDL_SET_INDEX_SET_NUMBER,
val, SDCA_FDL_SET_INDEX_FILE_SET_ID);
diff --git a/sound/soc/sdca/sdca_functions.c b/sound/soc/sdca/sdca_functions.c
index 32d9b7f30a4f..1eb6418e132d 100644
--- a/sound/soc/sdca/sdca_functions.c
+++ b/sound/soc/sdca/sdca_functions.c
@@ -25,6 +25,7 @@
*/
#define SDCA_PROPERTY_LENGTH 64
+#if IS_ENABLED(CONFIG_ACPI)
static int patch_sdca_function_type(u32 interface_revision, u32 *function_type)
{
/*
@@ -78,6 +79,8 @@ static const char *get_sdca_function_name(u32 function_type)
return SDCA_FUNCTION_TYPE_SPEAKER_MIC_NAME;
case SDCA_FUNCTION_TYPE_RJ:
return SDCA_FUNCTION_TYPE_RJ_NAME;
+ case SDCA_FUNCTION_TYPE_SIMPLE_JACK:
+ return SDCA_FUNCTION_TYPE_SIMPLE_NAME;
case SDCA_FUNCTION_TYPE_COMPANION_AMP:
return SDCA_FUNCTION_TYPE_COMPANION_AMP_NAME;
case SDCA_FUNCTION_TYPE_IMP_DEF:
@@ -199,6 +202,7 @@ void sdca_lookup_functions(struct sdw_slave *slave)
acpi_dev_for_each_child(adev, find_sdca_function, &slave->sdca_data);
}
EXPORT_SYMBOL_NS(sdca_lookup_functions, "SND_SOC_SDCA");
+#endif
struct raw_init_write {
__le32 addr;
@@ -918,8 +922,8 @@ static int find_sdca_control_value(struct device *dev, struct sdca_entity *entit
return 0;
}
-static int find_sdca_control_reset(const struct sdca_entity *entity,
- struct sdca_control *control)
+static void find_sdca_control_reset(const struct sdca_entity *entity,
+ struct sdca_control *control)
{
switch (SDCA_CTL_TYPE(entity->type, control->sel)) {
case SDCA_CTL_TYPE_S(FU, AGC):
@@ -946,8 +950,6 @@ static int find_sdca_control_reset(const struct sdca_entity *entity,
default:
break;
}
-
- return 0;
}
static int find_sdca_entity_control(struct device *dev, struct sdca_entity *entity,
@@ -1028,9 +1030,7 @@ static int find_sdca_entity_control(struct device *dev, struct sdca_entity *enti
control->is_volatile = find_sdca_control_volatile(entity, control);
- ret = find_sdca_control_reset(entity, control);
- if (ret)
- return ret;
+ find_sdca_control_reset(entity, control);
ret = find_sdca_control_range(dev, control_node, &control->range);
if (ret) {
@@ -1985,7 +1985,6 @@ static int find_sdca_cluster_channels(struct device *dev,
char channel_property[SDCA_PROPERTY_LENGTH];
struct fwnode_handle *channel_node;
- /* DisCo uses upper-case for hex numbers */
snprintf(channel_property, sizeof(channel_property),
"mipi-sdca-channel-%d-subproperties", i + 1);
diff --git a/sound/soc/sdw_utils/Makefile b/sound/soc/sdw_utils/Makefile
index 5ae8c69b8b98..1dbc00a2d64a 100644
--- a/sound/soc/sdw_utils/Makefile
+++ b/sound/soc/sdw_utils/Makefile
@@ -10,5 +10,7 @@ snd-soc-sdw-utils-y := soc_sdw_utils.o soc_sdw_dmic.o soc_sdw_rt_dmic.o \
soc_sdw_cs_amp.o \
soc_sdw_es9356.o \
soc_sdw_maxim.o \
- soc_sdw_ti_amp.o
+ soc_sdw_ti_amp.o \
+ soc_sdw_senary_sdca.o soc_sdw_senary_sdca_jack_common.o \
+ soc_sdw_senary_amp.o soc_sdw_senary_dmic.o
obj-$(CONFIG_SND_SOC_SDW_UTILS) += snd-soc-sdw-utils.o
diff --git a/sound/soc/sdw_utils/soc_sdw_rt711.c b/sound/soc/sdw_utils/soc_sdw_rt711.c
index 3a3a66b4b737..4707190e160c 100644
--- a/sound/soc/sdw_utils/soc_sdw_rt711.c
+++ b/sound/soc/sdw_utils/soc_sdw_rt711.c
@@ -124,6 +124,7 @@ int asoc_sdw_rt711_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_
device_remove_software_node(ctx->headset_codec_dev);
put_device(ctx->headset_codec_dev);
+ ctx->headset_codec_dev = NULL;
return 0;
}
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 5bf3627a97a0..6e5fa93b5fc2 100644
--- a/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c
+++ b/sound/soc/sdw_utils/soc_sdw_rt_mf_sdca.c
@@ -33,6 +33,11 @@ static const struct snd_soc_dapm_route rt722_spk_map[] = {
{ "Speaker", NULL, "rt722 SPK" },
};
+static const struct snd_soc_dapm_route rt766_spk_map[] = {
+ { "Speaker", NULL, "rt766 SPOL" },
+ { "Speaker", NULL, "rt766 SPOR" },
+};
+
/* Structure to map codec names to respective route arrays and sizes */
struct codec_route_map {
const char *codec_name;
@@ -45,6 +50,7 @@ static const struct codec_route_map codec_routes[] = {
{ "rt712", rt712_spk_map, ARRAY_SIZE(rt712_spk_map) },
{ "rt721", rt721_spk_map, ARRAY_SIZE(rt721_spk_map) },
{ "rt722", rt722_spk_map, ARRAY_SIZE(rt722_spk_map) },
+ { "rt766", rt766_spk_map, ARRAY_SIZE(rt766_spk_map) },
};
static const struct codec_route_map *get_codec_route_map(const char *codec_name)
diff --git a/sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c b/sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c
index 2547b5b3fdd7..b4e427d55130 100644
--- a/sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c
+++ b/sound/soc/sdw_utils/soc_sdw_rt_sdca_jack_common.c
@@ -70,6 +70,11 @@ static const struct snd_soc_dapm_route rt722_sdca_map[] = {
{ "rt722 MIC2", NULL, "Headset Mic" },
};
+static const struct snd_soc_dapm_route rt766_sdca_map[] = {
+ { "Headphone", NULL, "rt766 HP" },
+ { "rt766 MIC2", NULL, "Headset Mic" },
+};
+
static struct snd_soc_jack_pin rt_sdca_jack_pins[] = {
{
.pin = "Headphone",
@@ -133,6 +138,9 @@ int asoc_sdw_rt_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_s
} else if (strstr(component->name_prefix, "rt722")) {
ret = snd_soc_dapm_add_routes(dapm, rt722_sdca_map,
ARRAY_SIZE(rt722_sdca_map));
+ } else if (strstr(component->name_prefix, "rt766")) {
+ ret = snd_soc_dapm_add_routes(dapm, rt766_sdca_map,
+ ARRAY_SIZE(rt766_sdca_map));
} else {
dev_err(card->dev, "%s is not supported\n", component->name_prefix);
return -EINVAL;
diff --git a/sound/soc/sdw_utils/soc_sdw_senary_amp.c b/sound/soc/sdw_utils/soc_sdw_senary_amp.c
new file mode 100644
index 000000000000..9370402ab4c7
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_senary_amp.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2022 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/soc_sdw_utils.h>
+
+static int senary_amp_add_device_props(struct device *sdw_dev)
+{
+ return 0;
+}
+
+int asoc_sdw_senary_amp_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (ctx->amp_dev1) {
+ device_remove_software_node(ctx->amp_dev1);
+ put_device(ctx->amp_dev1);
+ }
+
+ if (ctx->amp_dev2) {
+ device_remove_software_node(ctx->amp_dev2);
+ put_device(ctx->amp_dev2);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_senary_amp_exit, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_senary_amp_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct device *sdw_dev1, *sdw_dev2;
+ int ret;
+
+ /* Count amp number and do init on playback link only. */
+ if (!playback)
+ return 0;
+
+ info->amp_num++;
+
+ if (info->amp_num == 2) {
+ sdw_dev1 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
+ if (!sdw_dev1)
+ return -EPROBE_DEFER;
+
+ ret = senary_amp_add_device_props(sdw_dev1);
+ if (ret < 0) {
+ put_device(sdw_dev1);
+ return ret;
+ }
+ ctx->amp_dev1 = sdw_dev1;
+
+ sdw_dev2 = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[1].name);
+ if (!sdw_dev2)
+ return -EPROBE_DEFER;
+
+ ret = senary_amp_add_device_props(sdw_dev2);
+ if (ret < 0) {
+ put_device(sdw_dev2);
+ return ret;
+ }
+ ctx->amp_dev2 = sdw_dev2;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_senary_amp_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_senary_dmic.c b/sound/soc/sdw_utils/soc_sdw_senary_dmic.c
new file mode 100644
index 000000000000..7481706561f9
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_senary_dmic.c
@@ -0,0 +1,49 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2024 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc_sdw_utils.h>
+
+/* Card pin "Dmic" → codec input (name_prefix + " DMIC") */
+static const struct snd_soc_dapm_route senary_dmic_map[] = {
+ { "sn624x DMIC", NULL, "Dmic" },
+};
+
+int asoc_sdw_senary_dmic_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct snd_soc_component *component;
+ char *mic_name;
+ int ret;
+
+ component = dai->component;
+
+ ret = snd_soc_dapm_add_routes(dapm, senary_dmic_map,
+ ARRAY_SIZE(senary_dmic_map));
+ if (ret) {
+ dev_err(card->dev, "senary dmic map addition failed: %d\n", ret);
+ return ret;
+ }
+
+ mic_name = devm_kasprintf(card->dev, GFP_KERNEL, "%s", component->name_prefix);
+ if (!mic_name)
+ return -ENOMEM;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s mic:%s-dmic", card->components,
+ mic_name);
+ if (!card->components)
+ return -ENOMEM;
+
+ dev_dbg(card->dev, "card->components: %s\n", card->components);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_senary_dmic_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_senary_sdca.c b/sound/soc/sdw_utils/soc_sdw_senary_sdca.c
new file mode 100644
index 000000000000..5c2cd75307c9
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_senary_sdca.c
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2024 Intel Corporation.
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc_sdw_utils.h>
+
+/* dapm routes for SPK will be registered dynamically */
+static const struct snd_soc_dapm_route sn624x_spk_map[] = {
+ { "Speaker", NULL, "sn624x SPK" },
+};
+
+/* Structure to map codec names to respective route arrays and sizes */
+struct codec_route_map {
+ const char *codec_name;
+ const struct snd_soc_dapm_route *route_map;
+ size_t route_size;
+};
+
+/* Codec route maps array */
+static const struct codec_route_map codec_routes[] = {
+ { "sn624x", sn624x_spk_map, ARRAY_SIZE(sn624x_spk_map) },
+};
+
+static const struct codec_route_map *get_codec_route_map(const char *dai_name)
+{
+ for (size_t i = 0; i < ARRAY_SIZE(codec_routes); i++) {
+ if (str_has_prefix(dai_name, codec_routes[i].codec_name))
+ return &codec_routes[i];
+ }
+ return NULL;
+}
+
+int asoc_sdw_senary_sdca_spk_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ int ret;
+
+ /* Match by DAI name prefix (e.g. sn624x-sdca-aif2). */
+ const struct codec_route_map *route_map = get_codec_route_map(dai->name);
+
+ if (!route_map) {
+ dev_err(rtd->dev, "failed to get codec name and route map\n");
+ return -EINVAL;
+ }
+
+ /* Add routes */
+ ret = snd_soc_dapm_add_routes(dapm, route_map->route_map, route_map->route_size);
+ if (ret)
+ dev_err(rtd->dev, "failed to add rt sdca spk map: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_senary_sdca_spk_rtd_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_senary_sdca_jack_common.c b/sound/soc/sdw_utils/soc_sdw_senary_sdca_jack_common.c
new file mode 100644
index 000000000000..1dab6f99877d
--- /dev/null
+++ b/sound/soc/sdw_utils/soc_sdw_senary_sdca_jack_common.c
@@ -0,0 +1,197 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// This file incorporates work covered by the following copyright notice:
+// Copyright (c) 2020 Intel Corporation
+// Copyright (c) 2024 Advanced Micro Devices, Inc.
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/control.h>
+#include <sound/soc.h>
+#include <sound/soc-acpi.h>
+#include <sound/soc-dapm.h>
+#include <sound/jack.h>
+#include <sound/soc_sdw_utils.h>
+
+/*
+ * jack_init / jack_exit / most of rtd_init follow soc_sdw_rt_sdca_jack_common.c
+ * (same SDCA headset machine-helper pattern). Vendor-specific pieces here are
+ * mainly the "senarytech,jd-src" property name and senary_sdca_jack_map[].
+ *
+ * Keep a Senary-local file for now to mirror the existing RT layout. Sharing
+ * parameterised SDCA jack helpers in snd-soc-sdw-utils (jd-src key + common
+ * init/exit/rtd_init boilerplate) is intentional follow-up work, out of scope
+ * for the sn624x codec series.
+ */
+
+/*
+ * Note this MUST be called before snd_soc_register_card(), so that the props
+ * are in place before the codec component driver's probe function parses them.
+ */
+static int senary_sdca_jack_add_codec_device_props(struct device *sdw_dev, unsigned long quirk)
+{
+ struct property_entry props[SOC_SDW_MAX_NO_PROPS] = {};
+ struct fwnode_handle *fwnode;
+ int ret;
+
+ if (!SOC_SDW_JACK_JDSRC(quirk))
+ return 0;
+
+ props[0] = PROPERTY_ENTRY_U32("senarytech,jd-src", SOC_SDW_JACK_JDSRC(quirk));
+
+ fwnode = fwnode_create_software_node(props, NULL);
+ if (IS_ERR(fwnode))
+ return PTR_ERR(fwnode);
+
+ ret = device_add_software_node(sdw_dev, to_software_node(fwnode));
+
+ fwnode_handle_put(fwnode);
+
+ return ret;
+}
+
+static const struct snd_soc_dapm_route senary_sdca_jack_map[] = {
+ { "Headphone", NULL, "sn624x HP" },
+ { "sn624x MIC2", NULL, "Headset Mic" },
+};
+
+static struct snd_soc_jack_pin senary_sdca_jack_pins[] = {
+ {
+ .pin = "Headphone",
+ .mask = SND_JACK_HEADPHONE,
+ },
+ {
+ .pin = "Headset Mic",
+ .mask = SND_JACK_MICROPHONE,
+ },
+};
+
+static const char * const need_sdca_suffix[] = {
+ "sn624x"
+};
+
+int asoc_sdw_senary_sdca_jack_rtd_init(struct snd_soc_pcm_runtime *rtd, struct snd_soc_dai *dai)
+{
+ struct snd_soc_card *card = rtd->card;
+ struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(card);
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct snd_soc_component *component;
+ struct snd_soc_jack *jack;
+ int ret;
+ int i;
+
+ component = dai->component;
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s hs:%s",
+ card->components, component->name_prefix);
+ if (!card->components)
+ return -ENOMEM;
+
+ for (i = 0; i < ARRAY_SIZE(need_sdca_suffix); i++) {
+ if (!strstr(component->name_prefix, need_sdca_suffix[i]))
+ continue;
+
+ card->components = devm_kasprintf(card->dev, GFP_KERNEL,
+ "%s-sdca", card->components);
+ if (!card->components)
+ return -ENOMEM;
+
+ ret = snd_soc_dapm_add_routes(dapm, senary_sdca_jack_map,
+ ARRAY_SIZE(senary_sdca_jack_map));
+ if (ret) {
+ dev_err(card->dev,
+ "senary sdca jack map addition failed: %d\n", ret);
+ return ret;
+ }
+ break;
+ }
+
+ if (i == ARRAY_SIZE(need_sdca_suffix)) {
+ dev_err(card->dev, "%s is not supported\n", component->name_prefix);
+ return -EINVAL;
+ }
+
+ ret = snd_soc_card_jack_new_pins(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 |
+ SND_JACK_BTN_1 | SND_JACK_BTN_2 |
+ SND_JACK_BTN_3,
+ &ctx->sdw_headset,
+ senary_sdca_jack_pins,
+ ARRAY_SIZE(senary_sdca_jack_pins));
+ if (ret) {
+ dev_err(rtd->card->dev, "Headset Jack creation failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ jack = &ctx->sdw_headset;
+
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP);
+ snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN);
+
+ ret = snd_soc_component_set_jack(component, jack, NULL);
+
+ if (ret)
+ dev_err(rtd->card->dev, "Headset Jack call-back failed: %d\n",
+ ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_senary_sdca_jack_rtd_init, "SND_SOC_SDW_UTILS");
+
+int asoc_sdw_senary_sdca_jack_exit(struct snd_soc_card *card, struct snd_soc_dai_link *dai_link)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+
+ if (!ctx->headset_codec_dev)
+ return 0;
+
+ if (!SOC_SDW_JACK_JDSRC(ctx->mc_quirk))
+ return 0;
+
+ device_remove_software_node(ctx->headset_codec_dev);
+ put_device(ctx->headset_codec_dev);
+ ctx->headset_codec_dev = NULL;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_senary_sdca_jack_exit, "SND_SOC_SDW_UTILS");
+
+/*
+ * Same control flow as asoc_sdw_rt_sdca_jack_init(); see file comment above
+ * about deferred helper sharing in snd-soc-sdw-utils.
+ */
+int asoc_sdw_senary_sdca_jack_init(struct snd_soc_card *card,
+ struct snd_soc_dai_link *dai_links,
+ struct asoc_sdw_codec_info *info,
+ bool playback)
+{
+ struct asoc_sdw_mc_private *ctx = snd_soc_card_get_drvdata(card);
+ struct device *sdw_dev;
+ int ret;
+
+ /*
+ * Jack detection should be only initialized once for headsets since
+ * the playback/capture is sharing the same jack
+ */
+ if (ctx->headset_codec_dev)
+ return 0;
+
+ sdw_dev = bus_find_device_by_name(&sdw_bus_type, NULL, dai_links->codecs[0].name);
+ if (!sdw_dev)
+ return -EPROBE_DEFER;
+
+ ret = senary_sdca_jack_add_codec_device_props(sdw_dev, ctx->mc_quirk);
+ if (ret < 0) {
+ put_device(sdw_dev);
+ return ret;
+ }
+ ctx->headset_codec_dev = sdw_dev;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(asoc_sdw_senary_sdca_jack_init, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/sdw_utils/soc_sdw_utils.c b/sound/soc/sdw_utils/soc_sdw_utils.c
index d2eeef4931c6..14aee10b40ba 100644
--- a/sound/soc/sdw_utils/soc_sdw_utils.c
+++ b/sound/soc/sdw_utils/soc_sdw_utils.c
@@ -17,6 +17,15 @@ static const struct snd_soc_dapm_widget generic_dmic_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
+/* Senary SN624x UCM uses CaptureMixerElem "Dmic"; keep separate from generic DMIC. */
+static const struct snd_soc_dapm_widget sn624x_dmic_widgets[] = {
+ SND_SOC_DAPM_MIC("Dmic", NULL),
+};
+
+static const struct snd_kcontrol_new sn624x_dmic_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Dmic"),
+};
+
static const struct snd_soc_dapm_widget generic_jack_widgets[] = {
SND_SOC_DAPM_HP("Headphone", NULL),
SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -678,6 +687,52 @@ struct asoc_sdw_codec_info codec_info_list[] = {
},
{
.vendor_id = 0x025d,
+ .part_id = 0x718,
+ .name_prefix = "rt721",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt721-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .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},
+ .init = asoc_sdw_rt_amp_init,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_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 = "rt721-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ .quirk = SOC_SDW_CODEC_MIC,
+ .quirk_exclude = true,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .vendor_id = 0x025d,
.part_id = 0x722,
.name_prefix = "rt722",
.version_id = 3,
@@ -725,6 +780,178 @@ struct asoc_sdw_codec_info codec_info_list[] = {
.dai_num = 3,
},
{
+ .vendor_id = 0x025d,
+ .part_id = 0x767,
+ .name_prefix = "rt766",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt766-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt766-sdca-aif2",
+ .component_name = "rt766",
+ .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,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_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 = "rt766-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .vendor_id = 0x025d,
+ .part_id = 0x767,
+ .name_prefix = "rt766",
+ .version_id = 4,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt766-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt766-sdca-aif2",
+ .component_name = "rt766",
+ .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,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_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 = "rt766-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .vendor_id = 0x025d,
+ .part_id = 0x766,
+ .name_prefix = "rt766",
+ .version_id = 3,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt766-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt766-sdca-aif2",
+ .component_name = "rt766",
+ .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,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_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 = "rt766-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .vendor_id = 0x025d,
+ .part_id = 0x766,
+ .name_prefix = "rt766",
+ .version_id = 4,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "rt766-sdca-aif1",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_rt_sdca_jack_init,
+ .exit = asoc_sdw_rt_sdca_jack_exit,
+ .rtd_init = asoc_sdw_rt_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "rt766-sdca-aif2",
+ .component_name = "rt766",
+ .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,
+ .exit = asoc_sdw_rt_amp_exit,
+ .rtd_init = asoc_sdw_rt_mf_sdca_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 = "rt766-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_rt_dmic_rtd_init,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
.vendor_id = 0x019f,
.part_id = 0x8373,
.name_prefix = "Left",
@@ -1264,6 +1491,163 @@ struct asoc_sdw_codec_info codec_info_list[] = {
},
.dai_num = 1,
},
+ /*
+ * Senary SN6242/6244/6247: same SDCA jack/amp/mic topology; only part_id
+ * differs. name_prefix stays "sn624x" for UCM/DAI naming.
+ */
+ {
+ .vendor_id = 0x0496,
+ .part_id = 0x6244,
+ .name_prefix = "sn624x",
+ .ignore_internal_dmic = true,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sn624x-sdca-aif",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_senary_sdca_jack_init,
+ .exit = asoc_sdw_senary_sdca_jack_exit,
+ .rtd_init = asoc_sdw_senary_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "sn624x-sdca-aif2",
+ .component_name = "sn624x",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_senary_amp_init,
+ .exit = asoc_sdw_senary_amp_exit,
+ .rtd_init = asoc_sdw_senary_sdca_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),
+ .quirk = SOC_SDW_CODEC_SPKR,
+ .quirk_exclude = true,
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "sn624x-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_senary_dmic_rtd_init,
+ .widgets = sn624x_dmic_widgets,
+ .num_widgets = ARRAY_SIZE(sn624x_dmic_widgets),
+ .controls = sn624x_dmic_controls,
+ .num_controls = ARRAY_SIZE(sn624x_dmic_controls),
+ .quirk = SOC_SDW_CODEC_MIC,
+ .quirk_exclude = true,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .vendor_id = 0x0496,
+ .part_id = 0x6247,
+ .name_prefix = "sn624x",
+ .ignore_internal_dmic = true,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sn624x-sdca-aif",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_senary_sdca_jack_init,
+ .exit = asoc_sdw_senary_sdca_jack_exit,
+ .rtd_init = asoc_sdw_senary_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "sn624x-sdca-aif2",
+ .component_name = "sn624x",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_senary_amp_init,
+ .exit = asoc_sdw_senary_amp_exit,
+ .rtd_init = asoc_sdw_senary_sdca_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),
+ .quirk = SOC_SDW_CODEC_SPKR,
+ .quirk_exclude = true,
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "sn624x-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_senary_dmic_rtd_init,
+ .widgets = sn624x_dmic_widgets,
+ .num_widgets = ARRAY_SIZE(sn624x_dmic_widgets),
+ .controls = sn624x_dmic_controls,
+ .num_controls = ARRAY_SIZE(sn624x_dmic_controls),
+ .quirk = SOC_SDW_CODEC_MIC,
+ .quirk_exclude = true,
+ },
+ },
+ .dai_num = 3,
+ },
+ {
+ .vendor_id = 0x0496,
+ .part_id = 0x6242,
+ .name_prefix = "sn624x",
+ .ignore_internal_dmic = true,
+ .dais = {
+ {
+ .direction = {true, true},
+ .dai_name = "sn624x-sdca-aif",
+ .dai_type = SOC_SDW_DAI_TYPE_JACK,
+ .dailink = {SOC_SDW_JACK_OUT_DAI_ID, SOC_SDW_JACK_IN_DAI_ID},
+ .init = asoc_sdw_senary_sdca_jack_init,
+ .exit = asoc_sdw_senary_sdca_jack_exit,
+ .rtd_init = asoc_sdw_senary_sdca_jack_rtd_init,
+ .controls = generic_jack_controls,
+ .num_controls = ARRAY_SIZE(generic_jack_controls),
+ .widgets = generic_jack_widgets,
+ .num_widgets = ARRAY_SIZE(generic_jack_widgets),
+ },
+ {
+ .direction = {true, false},
+ .dai_name = "sn624x-sdca-aif2",
+ .component_name = "sn624x",
+ .dai_type = SOC_SDW_DAI_TYPE_AMP,
+ .dailink = {SOC_SDW_AMP_OUT_DAI_ID, SOC_SDW_UNUSED_DAI_ID},
+ .init = asoc_sdw_senary_amp_init,
+ .exit = asoc_sdw_senary_amp_exit,
+ .rtd_init = asoc_sdw_senary_sdca_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),
+ .quirk = SOC_SDW_CODEC_SPKR,
+ .quirk_exclude = true,
+ },
+ {
+ .direction = {false, true},
+ .dai_name = "sn624x-sdca-aif3",
+ .dai_type = SOC_SDW_DAI_TYPE_MIC,
+ .dailink = {SOC_SDW_UNUSED_DAI_ID, SOC_SDW_DMIC_DAI_ID},
+ .rtd_init = asoc_sdw_senary_dmic_rtd_init,
+ .widgets = sn624x_dmic_widgets,
+ .num_widgets = ARRAY_SIZE(sn624x_dmic_widgets),
+ .controls = sn624x_dmic_controls,
+ .num_controls = ARRAY_SIZE(sn624x_dmic_controls),
+ .quirk = SOC_SDW_CODEC_MIC,
+ .quirk_exclude = true,
+ },
+ },
+ .dai_num = 3,
+ },
};
EXPORT_SYMBOL_NS(codec_info_list, "SND_SOC_SDW_UTILS");
diff --git a/sound/soc/soc-component.c b/sound/soc/soc-component.c
index dc7d203cb76a..463ea227cac8 100644
--- a/sound/soc/soc-component.c
+++ b/sound/soc/soc-component.c
@@ -315,8 +315,12 @@ void snd_soc_component_suspend(struct snd_soc_component *component)
void snd_soc_component_resume(struct snd_soc_component *component)
{
- if (component->driver->resume)
- component->driver->resume(component);
+ int ret;
+
+ if (component->driver->resume) {
+ ret = component->driver->resume(component);
+ soc_component_ret(component, ret);
+ }
component->suspended = 0;
}
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index b8402802ae78..093a70a93d2b 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -20,6 +20,7 @@
#include <sound/initval.h>
#include <sound/soc-dpcm.h>
#include <sound/soc-link.h>
+#include "soc-internal.h"
static int snd_soc_compr_components_open(struct snd_compr_stream *cstream)
{
@@ -69,10 +70,10 @@ static int soc_compr_clean(struct snd_compr_stream *cstream, int rollback)
snd_soc_dai_digital_mute(codec_dai, 1, stream);
if (!snd_soc_dai_active(cpu_dai))
- soc_pcm_set_dai_params(cpu_dai, NULL);
+ snd_soc_dai_symmetric_set_params(cpu_dai, NULL);
if (!snd_soc_dai_active(codec_dai))
- soc_pcm_set_dai_params(codec_dai, NULL);
+ snd_soc_dai_symmetric_set_params(codec_dai, NULL);
snd_soc_link_compr_shutdown(cstream, rollback);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index b9c70268d49f..06167a198591 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -41,11 +41,13 @@
#include <sound/soc-topology.h>
#include <sound/soc-link.h>
#include <sound/initval.h>
+#include "soc-internal.h"
#define CREATE_TRACE_POINTS
#include <trace/events/asoc.h>
-static DEFINE_MUTEX(client_mutex);
+DEFINE_MUTEX(client_mutex);
+
static LIST_HEAD(component_list);
static LIST_HEAD(unbind_card_list);
@@ -232,22 +234,6 @@ static inline void snd_soc_debugfs_exit(void) { }
#endif
-static int snd_soc_is_match_dai_args(const struct of_phandle_args *args1,
- const struct of_phandle_args *args2)
-{
- if (!args1 || !args2)
- return 0;
-
- if (args1->np != args2->np)
- return 0;
-
- for (int i = 0; i < args1->args_count; i++)
- if (args1->args[i] != args2->args[i])
- return 0;
-
- return 1;
-}
-
static inline int snd_soc_dlc_component_is_empty(struct snd_soc_dai_link_component *dlc)
{
return !(dlc->dai_args || dlc->name || dlc->of_node);
@@ -263,50 +249,6 @@ static inline int snd_soc_dlc_dai_is_empty(struct snd_soc_dai_link_component *dl
return !(dlc->dai_args || dlc->dai_name);
}
-static int snd_soc_is_matching_dai(const struct snd_soc_dai_link_component *dlc,
- struct snd_soc_dai *dai)
-{
- if (!dlc)
- return 0;
-
- if (dlc->dai_args)
- return snd_soc_is_match_dai_args(dai->driver->dai_args, dlc->dai_args);
-
- if (!dlc->dai_name)
- return 1;
-
- /* see snd_soc_dai_name_get() */
-
- if (dai->driver->name &&
- strcmp(dlc->dai_name, dai->driver->name) == 0)
- return 1;
-
- if (strcmp(dlc->dai_name, dai->name) == 0)
- return 1;
-
- if (dai->component->name &&
- strcmp(dlc->dai_name, dai->component->name) == 0)
- return 1;
-
- return 0;
-}
-
-const char *snd_soc_dai_name_get(const struct snd_soc_dai *dai)
-{
- /* see snd_soc_is_matching_dai() */
- if (dai->driver->name)
- return dai->driver->name;
-
- if (dai->name)
- return dai->name;
-
- if (dai->component->name)
- return dai->component->name;
-
- return NULL;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dai_name_get);
-
static int snd_soc_rtd_add_component(struct snd_soc_pcm_runtime *rtd,
struct snd_soc_component *component)
{
@@ -868,7 +810,7 @@ static int snd_soc_is_matching_component(
struct snd_soc_dai *dai;
for_each_component_dais(component, dai)
- if (snd_soc_is_matching_dai(dlc, dai))
+ if (snd_soc_dai_matches_dlc(dai, dlc))
return 1;
return 0;
}
@@ -928,7 +870,7 @@ struct snd_soc_dai *snd_soc_find_dai(
for_each_component(component)
if (snd_soc_is_matching_component(dlc, component))
for_each_component_dais(component, dai)
- if (snd_soc_is_matching_dai(dlc, dai))
+ if (snd_soc_dai_matches_dlc(dai, dlc))
return dai;
return NULL;
@@ -1859,7 +1801,7 @@ match:
}
dev_dbg(card->dev, "info: override BE DAI link %s\n",
- card->dai_link[i].name);
+ dai_link->name);
/* override platform component */
if (!dai_link->platforms) {
@@ -2343,9 +2285,9 @@ struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
}
EXPORT_SYMBOL_GPL(snd_soc_cnew);
-static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
- const struct snd_kcontrol_new *controls, int num_controls,
- const char *prefix, void *data)
+int snd_soc_add_controls(struct snd_card *card, struct device *dev,
+ const struct snd_kcontrol_new *controls, int num_controls,
+ const char *prefix, void *data)
{
int i;
@@ -2403,26 +2345,6 @@ int snd_soc_add_card_controls(struct snd_soc_card *soc_card,
EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
/**
- * snd_soc_add_dai_controls - add an array of controls to a DAI.
- * Convenience function to add a list of controls.
- *
- * @dai: DAI to add controls to
- * @controls: array of controls to add
- * @num_controls: number of elements in the array
- *
- * Return 0 for success, else error.
- */
-int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
- const struct snd_kcontrol_new *controls, int num_controls)
-{
- struct snd_card *card = dai->component->card->snd_card;
-
- return snd_soc_add_controls(card, dai->dev, controls, num_controls,
- NULL, dai);
-}
-EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
-
-/**
* snd_soc_register_card - Register a card with the ASoC core
*
* @card: Card to register
@@ -2480,7 +2402,7 @@ EXPORT_SYMBOL_GPL(snd_soc_unregister_card);
* Simplify DAI link configuration by removing ".-1" from device names
* and sanitizing names.
*/
-static char *fmt_single_name(struct device *dev, int *id)
+char *snd_soc_fmt_single_name(struct device *dev, int *id)
{
const char *devname = dev_name(dev);
char *found, *name;
@@ -2529,8 +2451,7 @@ static char *fmt_single_name(struct device *dev, int *id)
* Simplify DAI link naming for single devices with multiple DAIs by removing
* any ".-1" and using the DAI name (instead of device name).
*/
-static inline char *fmt_multiple_name(struct device *dev,
- struct snd_soc_dai_driver *dai_drv)
+char *snd_soc_fmt_multiple_name(struct device *dev, struct snd_soc_dai_driver *dai_drv)
{
if (dai_drv->name == NULL) {
dev_err(dev,
@@ -2542,74 +2463,6 @@ static inline char *fmt_multiple_name(struct device *dev,
return devm_kstrdup(dev, dai_drv->name, GFP_KERNEL);
}
-void snd_soc_unregister_dai(struct snd_soc_dai *dai)
-{
- lockdep_assert_held(&client_mutex);
-
- dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
- list_del(&dai->list);
-}
-EXPORT_SYMBOL_GPL(snd_soc_unregister_dai);
-
-/**
- * snd_soc_register_dai - Register a DAI dynamically & create its widgets
- *
- * @component: The component the DAIs are registered for
- * @dai_drv: DAI driver to use for the DAI
- * @legacy_dai_naming: if %true, use legacy single-name format;
- * if %false, use multiple-name format;
- *
- * Topology can use this API to register DAIs when probing a component.
- * These DAIs's widgets will be freed in the card cleanup and the DAIs
- * will be freed in the component cleanup.
- */
-struct snd_soc_dai *snd_soc_register_dai(struct snd_soc_component *component,
- struct snd_soc_dai_driver *dai_drv,
- bool legacy_dai_naming)
-{
- struct device *dev = component->dev;
- struct snd_soc_dai *dai;
-
- lockdep_assert_held(&client_mutex);
-
- dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
- if (dai == NULL)
- return NULL;
-
- /*
- * Back in the old days when we still had component-less DAIs,
- * instead of having a static name, component-less DAIs would
- * inherit the name of the parent device so it is possible to
- * register multiple instances of the DAI. We still need to keep
- * the same naming style even though those DAIs are not
- * component-less anymore.
- */
- if (legacy_dai_naming &&
- (dai_drv->id == 0 || dai_drv->name == NULL)) {
- dai->name = fmt_single_name(dev, &dai->id);
- } else {
- dai->name = fmt_multiple_name(dev, dai_drv);
- if (dai_drv->id)
- dai->id = dai_drv->id;
- else
- dai->id = component->num_dai;
- }
- if (!dai->name)
- return NULL;
-
- dai->component = component;
- dai->dev = dev;
- dai->driver = dai_drv;
-
- /* see for_each_component_dais */
- list_add_tail(&dai->list, &component->dai_list);
- component->num_dai++;
-
- dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
- return dai;
-}
-EXPORT_SYMBOL_GPL(snd_soc_register_dai);
-
/**
* snd_soc_unregister_dais - Unregister DAIs from the ASoC core
*
@@ -2724,7 +2577,7 @@ static int soc_component_initialize(struct snd_soc_component *component,
mutex_init(&component->io_mutex);
if (!component->name) {
- component->name = fmt_single_name(dev, NULL);
+ component->name = snd_soc_fmt_single_name(dev, NULL);
if (!component->name) {
dev_err(dev, "ASoC: Failed to allocate name\n");
return -ENOMEM;
@@ -3477,7 +3330,7 @@ int snd_soc_get_dlc(const struct of_phandle_args *args, struct snd_soc_dai_link_
id--;
}
- dlc->dai_name = snd_soc_dai_name_get(dai);
+ dlc->dai_name = snd_soc_dai_name(dai);
} else if (ret) {
/*
* if another error than ENOTSUPP is returned go on and
@@ -3552,7 +3405,7 @@ struct snd_soc_dai *snd_soc_get_dai_via_args(const struct of_phandle_args *dai_a
for_each_component(component) {
for_each_component_dais(component, dai)
- if (snd_soc_is_match_dai_args(dai->driver->dai_args, dai_args))
+ if (snd_soc_dai_matches_args(dai, dai_args))
return dai;
}
return NULL;
diff --git a/sound/soc/soc-dai.c b/sound/soc/soc-dai.c
index b098d1100b4b..d124b6afb1b8 100644
--- a/sound/soc/soc-dai.c
+++ b/sound/soc/soc-dai.c
@@ -6,9 +6,11 @@
// Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
//
+#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dai.h>
#include <sound/soc-link.h>
+#include "soc-internal.h"
#define soc_dai_ret(dai, ret) _soc_dai_ret(dai, __func__, ret)
static inline int _soc_dai_ret(const struct snd_soc_dai *dai,
@@ -18,6 +20,42 @@ static inline int _soc_dai_ret(const struct snd_soc_dai *dai,
"at %s() on %s\n", func, dai->name);
}
+struct snd_soc_component *snd_soc_dai_to_component(const struct snd_soc_dai *dai)
+{
+ return dai->component;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_to_component);
+
+struct snd_soc_dai_driver *snd_soc_dai_to_driver(const struct snd_soc_dai *dai)
+{
+ return dai->driver;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_to_driver);
+
+void snd_soc_dai_set_priv(struct snd_soc_dai *dai, void *priv)
+{
+ dai->priv = priv;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_priv);
+
+void *snd_soc_dai_to_priv(struct snd_soc_dai *dai)
+{
+ return dai->priv;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_to_priv);
+
+struct snd_soc_dai *snd_soc_dai_from_list(struct list_head *list)
+{
+ return list_entry(list, struct snd_soc_dai, list);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_from_list);
+
+struct list_head *snd_soc_dai_to_list(struct snd_soc_dai *dai)
+{
+ return &dai->list;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_to_list);
+
/*
* We might want to check substream by using list.
* In such case, we can update these macros.
@@ -669,8 +707,7 @@ bool snd_soc_dai_stream_valid(const struct snd_soc_dai *dai, int dir)
return stream->channels_min;
}
-void snd_soc_dai_action(struct snd_soc_dai *dai,
- int stream, int action)
+void snd_soc_dai_active_update(struct snd_soc_dai *dai, int stream, int action)
{
/* see snd_soc_dai_stream_active() */
dai->stream[stream].active += action;
@@ -678,7 +715,6 @@ void snd_soc_dai_action(struct snd_soc_dai *dai,
/* see snd_soc_component_active() */
dai->component->active += action;
}
-EXPORT_SYMBOL_GPL(snd_soc_dai_action);
int snd_soc_dai_active(const struct snd_soc_dai *dai)
{
@@ -686,7 +722,7 @@ int snd_soc_dai_active(const struct snd_soc_dai *dai)
active = 0;
for_each_pcm_streams(stream)
- active += dai->stream[stream].active;
+ active += snd_soc_dai_stream_active(dai, stream);
return active;
}
@@ -989,3 +1025,377 @@ int snd_soc_dai_compr_get_metadata(struct snd_soc_dai *dai,
return soc_dai_ret(dai, ret);
}
EXPORT_SYMBOL_GPL(snd_soc_dai_compr_get_metadata);
+
+/**
+ * snd_soc_dai_add_controls - add an array of controls to a DAI.
+ * Convenience function to add a list of controls.
+ *
+ * @dai: DAI to add controls to
+ * @controls: array of controls to add
+ * @num_controls: number of elements in the array
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_dai_add_controls(struct snd_soc_dai *dai,
+ const struct snd_kcontrol_new *controls, int num_controls)
+{
+ struct snd_card *card = dai->component->card->snd_card;
+
+ return snd_soc_add_controls(card, dai->dev, controls, num_controls, NULL, dai);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_add_controls);
+
+int snd_soc_dai_matches_args(const struct snd_soc_dai *dai,
+ const struct of_phandle_args *args2)
+{
+ const struct of_phandle_args *args1 = dai->driver->dai_args;
+
+ if (!args1 || !args2)
+ return 0;
+
+ if (args1->np != args2->np)
+ return 0;
+
+ for (int i = 0; i < args1->args_count; i++)
+ if (args1->args[i] != args2->args[i])
+ return 0;
+
+ return 1;
+}
+
+int snd_soc_dai_matches_dlc(struct snd_soc_dai *dai,
+ const struct snd_soc_dai_link_component *dlc)
+{
+ if (!dlc)
+ return 0;
+
+ if (dlc->dai_args)
+ return snd_soc_dai_matches_args(dai, dlc->dai_args);
+
+ if (!dlc->dai_name)
+ return 1;
+
+ /* see snd_soc_dai_name() */
+
+ if (dai->driver->name &&
+ strcmp(dlc->dai_name, dai->driver->name) == 0)
+ return 1;
+
+ if (strcmp(dlc->dai_name, dai->name) == 0)
+ return 1;
+
+ if (dai->component->name &&
+ strcmp(dlc->dai_name, dai->component->name) == 0)
+ return 1;
+
+ return 0;
+}
+
+const char *snd_soc_dai_name(const struct snd_soc_dai *dai)
+{
+ /* see snd_soc_dai_matches_dlc() */
+ if (dai->driver->name)
+ return dai->driver->name;
+
+ if (dai->name)
+ return dai->name;
+
+ if (dai->component->name)
+ return dai->component->name;
+
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_name);
+
+int snd_soc_dai_id(const struct snd_soc_dai *dai)
+{
+ return dai->id;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_id);
+
+const struct snd_soc_pcm_stream *
+snd_soc_dai_pcm_stream_get_i(const struct snd_soc_dai *dai, int stream)
+{
+ return (stream == SNDRV_PCM_STREAM_PLAYBACK) ?
+ &dai->driver->playback : &dai->driver->capture;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_pcm_stream_get_i);
+
+struct snd_soc_dapm_widget *snd_soc_dai_stream_widget_get(struct snd_soc_dai *dai, int stream)
+{
+ return dai->stream[stream].widget;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_stream_widget_get);
+
+void snd_soc_dai_stream_widget_set(struct snd_soc_dai *dai, int stream, struct snd_soc_dapm_widget *widget)
+{
+ dai->stream[stream].widget = widget;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_stream_widget_set);
+
+void *snd_soc_dai_stream_dma_data_get_i(const struct snd_soc_dai *dai, int stream)
+{
+ return dai->stream[stream].dma_data;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_stream_dma_data_get_i);
+
+void snd_soc_dai_stream_dma_data_set_i(struct snd_soc_dai *dai, int stream, void *data)
+{
+ dai->stream[stream].dma_data = data;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_stream_dma_data_set_i);
+
+unsigned int snd_soc_dai_stream_tdm_mask_get(const struct snd_soc_dai *dai, int stream)
+{
+ return dai->stream[stream].tdm_mask;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_stream_tdm_mask_get);
+
+void snd_soc_dai_stream_tdm_mask_set(struct snd_soc_dai *dai, int stream, unsigned int tdm_mask)
+{
+ dai->stream[stream].tdm_mask = tdm_mask;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_stream_tdm_mask_set);
+
+unsigned int snd_soc_dai_stream_active(const struct snd_soc_dai *dai, int stream)
+{
+ /* see snd_soc_dai_active_update() for setup */
+ return dai->stream[stream].active;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_stream_active);
+
+/**
+ * snd_soc_dai_set_stream() - Configures a DAI for stream operation
+ * @dai: DAI
+ * @stream: STREAM (opaque structure depending on DAI type)
+ * @direction: Stream direction(Playback/Capture)
+ * Some subsystems, such as SoundWire, don't have a notion of direction and we reuse
+ * the ASoC stream direction to configure sink/source ports.
+ * Playback maps to source ports and Capture for sink ports.
+ *
+ * This should be invoked with NULL to clear the stream set previously.
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int snd_soc_dai_set_stream(struct snd_soc_dai *dai, void *stream, int direction)
+{
+ if (dai->driver->ops->set_stream)
+ return dai->driver->ops->set_stream(dai, stream, direction);
+ else
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_stream);
+
+/**
+ * snd_soc_dai_get_stream() - Retrieves stream from DAI
+ * @dai: DAI
+ * @direction: Stream direction(Playback/Capture)
+ *
+ * This routine only retrieves that was previously configured
+ * with snd_soc_dai_get_stream()
+ *
+ * Returns pointer to stream or an ERR_PTR value, e.g.
+ * ERR_PTR(-ENOTSUPP) if callback is not supported;
+ */
+void *snd_soc_dai_get_stream(struct snd_soc_dai *dai, int direction)
+{
+ if (dai->driver->ops->get_stream)
+ return dai->driver->ops->get_stream(dai, direction);
+ else
+ return ERR_PTR(-ENOTSUPP);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_get_stream);
+
+void snd_soc_dai_unregister(struct snd_soc_dai *dai)
+{
+ lockdep_assert_held(&client_mutex);
+
+ dev_dbg(dai->dev, "ASoC: Unregistered DAI '%s'\n", dai->name);
+ list_del(&dai->list);
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_unregister);
+
+/**
+ * snd_soc_dai_register - Register a DAI dynamically & create its widgets
+ *
+ * @component: The component the DAIs are registered for
+ * @dai_drv: DAI driver to use for the DAI
+ * @legacy_dai_naming: if %true, use legacy single-name format;
+ * if %false, use multiple-name format;
+ *
+ * Topology can use this API to register DAIs when probing a component.
+ * These DAIs's widgets will be freed in the card cleanup and the DAIs
+ * will be freed in the component cleanup.
+ */
+struct snd_soc_dai *snd_soc_dai_register(struct snd_soc_component *component,
+ struct snd_soc_dai_driver *dai_drv,
+ bool legacy_dai_naming)
+{
+ struct device *dev = component->dev;
+ struct snd_soc_dai *dai;
+
+ lockdep_assert_held(&client_mutex);
+
+ dai = devm_kzalloc(dev, sizeof(*dai), GFP_KERNEL);
+ if (dai == NULL)
+ return NULL;
+
+ /*
+ * Back in the old days when we still had component-less DAIs,
+ * instead of having a static name, component-less DAIs would
+ * inherit the name of the parent device so it is possible to
+ * register multiple instances of the DAI. We still need to keep
+ * the same naming style even though those DAIs are not
+ * component-less anymore.
+ */
+ if (legacy_dai_naming &&
+ (dai_drv->id == 0 || dai_drv->name == NULL)) {
+ dai->name = snd_soc_fmt_single_name(dev, &dai->id);
+ } else {
+ dai->name = snd_soc_fmt_multiple_name(dev, dai_drv);
+ if (dai_drv->id)
+ dai->id = dai_drv->id;
+ else
+ dai->id = component->num_dai;
+ }
+ if (!dai->name)
+ return NULL;
+
+ dai->component = component;
+ dai->dev = dev;
+ dai->driver = dai_drv;
+
+ /* see for_each_component_dais */
+ list_add_tail(&dai->list, &component->dai_list);
+ component->num_dai++;
+
+ dev_dbg(dev, "ASoC: Registered DAI '%s'\n", dai->name);
+ return dai;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_register);
+
+unsigned int snd_soc_dai_get_symmetric_rate(struct snd_soc_dai *dai)
+{
+ return dai->symmetric_rate;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_get_symmetric_rate);
+
+unsigned int snd_soc_dai_get_symmetric_channels(struct snd_soc_dai *dai)
+{
+ return dai->symmetric_channels;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_get_symmetric_channels);
+
+unsigned int snd_soc_dai_get_symmetric_sample_bits(struct snd_soc_dai *dai)
+{
+ return dai->symmetric_sample_bits;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_get_symmetric_sample_bits);
+
+void snd_soc_dai_symmetric_set_params(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params)
+{
+ if (params) {
+ dai->symmetric_rate = params_rate(params);
+ dai->symmetric_channels = params_channels(params);
+ dai->symmetric_sample_bits = snd_pcm_format_physical_width(params_format(params));
+ } else {
+ dai->symmetric_rate = 0;
+ dai->symmetric_channels = 0;
+ dai->symmetric_sample_bits = 0;
+ }
+}
+
+int snd_soc_dai_symmetric_apply(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ int ret;
+
+ if (!snd_soc_dai_active(dai))
+ return 0;
+
+#define __symmetric_apply(name, NAME) \
+ if (dai->symmetric_##name && \
+ (dai->driver->symmetric_##name || rtd->dai_link->symmetric_##name)) { \
+ dev_dbg(dai->dev, "ASoC: Symmetry forces %s to %d\n", \
+ #name, dai->symmetric_##name); \
+ \
+ ret = snd_pcm_hw_constraint_single(substream->runtime, \
+ SNDRV_PCM_HW_PARAM_##NAME, \
+ dai->symmetric_##name); \
+ if (ret < 0) \
+ return snd_soc_ret(dai->dev, ret, \
+ "Unable to apply %s constraint\n", #name); \
+ }
+
+ __symmetric_apply(rate, RATE);
+ __symmetric_apply(channels, CHANNELS);
+ __symmetric_apply(sample_bits, SAMPLE_BITS);
+
+ return 0;
+}
+
+int snd_soc_dai_symmetric_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai d;
+ struct snd_soc_dai *dai;
+ struct snd_soc_dai *cpu_dai;
+ unsigned int symmetry, i;
+
+ d.name = __func__;
+ snd_soc_dai_symmetric_set_params(&d, params);
+
+#define __symmetric_params(xxx) \
+ symmetry = rtd->dai_link->symmetric_##xxx; \
+ for_each_rtd_dais(rtd, i, dai) \
+ symmetry |= dai->driver->symmetric_##xxx; \
+ \
+ if (symmetry) \
+ for_each_rtd_cpu_dais(rtd, i, cpu_dai) \
+ if (!snd_soc_dai_is_dummy(cpu_dai) && \
+ cpu_dai->symmetric_##xxx && \
+ cpu_dai->symmetric_##xxx != d.symmetric_##xxx) \
+ return snd_soc_ret(rtd->dev, -EINVAL, \
+ "unmatched %s symmetry: %s:%d - %s:%d\n", \
+ #xxx, cpu_dai->name, cpu_dai->symmetric_##xxx, \
+ d.name, d.symmetric_##xxx);
+
+ /* reject unmatched parameters when applying symmetry */
+ __symmetric_params(rate);
+ __symmetric_params(channels);
+ __symmetric_params(sample_bits);
+
+ return 0;
+}
+
+void snd_soc_dai_symmetric_update(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
+ struct snd_soc_dai_link *link = rtd->dai_link;
+ struct snd_soc_dai *dai;
+ unsigned int symmetry, i;
+
+ symmetry = link->symmetric_rate ||
+ link->symmetric_channels ||
+ link->symmetric_sample_bits;
+
+ for_each_rtd_dais(rtd, i, dai)
+ symmetry = symmetry ||
+ dai->driver->symmetric_rate ||
+ dai->driver->symmetric_channels ||
+ dai->driver->symmetric_sample_bits;
+
+ if (symmetry)
+ substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
+}
+
+struct clk *snd_soc_dai_get_bclk(struct snd_soc_dai *dai)
+{
+ return dai->bclk;
+}
+
+unsigned int snd_soc_dai_get_bclk_ratio(struct snd_soc_dai *dai)
+{
+ return dai->bclk_ratio;
+}
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 06b85313af12..74be5e209493 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -37,6 +37,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
+#include "soc-internal.h"
#include <trace/events/asoc.h>
@@ -4010,7 +4011,7 @@ static int dapm_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
if (ret < 0)
return ret;
- snd_soc_dai_activate(source, substream->stream);
+ snd_soc_dai_active_update(source, substream->stream, 1);
}
substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
@@ -4021,7 +4022,7 @@ static int dapm_dai_link_event_pre_pmu(struct snd_soc_dapm_widget *w,
if (ret < 0)
return ret;
- snd_soc_dai_activate(sink, substream->stream);
+ snd_soc_dai_active_update(sink, substream->stream, 1);
}
substream->hw_opened = 1;
@@ -4150,14 +4151,14 @@ static int dapm_dai_link_event(struct snd_soc_dapm_widget *w,
substream->stream = SNDRV_PCM_STREAM_CAPTURE;
snd_soc_dapm_widget_for_each_source_path(w, path) {
source = path->source->priv;
- snd_soc_dai_deactivate(source, substream->stream);
+ snd_soc_dai_active_update(source, substream->stream, -1);
snd_soc_dai_shutdown(source, substream, 0);
}
substream->stream = SNDRV_PCM_STREAM_PLAYBACK;
snd_soc_dapm_widget_for_each_sink_path(w, path) {
sink = path->sink->priv;
- snd_soc_dai_deactivate(sink, substream->stream);
+ snd_soc_dai_active_update(sink, substream->stream, -1);
snd_soc_dai_shutdown(sink, substream, 0);
}
break;
diff --git a/sound/soc/soc-generic-dmaengine-pcm.c b/sound/soc/soc-generic-dmaengine-pcm.c
index 65b7ec52a054..f6206ecaf26f 100644
--- a/sound/soc/soc-generic-dmaengine-pcm.c
+++ b/sound/soc/soc-generic-dmaengine-pcm.c
@@ -32,7 +32,7 @@ static struct device *dmaengine_dma_dev(struct dmaengine_pcm *pcm,
if (!pcm->chan[substream->stream])
return NULL;
- return pcm->chan[substream->stream]->device->dev;
+ return dmaengine_get_dma_device(pcm->chan[substream->stream]);
}
/**
@@ -242,9 +242,11 @@ static int dmaengine_pcm_new(struct snd_soc_component *component,
if (!substream)
continue;
- if (!pcm->chan[i] && config->chan_names[i])
- pcm->chan[i] = dma_request_slave_channel(dev,
- config->chan_names[i]);
+ if (!pcm->chan[i] && config->chan_names[i]) {
+ pcm->chan[i] = dma_request_chan(dev, config->chan_names[i]);
+ if (IS_ERR(pcm->chan[i]))
+ pcm->chan[i] = NULL;
+ }
if (!pcm->chan[i] && (pcm->flags & SND_DMAENGINE_PCM_FLAG_COMPAT)) {
pcm->chan[i] = dmaengine_pcm_compat_request_channel(
diff --git a/sound/soc/soc-internal.h b/sound/soc/soc-internal.h
new file mode 100644
index 000000000000..31559918355f
--- /dev/null
+++ b/sound/soc/soc-internal.h
@@ -0,0 +1,39 @@
+/* SPDX-License-Identifier: GPL-2.0-only
+ *
+ * soc-internal.h
+ *
+ * Copyright (c) 2026 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#ifndef __SOC_INTERNAL_H
+#define __SOC_INTERNAL_H
+
+/*
+ * This header is for ALSA SoC Framework internal, not for Vender drivers.
+ * The ALSA SoC functions for Vender drivers are defined in linux/include/sound/xxx.h
+ * as snd_soc_xxx();
+ */
+
+/*
+ * In soc-core
+ */
+extern struct mutex client_mutex;
+
+char *snd_soc_fmt_single_name(struct device *dev, int *id);
+char *snd_soc_fmt_multiple_name(struct device *dev, struct snd_soc_dai_driver *dai_drv);
+int snd_soc_add_controls(struct snd_card *card, struct device *dev,
+ const struct snd_kcontrol_new *controls, int num_controls,
+ const char *prefix, void *data);
+
+/*
+ * In soc-dai
+ */
+void snd_soc_dai_symmetric_set_params(struct snd_soc_dai *dai,
+ struct snd_pcm_hw_params *params);
+int snd_soc_dai_symmetric_apply(struct snd_pcm_substream *substream, struct snd_soc_dai *dai);
+int snd_soc_dai_symmetric_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params);
+void snd_soc_dai_symmetric_update(struct snd_pcm_substream *substream);
+struct clk *snd_soc_dai_get_bclk(struct snd_soc_dai *dai);
+unsigned int snd_soc_dai_get_bclk_ratio(struct snd_soc_dai *dai);
+void snd_soc_dai_active_update(struct snd_soc_dai *dai, int stream, int action);
+
+#endif /* __SOC_INTERNAL_H */
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index c935571d43e9..142f407e3939 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -305,8 +305,8 @@ EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
* Callback to provide information about a single mixer control, or a double
* mixer control that spans 2 registers of the SX TLV type. SX TLV controls
* have a range that represents both positive and negative values either side
- * of zero but without a sign bit. min is the minimum register value, max is
- * the number of steps.
+ * of zero but without a sign bit. min is the register value representing the
+ * minimum volume, max is the number of volume steps.
*
* Returns 0 for success.
*/
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index 3137c091bdb8..ef8ccf52798e 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -26,6 +26,7 @@
#include <sound/soc-dpcm.h>
#include <sound/soc-link.h>
#include <sound/initval.h>
+#include "soc-internal.h"
DEFINE_GUARD(snd_soc_card_mutex, struct snd_soc_card *,
@@ -367,7 +368,7 @@ void snd_soc_runtime_action(struct snd_soc_pcm_runtime *rtd,
snd_soc_dpcm_mutex_assert_held(rtd);
for_each_rtd_dais(rtd, i, dai)
- snd_soc_dai_action(dai, stream, action);
+ snd_soc_dai_active_update(dai, stream, action);
/* Increments/Decrements the active count for components without DAIs */
for_each_rtd_components(rtd, i, component) {
@@ -427,50 +428,6 @@ void dpcm_dapm_stream_event(struct snd_soc_pcm_runtime *fe, int dir, int event)
snd_soc_dapm_stream_event(fe, dir, event);
}
-void soc_pcm_set_dai_params(struct snd_soc_dai *dai,
- struct snd_pcm_hw_params *params)
-{
- if (params) {
- dai->symmetric_rate = params_rate(params);
- dai->symmetric_channels = params_channels(params);
- dai->symmetric_sample_bits = snd_pcm_format_physical_width(params_format(params));
- } else {
- dai->symmetric_rate = 0;
- dai->symmetric_channels = 0;
- dai->symmetric_sample_bits = 0;
- }
-}
-
-static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
- struct snd_soc_dai *soc_dai)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- int ret;
-
- if (!snd_soc_dai_active(soc_dai))
- return 0;
-
-#define __soc_pcm_apply_symmetry(name, NAME) \
- if (soc_dai->symmetric_##name && \
- (soc_dai->driver->symmetric_##name || rtd->dai_link->symmetric_##name)) { \
- dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %s to %d\n",\
- #name, soc_dai->symmetric_##name); \
- \
- ret = snd_pcm_hw_constraint_single(substream->runtime, \
- SNDRV_PCM_HW_PARAM_##NAME,\
- soc_dai->symmetric_##name); \
- if (ret < 0) \
- return snd_soc_ret(soc_dai->dev, ret, \
- "Unable to apply %s constraint\n", #name); \
- }
-
- __soc_pcm_apply_symmetry(rate, RATE);
- __soc_pcm_apply_symmetry(channels, CHANNELS);
- __soc_pcm_apply_symmetry(sample_bits, SAMPLE_BITS);
-
- return 0;
-}
-
/*
* Shared BCLK constraint: when multiple DAIs share the same physical BCLK,
* constrain hw_params so that the BCLK rate (rate * channels * sample_bits,
@@ -488,17 +445,24 @@ static int soc_pcm_shared_bclk_rule_rate(struct snd_pcm_hw_params *params,
struct snd_interval *rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval constraint = { .empty = 1 };
unsigned int target_rate;
+ struct clk *dai_bclk;
+ unsigned int dai_bclk_ratio;
int i;
/* Protect the rtd list traversal with the ASoC card mutex helper. */
guard(snd_soc_card_mutex)(card);
+ dai_bclk = snd_soc_dai_get_bclk(dai);
+ dai_bclk_ratio = snd_soc_dai_get_bclk_ratio(dai);
+
/* Scan all DAIs on the card for an active peer sharing the same BCLK */
for_each_card_rtds(card, rtd) {
for_each_rtd_cpu_dais(rtd, i, other_dai) {
+ struct clk *other_dai_bclk = snd_soc_dai_get_bclk(other_dai);
+
if (other_dai == dai)
continue;
- if (!other_dai->bclk)
+ if (!other_dai_bclk)
continue;
if (!snd_soc_dai_active(other_dai))
continue;
@@ -510,10 +474,10 @@ static int soc_pcm_shared_bclk_rule_rate(struct snd_pcm_hw_params *params,
*/
if (!other_dai->symmetric_rate)
continue;
- if (!clk_is_match(dai->bclk, other_dai->bclk))
+ if (!clk_is_match(dai_bclk, other_dai_bclk))
continue;
- active_bclk_rate = clk_get_rate(other_dai->bclk);
+ active_bclk_rate = clk_get_rate(other_dai_bclk);
if (active_bclk_rate)
goto found;
}
@@ -522,13 +486,13 @@ static int soc_pcm_shared_bclk_rule_rate(struct snd_pcm_hw_params *params,
return 0;
found:
- if (dai->bclk_ratio) {
+ if (dai_bclk_ratio) {
/*
* Driver has set an explicit BCLK ratio (e.g. for TDM where
* BCLK = rate * slots * slot_width). The only valid rate is
* active_bclk_rate / bclk_ratio.
*/
- target_rate = active_bclk_rate / dai->bclk_ratio;
+ target_rate = active_bclk_rate / dai_bclk_ratio;
constraint.min = target_rate;
constraint.max = target_rate;
@@ -565,7 +529,7 @@ found:
static int soc_pcm_apply_shared_bclk(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- if (!dai->bclk)
+ if (!snd_soc_dai_get_bclk(dai))
return 0;
dev_dbg(dai->dev,
@@ -579,62 +543,6 @@ static int soc_pcm_apply_shared_bclk(struct snd_pcm_substream *substream,
-1);
}
-static int soc_pcm_params_symmetry(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_soc_dai d;
- struct snd_soc_dai *dai;
- struct snd_soc_dai *cpu_dai;
- unsigned int symmetry, i;
-
- d.name = __func__;
- soc_pcm_set_dai_params(&d, params);
-
-#define __soc_pcm_params_symmetry(xxx) \
- symmetry = rtd->dai_link->symmetric_##xxx; \
- for_each_rtd_dais(rtd, i, dai) \
- symmetry |= dai->driver->symmetric_##xxx; \
- \
- if (symmetry) \
- for_each_rtd_cpu_dais(rtd, i, cpu_dai) \
- if (!snd_soc_dai_is_dummy(cpu_dai) && \
- cpu_dai->symmetric_##xxx && \
- cpu_dai->symmetric_##xxx != d.symmetric_##xxx) \
- return snd_soc_ret(rtd->dev, -EINVAL, \
- "unmatched %s symmetry: %s:%d - %s:%d\n", \
- #xxx, cpu_dai->name, cpu_dai->symmetric_##xxx, \
- d.name, d.symmetric_##xxx);
-
- /* reject unmatched parameters when applying symmetry */
- __soc_pcm_params_symmetry(rate);
- __soc_pcm_params_symmetry(channels);
- __soc_pcm_params_symmetry(sample_bits);
-
- return 0;
-}
-
-static void soc_pcm_update_symmetry(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_soc_dai_link *link = rtd->dai_link;
- struct snd_soc_dai *dai;
- unsigned int symmetry, i;
-
- symmetry = link->symmetric_rate ||
- link->symmetric_channels ||
- link->symmetric_sample_bits;
-
- for_each_rtd_dais(rtd, i, dai)
- symmetry = symmetry ||
- dai->driver->symmetric_rate ||
- dai->driver->symmetric_channels ||
- dai->driver->symmetric_sample_bits;
-
- if (symmetry)
- substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
-}
-
static void soc_pcm_set_msb(struct snd_pcm_substream *substream, int bits)
{
struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
@@ -879,7 +787,7 @@ static int soc_pcm_clean(struct snd_soc_pcm_runtime *rtd,
/* Make sure DAI parameters cleared if the DAI becomes inactive */
for_each_rtd_dais(rtd, i, dai) {
if (snd_soc_dai_active(dai) == 0)
- soc_pcm_set_dai_params(dai, NULL);
+ snd_soc_dai_symmetric_set_params(dai, NULL);
}
}
@@ -1001,7 +909,7 @@ static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd,
/* Check that the codec and cpu DAIs are compatible */
soc_pcm_init_runtime_hw(substream);
- soc_pcm_update_symmetry(substream);
+ snd_soc_dai_symmetric_update(substream);
ret = soc_hw_sanity_check(substream);
if (ret < 0)
@@ -1011,7 +919,7 @@ static int __soc_pcm_open(struct snd_soc_pcm_runtime *rtd,
/* Symmetry only applies if we've already got an active stream. */
for_each_rtd_dais(rtd, i, dai) {
- ret = soc_pcm_apply_symmetry(substream, dai);
+ ret = snd_soc_dai_symmetric_apply(substream, dai);
if (ret != 0)
goto err;
}
@@ -1135,7 +1043,7 @@ static int soc_pcm_hw_clean(struct snd_soc_pcm_runtime *rtd,
/* clear the corresponding DAIs parameters when going to be inactive */
for_each_rtd_dais(rtd, i, dai) {
if (snd_soc_dai_active(dai) == 1)
- soc_pcm_set_dai_params(dai, NULL);
+ snd_soc_dai_symmetric_set_params(dai, NULL);
if (snd_soc_dai_stream_active(dai, substream->stream) == 1) {
if (!snd_soc_dai_mute_is_ctrled_at_trigger(dai))
@@ -1197,7 +1105,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
snd_soc_dpcm_mutex_assert_held(rtd);
- ret = soc_pcm_params_symmetry(substream, params);
+ ret = snd_soc_dai_symmetric_params(substream, params);
if (ret)
goto out;
@@ -1245,7 +1153,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
if(ret < 0)
goto out;
- soc_pcm_set_dai_params(codec_dai, &tmp_params);
+ snd_soc_dai_symmetric_set_params(codec_dai, &tmp_params);
snd_soc_dapm_update_dai(substream, &tmp_params, codec_dai);
}
@@ -1283,7 +1191,7 @@ static int __soc_pcm_hw_params(struct snd_pcm_substream *substream,
goto out;
/* store the parameters for each DAI */
- soc_pcm_set_dai_params(cpu_dai, &tmp_params);
+ snd_soc_dai_symmetric_set_params(cpu_dai, &tmp_params);
snd_soc_dapm_update_dai(substream, &tmp_params, cpu_dai);
}
@@ -2032,11 +1940,11 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
int i;
/* apply symmetry for FE */
- soc_pcm_update_symmetry(fe_substream);
+ snd_soc_dai_symmetric_update(fe_substream);
for_each_rtd_cpu_dais (fe, i, fe_cpu_dai) {
/* Symmetry only applies if we've got an active stream. */
- err = soc_pcm_apply_symmetry(fe_substream, fe_cpu_dai);
+ err = snd_soc_dai_symmetric_apply(fe_substream, fe_cpu_dai);
if (err < 0)
goto error;
}
@@ -2057,11 +1965,11 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
if (rtd->dai_link->be_hw_params_fixup)
continue;
- soc_pcm_update_symmetry(be_substream);
+ snd_soc_dai_symmetric_update(be_substream);
/* Symmetry only applies if we've got an active stream. */
for_each_rtd_dais(rtd, i, dai) {
- err = soc_pcm_apply_symmetry(fe_substream, dai);
+ err = snd_soc_dai_symmetric_apply(fe_substream, dai);
if (err < 0)
goto error;
}
diff --git a/sound/soc/sof/amd/Kconfig b/sound/soc/sof/amd/Kconfig
index 903e7ec3b3ba..d64f682cfbea 100644
--- a/sound/soc/sof/amd/Kconfig
+++ b/sound/soc/sof/amd/Kconfig
@@ -109,6 +109,7 @@ config SND_SOC_SOF_AMD_ACP7X
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.B and ACP7.F PCI
revision based platforms.
diff --git a/sound/soc/sof/amd/acp-common.c b/sound/soc/sof/amd/acp-common.c
index df656cdc1527..33540f7c421b 100644
--- a/sound/soc/sof/amd/acp-common.c
+++ b/sound/soc/sof/amd/acp-common.c
@@ -16,6 +16,7 @@
#include "acp.h"
#include "acp-dsp-offset.h"
#include <sound/sof/xtensa.h>
+#include "../../amd/acp/soc_amd_sdw_common.h"
/**
* amd_sof_ipc_dump() - This function is called when IPC tx times out.
@@ -177,6 +178,7 @@ struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev)
struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
const struct sof_dev_desc *desc = sof_pdata->desc;
struct snd_soc_acpi_mach *mach = NULL;
+ struct amd_pdm_pdata *pdm_pdata;
if (desc->machines)
mach = snd_soc_acpi_find_machine(desc->machines);
@@ -188,7 +190,22 @@ struct snd_soc_acpi_mach *amd_sof_machine_select(struct snd_sof_dev *sdev)
}
}
+ mach = devm_kmemdup(sdev->dev, mach, sizeof(*mach), GFP_KERNEL);
+ if (!mach) {
+ dev_err(sdev->dev, "failed to allocate machine entry copy\n");
+ return NULL;
+ }
+
mach->mach_params.subsystem_rev = acp_data->pci_rev;
+
+ if (acp_data->pdm_sel) {
+ pdm_pdata = devm_kzalloc(sdev->dev, sizeof(*pdm_pdata), GFP_KERNEL);
+ if (!pdm_pdata)
+ return NULL;
+ pdm_pdata->pdm_sel = acp_data->pdm_sel;
+ mach->pdata = pdm_pdata;
+ }
+
sof_pdata->tplg_filename = mach->sof_tplg_filename;
sof_pdata->fw_filename = mach->fw_filename;
diff --git a/sound/soc/sof/amd/acp-dsp-offset.h b/sound/soc/sof/amd/acp-dsp-offset.h
index bea1bd3afa70..3984cd627db6 100644
--- a/sound/soc/sof/amd/acp-dsp-offset.h
+++ b/sound/soc/sof/amd/acp-dsp-offset.h
@@ -156,5 +156,22 @@
#define ACP7X_IDMA_ERROR_MASK 0x1FF9FF
#define ACP7X_ZSC_DSP_CTRL 0x001014
#define ACP7X_PME_EN ACP70_PME_EN
+/* SW enable base for SDW0; manager N uses ACP7X_SW_EN + (N * 0x2000) */
+#define ACP7X_SW_EN 0x5200
+
+/* ACP7X SoundWire IO registers (non-gsync, up to 4 managers SW0-SW3) */
+#define ACP7X_EXTERNAL_SW_INTR_STAT 0x001A24
+#define ACP7X_SW_WAKE_EN 0x001458
+#define ACP7X_SW_PME_STS 0x001474
+#define ACP7X_SDW_STAT BIT(23)
+#define ACP7X_ERROR_IRQ BIT(29)
+#define ACP7X_SW_WAKE_EN_MASK BIT(0)
+#define ACP7X_SW_HOST_WAKE_MASK BIT(22)
+#define ACP7X_SDW_HOST_WAKE_STAT BIT(24)
+#define ACP7X_SW_FIFO_ERROR_REASON 0x50C4
+#define ACP7X_SW_ERROR_REASON1 0x50CC
+#define ACP7X_SW_ERROR_REASON2 0x50D4
+#define ACP7X_EXTERNAL_SDW_STAT BIT(16)
+#define ACP7X_SW_ERR_STAT_MASK BIT(11)
#endif
diff --git a/sound/soc/sof/amd/acp.c b/sound/soc/sof/amd/acp.c
index f89ad86260b4..b059039c9e0d 100644
--- a/sound/soc/sof/amd/acp.c
+++ b/sound/soc/sof/amd/acp.c
@@ -22,11 +22,20 @@
#include "../ops.h"
#include "acp.h"
#include "acp-dsp-offset.h"
+#include "../../amd/acp/soc_amd_sdw_common.h"
+#include "acp7x.h"
static bool enable_fw_debug;
module_param(enable_fw_debug, bool, 0444);
MODULE_PARM_DESC(enable_fw_debug, "Enable Firmware debug");
+static const u32 acp7x_sof_sdw_ext_stat[ACP7X_SDW_MAX_MANAGER_COUNT] = {
+ ACP7X_SDW_STAT << 0,
+ ACP7X_SDW_STAT << 1,
+ ACP7X_SDW_STAT << 2,
+ ACP7X_SDW_STAT << 3,
+};
+
static struct acp_quirk_entry quirk_valve_galileo = {
.signed_fw_image = true,
.skip_iram_dram_size_mod = true,
@@ -581,30 +590,196 @@ static irqreturn_t acp_irq_handler(int irq, void *dev_id)
return IRQ_NONE;
}
+/* ACP7X SoundWire IO data tables */
+
+const struct sof_amd_acp7x_sdw_err_regs acp7x_sdw_err_regs[ACP7X_SDW_MAX_MANAGER_COUNT] = {
+ { ACP7X_SW_ERR_STAT_MASK << 0,
+ ACP7X_SW_FIFO_ERROR_REASON + (0 * 0x2000),
+ ACP7X_SW_ERROR_REASON1 + (0 * 0x2000),
+ ACP7X_SW_ERROR_REASON2 + (0 * 0x2000) },
+ { ACP7X_SW_ERR_STAT_MASK << 1,
+ ACP7X_SW_FIFO_ERROR_REASON + (1 * 0x2000),
+ ACP7X_SW_ERROR_REASON1 + (1 * 0x2000),
+ ACP7X_SW_ERROR_REASON2 + (1 * 0x2000) },
+ { ACP7X_SW_ERR_STAT_MASK << 2,
+ ACP7X_SW_FIFO_ERROR_REASON + (2 * 0x2000),
+ ACP7X_SW_ERROR_REASON1 + (2 * 0x2000),
+ ACP7X_SW_ERROR_REASON2 + (2 * 0x2000) },
+ { ACP7X_SW_ERR_STAT_MASK << 3,
+ ACP7X_SW_FIFO_ERROR_REASON + (3 * 0x2000),
+ ACP7X_SW_ERROR_REASON1 + (3 * 0x2000),
+ ACP7X_SW_ERROR_REASON2 + (3 * 0x2000) },
+};
+
+const struct acp7x_sdw_wake_src acp7x_sdw_wake_sources[] = {
+ { ACP7X_SDW_HOST_WAKE_STAT << 0, ACP7X_SW_PME_STS + (0 * 4),
+ ACP7X_SW_WAKE_EN_MASK << 0, 0 },
+ { ACP7X_SDW_HOST_WAKE_STAT << 1, ACP7X_SW_PME_STS + (1 * 4),
+ ACP7X_SW_WAKE_EN_MASK << 1, 1 },
+ { ACP7X_SDW_HOST_WAKE_STAT << 2, ACP7X_SW_PME_STS + (2 * 4),
+ ACP7X_SW_WAKE_EN_MASK << 2, 2 },
+ { ACP7X_SDW_HOST_WAKE_STAT << 3, ACP7X_SW_PME_STS + (3 * 4),
+ ACP7X_SW_WAKE_EN_MASK << 3, 3 },
+};
+
+/* ACP7X SoundWire IO interrupt and wake helpers */
+
+static void sof_amd_acp7x_handle_one_sdw_err(struct snd_sof_dev *sdev, u32 *err_stat,
+ u32 acp_error_stat_reg,
+ const struct sof_amd_acp7x_sdw_err_regs *regs)
+{
+ if (!(*err_stat & regs->err_stat_mask))
+ return;
+
+ *err_stat &= ~regs->err_stat_mask;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, acp_error_stat_reg, *err_stat);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, regs->fifo_err_reason, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, regs->err_reason1, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, regs->err_reason2, 0);
+}
+
+/* Clears SoundWire error registers for all managers; runs in hard IRQ context. */
+static void sof_amd_acp7x_clear_sdw_err_regs(struct snd_sof_dev *sdev)
+{
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+ u32 err_stat;
+ unsigned int instance;
+
+ err_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->acp_error_stat);
+
+ for (instance = 0; instance < ACP7X_SDW_MAX_MANAGER_COUNT; instance++)
+ sof_amd_acp7x_handle_one_sdw_err(sdev, &err_stat, desc->acp_error_stat,
+ &acp7x_sdw_err_regs[instance]);
+}
+
+static void sof_amd_handle_acp7x_sdw_wake_event(struct acp_dev_data *adata)
+{
+ struct amd_sdw_manager *amd_manager;
+ unsigned int instance;
+
+ for (instance = 0; instance < ACP7X_SDW_MAX_MANAGER_COUNT; instance++) {
+ if (!adata->acp7x_sdw_wake_event[instance])
+ continue;
+ if (!adata->sdw->pdev[instance])
+ continue;
+
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[instance]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ adata->acp7x_sdw_wake_event[instance] = false;
+ }
+}
+
+static int sof_amd_check_and_handle_acp7x_sdw_wake_irq(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
+ const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
+ unsigned int ext_intr_stat1;
+ unsigned int sdw_pme_stat, sdw_wake_en;
+ unsigned int i;
+ bool sdw_wake_irq = false;
+
+ ext_intr_stat1 = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat1);
+
+ for (i = 0; i < ARRAY_SIZE(acp7x_sdw_wake_sources); i++) {
+ const struct acp7x_sdw_wake_src *src = &acp7x_sdw_wake_sources[i];
+
+ bool woke = false;
+
+ if (ext_intr_stat1 & src->host_stat_mask) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat1,
+ src->host_stat_mask);
+ woke = true;
+ }
+
+ sdw_pme_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, src->pme_sts_reg);
+ if (sdw_pme_stat) {
+ sdw_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_WAKE_EN);
+ sdw_wake_en &= ~src->wake_en_mask;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_SW_WAKE_EN, sdw_wake_en);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, src->pme_sts_reg, sdw_pme_stat);
+ woke = true;
+ }
+
+ if (woke) {
+ adata->acp7x_sdw_wake_event[src->instance] = true;
+ sdw_wake_irq = true;
+ }
+ }
+
+ if (sdw_wake_irq) {
+ sof_amd_handle_acp7x_sdw_wake_event(adata);
+ return WAKE_IRQ_HANDLED;
+ }
+ return WAKE_IRQ_NONE;
+}
+
+static void sof_acp7x_handle_sdw_manager_irq(struct snd_sof_dev *sdev,
+ struct acp_dev_data *adata,
+ const struct sof_amd_acp_desc *desc,
+ unsigned int instance,
+ u32 ext_stat_mask)
+{
+ struct amd_sdw_manager *amd_manager;
+ unsigned int sdw_intr_stat;
+ u32 sw_intr_reg = ACP7X_EXTERNAL_SW_INTR_STAT + (instance * 4);
+
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ext_stat_mask);
+ sdw_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, sw_intr_reg);
+ if (sdw_intr_stat & ACP7X_EXTERNAL_SDW_STAT) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, sw_intr_reg, ACP7X_EXTERNAL_SDW_STAT);
+ if (!adata->sdw->pdev[instance])
+ return;
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[instance]->dev);
+ if (amd_manager)
+ schedule_work(&amd_manager->amd_sdw_irq_thread);
+ }
+}
+
static irqreturn_t acp7x_irq_handler(int irq, void *dev_id)
{
struct snd_sof_dev *sdev = dev_id;
+ struct acp_dev_data *adata = sdev->pdata->hw_pdata;
const struct sof_amd_acp_desc *desc = get_chip_info(sdev->pdata);
unsigned int base = desc->dsp_intr_base;
unsigned int val;
unsigned int ext_intr_stat;
- int irq_flag = 0;
+ unsigned int instance;
+ int irq_flag = 0, wake_irq_flag = 0;
+ bool dsp_irq = false;
val = snd_sof_dsp_read(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET);
if (val & ACP_DSP_TO_HOST_IRQ) {
snd_sof_dsp_write(sdev, ACP_DSP_BAR, base + DSP_SW_INTR_STAT_OFFSET,
ACP_DSP_TO_HOST_IRQ);
- return IRQ_WAKE_THREAD;
+ dsp_irq = true;
}
ext_intr_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, desc->ext_intr_stat);
- if (ext_intr_stat & ACP_ERROR_IRQ_MASK) {
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP_ERROR_IRQ_MASK);
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->acp_error_stat, 0);
+
+ if (adata->sdw) {
+ for (instance = 0; instance < ACP7X_SDW_MAX_MANAGER_COUNT; instance++) {
+ if (ext_intr_stat & acp7x_sof_sdw_ext_stat[instance]) {
+ sof_acp7x_handle_sdw_manager_irq(sdev, adata, desc, instance,
+ acp7x_sof_sdw_ext_stat[instance]);
+ irq_flag = 1;
+ }
+ }
+ }
+
+ if (adata->sdw)
+ wake_irq_flag = sof_amd_check_and_handle_acp7x_sdw_wake_irq(sdev);
+
+ if (ext_intr_stat & ACP7X_ERROR_IRQ) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, desc->ext_intr_stat, ACP7X_ERROR_IRQ);
+ sof_amd_acp7x_clear_sdw_err_regs(sdev);
irq_flag = 1;
}
- if (irq_flag)
+ if (dsp_irq)
+ return IRQ_WAKE_THREAD;
+
+ if (irq_flag || wake_irq_flag)
return IRQ_HANDLED;
return IRQ_NONE;
@@ -728,6 +903,8 @@ 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;
+ u32 sdw_wake_en, intr_mask;
+ unsigned int i;
int ret;
/* power on */
@@ -771,6 +948,20 @@ static int acp_init(struct snd_sof_dev *sdev)
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_DSP0_IDMA_ERROR_MASK,
ACP7X_IDMA_ERROR_MASK);
+ /*
+ * Enable host-wake interrupt per manager based on SW_WAKE_EN:
+ * SW_WAKE_EN bit i enables ACP7X_SW_HOST_WAKE_MASK << i in INTR_CNTL1.
+ */
+ sdw_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_WAKE_EN);
+ intr_mask = 0;
+ for (i = 0; i < ACP7X_SDW_MAX_MANAGER_COUNT; i++) {
+ if (sdw_wake_en & BIT(i))
+ intr_mask |= ACP7X_SW_HOST_WAKE_MASK << i;
+ }
+ if (intr_mask)
+ snd_sof_dsp_update_bits(sdev, ACP_DSP_BAR,
+ ACP7X_EXTERNAL_INTR_CNTL1,
+ intr_mask, intr_mask);
break;
}
return 0;
@@ -779,15 +970,30 @@ static int acp_init(struct snd_sof_dev *sdev)
static bool check_acp_sdw_enable_status(struct snd_sof_dev *sdev)
{
struct acp_dev_data *acp_data;
- u32 sdw0_en, sdw1_en;
+ u32 sdw0_en, sdw1_en, sdw2_en, sdw3_en;
acp_data = sdev->pdata->hw_pdata;
if (!acp_data->sdw)
return false;
- sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW0_EN);
- sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW1_EN);
- acp_data->sdw_en_stat = sdw0_en || sdw1_en;
+ switch (acp_data->pci_rev) {
+ case ACP63_PCI_ID:
+ case ACP70_PCI_ID:
+ case ACP71_PCI_ID:
+ case ACP72_PCI_ID:
+ sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW0_EN);
+ sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP_SW1_EN);
+ acp_data->sdw_en_stat = sdw0_en || sdw1_en;
+ break;
+ case ACP7B_PCI_ID:
+ case ACP7F_PCI_ID:
+ sdw0_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_EN + (0 * 0x2000));
+ sdw1_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_EN + (1 * 0x2000));
+ sdw2_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_EN + (2 * 0x2000));
+ sdw3_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_EN + (3 * 0x2000));
+ acp_data->sdw_en_stat = sdw0_en || sdw1_en || sdw2_en || sdw3_en;
+ break;
+ }
return acp_data->sdw_en_stat;
}
@@ -853,6 +1059,31 @@ int amd_sof_acp_resume(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS(amd_sof_acp_resume, "SND_SOC_SOF_AMD_COMMON");
+static void acp_sof_scan_pdm_devices(struct snd_sof_dev *sdev,
+ struct acpi_device *pdm_dev)
+{
+ struct acp_dev_data *acp_data = sdev->pdata->hw_pdata;
+ struct fwnode_handle *fwnode, *child;
+ u32 ep_port_val;
+
+ fwnode = acpi_fwnode_handle(pdm_dev);
+ child = fwnode_get_next_child_node(fwnode, NULL);
+ if (!child)
+ return;
+
+ if (!fwnode_property_read_u32(child, "acp-audio-ep-port", &ep_port_val)) {
+ if (ep_port_val == ACP_DEV_PORT_PDM)
+ acp_data->pdm_sel = ACP7X_PDM_DMIC0;
+ else if (ep_port_val == ACP_DEV_PORT_PDM2)
+ acp_data->pdm_sel = ACP7X_PDM_DMIC1;
+ else
+ dev_warn(sdev->dev,
+ "acp-audio-ep-port: unrecognized value %u\n",
+ ep_port_val);
+ }
+ fwnode_handle_put(child);
+}
+
#if IS_ENABLED(CONFIG_SND_SOC_SOF_AMD_SOUNDWIRE)
static int acp_sof_scan_sdw_devices(struct snd_sof_dev *sdev, u64 addr)
{
@@ -1038,6 +1269,8 @@ skip_soundwire:
free_ipc_irq:
free_irq(sdev->ipc_irq, sdev);
+ if (adata->sdw)
+ amd_sof_sdw_exit(sdev);
unregister_dev:
platform_device_unregister(adata->dmic_dev);
return ret;
@@ -1048,12 +1281,12 @@ void amd_sof_acp_remove(struct snd_sof_dev *sdev)
{
struct acp_dev_data *adata = sdev->pdata->hw_pdata;
- if (adata->sdw)
- amd_sof_sdw_exit(sdev);
-
if (sdev->ipc_irq)
free_irq(sdev->ipc_irq, sdev);
+ if (adata->sdw)
+ amd_sof_sdw_exit(sdev);
+
if (adata->dmic_dev)
platform_device_unregister(adata->dmic_dev);
@@ -1068,6 +1301,7 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev)
const struct sof_amd_acp_desc *chip;
const union acpi_object *obj;
struct acpi_device *adev;
+ struct acpi_device *pdm_dev;
unsigned int addr;
unsigned int irqflags;
int ret;
@@ -1120,7 +1354,27 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev)
goto unregister_dev;
}
+ /* scan SoundWire capabilities exposed by DSDT */
+ ret = acp_sof_scan_sdw_devices(sdev, chip->sdw_acpi_dev_addr);
+ if (ret < 0) {
+ dev_dbg(sdev->dev, "skipping SoundWire, not detected with ACPI scan\n");
+ goto skip_soundwire;
+ }
+ ret = amd_sof_sdw_probe(sdev);
+ if (ret < 0) {
+ dev_err(sdev->dev, "error: SoundWire probe error\n");
+ goto free_ipc_irq;
+ }
+ if (adata->info.link_mask)
+ adata->is_sdw_dev = true;
+
+skip_soundwire:
if (adev) {
+ /* DMIC ACPI child address is 2 on ACP7x platforms */
+ pdm_dev = acpi_find_child_device(adev, ACP7X_DMIC_ADDR, 0);
+ if (pdm_dev)
+ acp_sof_scan_pdm_devices(sdev, pdm_dev);
+
if (!acpi_dev_get_property(adev, "acp-sof-signed-firmware-image",
ACPI_TYPE_INTEGER, &obj))
adata->acp_sof_signed_firmware_image = obj->integer.value;
@@ -1158,6 +1412,8 @@ int amd_sof_acp7x_probe(struct snd_sof_dev *sdev)
free_ipc_irq:
free_irq(sdev->ipc_irq, sdev);
+ if (adata->sdw)
+ amd_sof_sdw_exit(sdev);
unregister_dev:
platform_device_unregister(adata->dmic_dev);
return ret;
@@ -1171,6 +1427,9 @@ void amd_sof_acp7x_remove(struct snd_sof_dev *sdev)
if (sdev->ipc_irq)
free_irq(sdev->ipc_irq, sdev);
+ if (adata->sdw)
+ amd_sof_sdw_exit(sdev);
+
if (adata->dmic_dev)
platform_device_unregister(adata->dmic_dev);
@@ -1178,6 +1437,44 @@ void amd_sof_acp7x_remove(struct snd_sof_dev *sdev)
}
EXPORT_SYMBOL_NS(amd_sof_acp7x_remove, "SND_SOC_SOF_AMD_COMMON");
+static void handle_amd_sof_acp7x_sdw_pme_event(struct snd_sof_dev *sdev)
+{
+ struct acp_dev_data *adata;
+ struct amd_sdw_manager *amd_manager;
+ u32 sdw_pme_stat;
+ u32 sdw_wake_en;
+ u32 pme_reg;
+ u32 wake_mask;
+ unsigned int instance;
+
+ adata = sdev->pdata->hw_pdata;
+ if (!adata->sdw)
+ return;
+
+ for (instance = 0; instance < ACP7X_SDW_MAX_MANAGER_COUNT; instance++) {
+ pme_reg = ACP7X_SW_PME_STS + (instance * 4);
+ wake_mask = ACP7X_SW_WAKE_EN_MASK << instance;
+
+ sdw_pme_stat = snd_sof_dsp_read(sdev, ACP_DSP_BAR, pme_reg);
+ if (!sdw_pme_stat)
+ continue;
+
+ mutex_lock(&adata->acp_lock);
+ sdw_wake_en = snd_sof_dsp_read(sdev, ACP_DSP_BAR, ACP7X_SW_WAKE_EN);
+ sdw_wake_en &= ~wake_mask;
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_SW_WAKE_EN, sdw_wake_en);
+ mutex_unlock(&adata->acp_lock);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, pme_reg, sdw_pme_stat);
+
+ if (!adata->sdw->pdev[instance])
+ continue;
+
+ amd_manager = dev_get_drvdata(&adata->sdw->pdev[instance]->dev);
+ if (amd_manager)
+ pm_request_resume(amd_manager->dev);
+ }
+}
+
int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state)
{
struct acp_dev_data *acp_data;
@@ -1186,6 +1483,11 @@ int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state)
acp_data = sdev->pdata->hw_pdata;
+ if (acp_data->is_sdw_dev && check_acp_sdw_enable_status(sdev)) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1);
+ return acp_dsp_reset(sdev);
+ }
+
ret = acp_reset(sdev);
if (ret) {
dev_err(sdev->dev, "ACP Reset failed\n");
@@ -1197,7 +1499,8 @@ int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state)
enable = true;
break;
default:
- break;
+ dev_err(sdev->dev, "Unexpected PCI revision: 0x%x\n", acp_data->pci_rev);
+ return -EINVAL;
}
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP_CONTROL, enable);
snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 1);
@@ -1206,13 +1509,25 @@ int amd_sof_acp7x_suspend(struct snd_sof_dev *sdev, u32 target_state)
}
EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend, "SND_SOC_SOF_AMD_COMMON");
-int amd_sof_acp7x_resume(struct snd_sof_dev *sdev)
+int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev)
+{
+ return amd_sof_acp7x_suspend(sdev, 0);
+}
+EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON");
+
+int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev)
{
struct acp_dev_data *acp_data;
int ret;
acp_data = sdev->pdata->hw_pdata;
+ if (acp_data->sdw_en_stat) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1);
+ return acp_dsp_reset(sdev);
+ }
+
ret = acp_init(sdev);
if (ret) {
dev_err(sdev->dev, "ACP Init failed\n");
@@ -1224,30 +1539,40 @@ int amd_sof_acp7x_resume(struct snd_sof_dev *sdev)
return ret;
}
- switch (acp_data->pci_rev) {
- case ACP7B_PCI_ID:
- case ACP7F_PCI_ID:
- snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1);
- break;
- default:
- break;
- }
+ if (acp_data->is_sdw_dev)
+ handle_amd_sof_acp7x_sdw_pme_event(sdev);
return 0;
}
-EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON");
+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON");
-int amd_sof_acp7x_suspend_runtime(struct snd_sof_dev *sdev)
+int amd_sof_acp7x_resume(struct snd_sof_dev *sdev)
{
- return amd_sof_acp7x_suspend(sdev, 0);
-}
-EXPORT_SYMBOL_NS(amd_sof_acp7x_suspend_runtime, "SND_SOC_SOF_AMD_COMMON");
+ struct acp_dev_data *acp_data;
+ int ret;
-int amd_sof_acp7x_resume_runtime(struct snd_sof_dev *sdev)
-{
- return amd_sof_acp7x_resume(sdev);
+ acp_data = sdev->pdata->hw_pdata;
+
+ if (acp_data->sdw_en_stat) {
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_ZSC_DSP_CTRL, 0);
+ snd_sof_dsp_write(sdev, ACP_DSP_BAR, ACP7X_PME_EN, 1);
+ return acp_dsp_reset(sdev);
+ }
+
+ ret = acp_init(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "ACP Init failed\n");
+ return ret;
+ }
+ ret = acp_memory_init(sdev);
+ if (ret) {
+ dev_err(sdev->dev, "ACP Memory init failed\n");
+ return ret;
+ }
+
+ return 0;
}
-EXPORT_SYMBOL_NS(amd_sof_acp7x_resume_runtime, "SND_SOC_SOF_AMD_COMMON");
+EXPORT_SYMBOL_NS(amd_sof_acp7x_resume, "SND_SOC_SOF_AMD_COMMON");
MODULE_LICENSE("Dual BSD/GPL");
MODULE_DESCRIPTION("AMD ACP sof driver");
diff --git a/sound/soc/sof/amd/acp.h b/sound/soc/sof/amd/acp.h
index 16d66b2eaa70..1bc6b572448d 100644
--- a/sound/soc/sof/amd/acp.h
+++ b/sound/soc/sof/amd/acp.h
@@ -125,6 +125,17 @@
#define ACP_SRAM_PAGE_COUNT 128
#define ACP6X_SDW_MAX_MANAGER_COUNT 2
#define ACP70_SDW_MAX_MANAGER_COUNT ACP6X_SDW_MAX_MANAGER_COUNT
+/* ACPI _DSD acp-audio-ep-port values for PDM controller selection */
+#define ACP_DEV_PORT_PDM 4
+#define ACP_DEV_PORT_PDM2 5
+#define SDW_ACPI_ADDR_ACP7X SDW_ACPI_ADDR_ACP63
+#define ACP7X_SDW_MAX_MANAGER_COUNT 4
+
+/* ACPI child device address for the ACP7x PDM/DMIC device */
+#define ACP7X_DMIC_ADDR 2
+/* ACP7X PDM controller selection values for acp_dev_data.pdm_sel; 0 = not set */
+#define ACP7X_PDM_DMIC0 1
+#define ACP7X_PDM_DMIC1 2
#define ACP_DSP_MSG_SET 1
#define ACP_DSP_ACK_SET 1
@@ -279,6 +290,11 @@ struct acp_dev_data {
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;
+ /* per-manager wake event flags; indexed by SoundWire manager instance (0-3) */
+ bool acp7x_sdw_wake_event[ACP7X_SDW_MAX_MANAGER_COUNT];
+ /* PDM controller index selected from ACPI acp-audio-ep-port; passed to machine driver */
+ unsigned int pdm_sel;
+ bool is_sdw_dev;
unsigned int pci_rev;
int acp_sof_signed_firmware_image;
};
diff --git a/sound/soc/sof/amd/acp7x.h b/sound/soc/sof/amd/acp7x.h
new file mode 100644
index 000000000000..5151d8757c12
--- /dev/null
+++ b/sound/soc/sof/amd/acp7x.h
@@ -0,0 +1,37 @@
+/* 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) 2026 Advanced Micro Devices, Inc. All rights reserved.
+ *
+ * Author: Vijendar Mukunda <Vijendar.Mukunda@amd.com>
+ */
+
+#ifndef __SOF_AMD_ACP7X_H
+#define __SOF_AMD_ACP7X_H
+
+/* Return values for sof_amd_check_and_handle_acp7x_sdw_wake_irq() */
+#define WAKE_IRQ_HANDLED 1
+#define WAKE_IRQ_NONE 0
+
+/* ACP7X SoundWire IO structures */
+
+struct sof_amd_acp7x_sdw_err_regs {
+ u32 err_stat_mask;
+ u32 fifo_err_reason;
+ u32 err_reason1;
+ u32 err_reason2;
+};
+
+struct acp7x_sdw_wake_src {
+ u32 host_stat_mask;
+ u32 pme_sts_reg;
+ u32 wake_en_mask;
+ u8 instance;
+};
+
+extern const struct sof_amd_acp7x_sdw_err_regs acp7x_sdw_err_regs[ACP7X_SDW_MAX_MANAGER_COUNT];
+extern const struct acp7x_sdw_wake_src acp7x_sdw_wake_sources[];
+
+#endif /* __SOF_AMD_ACP7X_H */
diff --git a/sound/soc/sof/amd/pci-acp7x.c b/sound/soc/sof/amd/pci-acp7x.c
index 532e15313795..0594c1f3deb6 100644
--- a/sound/soc/sof/amd/pci-acp7x.c
+++ b/sound/soc/sof/amd/pci-acp7x.c
@@ -40,6 +40,8 @@ static const struct sof_amd_acp_desc acp7x_chip_info = {
.fusion_dsp_offset = ACP7X_DSP_FUSION_RUNSTALL,
.probe_reg_offset = ACP7X_FUTURE_REG_ACLK_0,
.reg_start_addr = ACP7X_REG_START,
+ .sdw_max_link_count = ACP7X_SDW_MAX_MANAGER_COUNT,
+ .sdw_acpi_dev_addr = SDW_ACPI_ADDR_ACP7X,
.reg_end_addr = ACP7X_REG_END,
};
diff --git a/sound/soc/sof/imx/imx-common.c b/sound/soc/sof/imx/imx-common.c
index 7a03c8cc5dd4..436fe49246ba 100644
--- a/sound/soc/sof/imx/imx-common.c
+++ b/sound/soc/sof/imx/imx-common.c
@@ -34,6 +34,7 @@ void imx8_get_registers(struct snd_sof_dev *sdev,
/* first read registers */
sof_mailbox_read(sdev, offset, xoops, sizeof(*xoops));
+ xoops->plat_hdr.numaregs = 0;
/* then get panic info */
if (xoops->arch_hdr.totalsize > EXCEPT_MAX_HDR_SIZE) {
diff --git a/sound/soc/sof/imx/imx8.c b/sound/soc/sof/imx/imx8.c
index 7e9eab2e3034..0820724dbb34 100644
--- a/sound/soc/sof/imx/imx8.c
+++ b/sound/soc/sof/imx/imx8.c
@@ -36,19 +36,23 @@
struct imx8m_chip_data {
void __iomem *dap;
- struct regmap *regmap;
struct reset_control *run_stall;
};
static int imx8_shutdown(struct snd_sof_dev *sdev)
{
+ int ret;
/*
* 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);
+ ret = imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, false,
+ RESET_VECTOR_VADDR);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Error stalling DSP core %d\n", ret);
+ return ret;
+ }
return 0;
}
@@ -89,8 +93,12 @@ static int imx8x_run(struct snd_sof_dev *sdev)
return ret;
}
- imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true,
- RESET_VECTOR_VADDR);
+ ret = imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true,
+ RESET_VECTOR_VADDR);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Error starting DSP %d\n", ret);
+ return ret;
+ }
return 0;
}
@@ -106,8 +114,12 @@ static int imx8_run(struct snd_sof_dev *sdev)
return ret;
}
- imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true,
- RESET_VECTOR_VADDR);
+ ret = imx_sc_pm_cpu_start(get_chip_pdata(sdev), IMX_SC_R_DSP, true,
+ RESET_VECTOR_VADDR);
+ if (ret < 0) {
+ dev_err(sdev->dev, "Error starting DSP %d\n", ret);
+ return ret;
+ }
return 0;
}
diff --git a/sound/soc/sof/intel/apl.c b/sound/soc/sof/intel/apl.c
index b0072601181e..753da09301fa 100644
--- a/sound/soc/sof/intel/apl.c
+++ b/sound/soc/sof/intel/apl.c
@@ -59,6 +59,7 @@ int sof_apl_ops_init(struct snd_sof_dev *sdev)
return -ENOMEM;
ipc4_data = sdev->private;
+ INIT_LIST_HEAD(&ipc4_data->nhlt_list);
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5;
diff --git a/sound/soc/sof/intel/cnl.c b/sound/soc/sof/intel/cnl.c
index 660c1475e5a4..ec33cd18e669 100644
--- a/sound/soc/sof/intel/cnl.c
+++ b/sound/soc/sof/intel/cnl.c
@@ -406,6 +406,7 @@ int sof_cnl_ops_init(struct snd_sof_dev *sdev)
return -ENOMEM;
ipc4_data = sdev->private;
+ INIT_LIST_HEAD(&ipc4_data->nhlt_list);
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_8;
diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
index bb44d4f8a4da..c8729b6d82bd 100644
--- a/sound/soc/sof/intel/hda-dai.c
+++ b/sound/soc/sof/intel/hda-dai.c
@@ -760,8 +760,20 @@ void hda_set_dai_drv_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *ops)
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4 && !hda_use_tplg_nhlt) {
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct snd_ipc4_nhlt *entry;
- ipc4_data->nhlt = intel_nhlt_init(sdev->dev);
+ entry = devm_kzalloc(sdev->dev, sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ return;
+
+ entry->nhlt = intel_nhlt_init(sdev->dev);
+ if (!entry->nhlt) {
+ devm_kfree(sdev->dev, entry);
+ return;
+ }
+
+ entry->from_acpi = true;
+ list_add(&entry->list, &ipc4_data->nhlt_list);
}
}
EXPORT_SYMBOL_NS(hda_set_dai_drv_ops, "SND_SOC_SOF_INTEL_HDA_COMMON");
@@ -770,9 +782,17 @@ void hda_ops_free(struct snd_sof_dev *sdev)
{
if (sdev->pdata->ipc_type == SOF_IPC_TYPE_4) {
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
+ struct snd_ipc4_nhlt *entry;
+ struct snd_ipc4_nhlt *tmp;
- if (!hda_use_tplg_nhlt)
- intel_nhlt_free(ipc4_data->nhlt);
+ if (!hda_use_tplg_nhlt) {
+ list_for_each_entry_safe(entry, tmp, &ipc4_data->nhlt_list, list) {
+ if (entry->from_acpi && entry->nhlt)
+ intel_nhlt_free(entry->nhlt);
+
+ list_del(&entry->list);
+ }
+ }
kfree(sdev->private);
sdev->private = NULL;
diff --git a/sound/soc/sof/intel/icl.c b/sound/soc/sof/intel/icl.c
index 549bb4ca73e1..196caba14be0 100644
--- a/sound/soc/sof/intel/icl.c
+++ b/sound/soc/sof/intel/icl.c
@@ -131,6 +131,7 @@ int sof_icl_ops_init(struct snd_sof_dev *sdev)
return -ENOMEM;
ipc4_data = sdev->private;
+ INIT_LIST_HEAD(&ipc4_data->nhlt_list);
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
diff --git a/sound/soc/sof/intel/mtl.c b/sound/soc/sof/intel/mtl.c
index 3d67d6777f1b..2506506f813a 100644
--- a/sound/soc/sof/intel/mtl.c
+++ b/sound/soc/sof/intel/mtl.c
@@ -762,6 +762,7 @@ int sof_mtl_set_ops(struct snd_sof_dev *sdev, struct snd_sof_dsp_ops *dsp_ops)
return -ENOMEM;
ipc4_data = sdev->private;
+ INIT_LIST_HEAD(&ipc4_data->nhlt_list);
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
diff --git a/sound/soc/sof/intel/skl.c b/sound/soc/sof/intel/skl.c
index 90519ebd3168..f831143a7afc 100644
--- a/sound/soc/sof/intel/skl.c
+++ b/sound/soc/sof/intel/skl.c
@@ -67,6 +67,7 @@ int sof_skl_ops_init(struct snd_sof_dev *sdev)
return -ENOMEM;
ipc4_data = sdev->private;
+ INIT_LIST_HEAD(&ipc4_data->nhlt_list);
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET_CAVS_1_5;
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_1_5;
diff --git a/sound/soc/sof/intel/tgl.c b/sound/soc/sof/intel/tgl.c
index 7936361e2e39..09a8a4a2b017 100644
--- a/sound/soc/sof/intel/tgl.c
+++ b/sound/soc/sof/intel/tgl.c
@@ -95,6 +95,7 @@ int sof_tgl_ops_init(struct snd_sof_dev *sdev)
return -ENOMEM;
ipc4_data = sdev->private;
+ INIT_LIST_HEAD(&ipc4_data->nhlt_list);
ipc4_data->manifest_fw_hdr_offset = SOF_MAN4_FW_HDR_OFFSET;
ipc4_data->mtrace_type = SOF_IPC4_MTRACE_INTEL_CAVS_2;
diff --git a/sound/soc/sof/ipc4-priv.h b/sound/soc/sof/ipc4-priv.h
index a8cdf9bc750b..61dd48282dd3 100644
--- a/sound/soc/sof/ipc4-priv.h
+++ b/sound/soc/sof/ipc4-priv.h
@@ -58,13 +58,18 @@ struct sof_ipc4_fw_library {
struct sof_ipc4_fw_module *modules;
};
+struct snd_ipc4_nhlt {
+ struct list_head list;
+ void *nhlt;
+ bool from_acpi;
+};
+
/**
* struct sof_ipc4_fw_data - IPC4-specific data
* @manifest_fw_hdr_offset: FW header offset in the manifest
* @fw_lib_xa: XArray for firmware libraries, including basefw (ID = 0)
* Used to store the FW libraries and to manage the unique IDs of the
* libraries.
- * @nhlt: NHLT table either from the BIOS or the topology manifest
* @mtrace_type: mtrace type supported on the booted platform
* @mtrace_log_bytes: log bytes as reported by the firmware via fw_config reply
* @num_playback_streams: max number of playback DMAs, needed for CHAIN_DMA offset
@@ -74,6 +79,7 @@ struct sof_ipc4_fw_library {
* base firmware
* @fw_context_save: Firmware supports full context save and restore
* @libraries_restored: The libraries have been retained during firmware boot
+ * @nhlt_list: The NHLT tables from the BIOS and the topology manifest
*
* @load_library: Callback function for platform dependent library loading
* @pipeline_state_mutex: Mutex to protect pipeline triggers, ref counts, states and deletion
@@ -81,7 +87,6 @@ struct sof_ipc4_fw_library {
struct sof_ipc4_fw_data {
u32 manifest_fw_hdr_offset;
struct xarray fw_lib_xa;
- void *nhlt;
enum sof_ipc4_mtrace_type mtrace_type;
u32 mtrace_log_bytes;
int num_playback_streams;
@@ -90,6 +95,7 @@ struct sof_ipc4_fw_data {
u32 max_libs_count;
bool fw_context_save;
bool libraries_restored;
+ struct list_head nhlt_list;
int (*load_library)(struct snd_sof_dev *sdev,
struct sof_ipc4_fw_library *fw_lib, bool reload);
diff --git a/sound/soc/sof/ipc4-topology.c b/sound/soc/sof/ipc4-topology.c
index 24a1175dd690..f6617dcbf789 100644
--- a/sound/soc/sof/ipc4-topology.c
+++ b/sound/soc/sof/ipc4-topology.c
@@ -1844,12 +1844,13 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai
u32 linktype, u8 dir, u32 **dst, u32 *len)
{
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
- struct nhlt_specific_cfg *cfg;
+ struct nhlt_specific_cfg *cfg = NULL;
+ struct snd_ipc4_nhlt *entry = NULL;
int sample_rate, channel_count;
bool format_change = false;
int bit_depth, ret;
u32 nhlt_type;
- int dev_type = 0;
+ int dev_type = -EINVAL;
/* convert to NHLT type */
switch (linktype) {
@@ -1880,10 +1881,17 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai
* Query the type for the port and then pass that information back
* to the blob lookup function.
*/
- dev_type = intel_nhlt_ssp_device_type(sdev->dev, ipc4_data->nhlt,
- dai_index);
- if (dev_type < 0)
+ list_for_each_entry(entry, &ipc4_data->nhlt_list, list) {
+ dev_type = intel_nhlt_ssp_device_type(sdev->dev, entry->nhlt,
+ dai_index);
+ if (dev_type >= 0)
+ break;
+ }
+ if (dev_type < 0) {
+ dev_err(sdev->dev, "%s: No match for SSP%d in NHLT table\n",
+ __func__, dai_index);
return dev_type;
+ }
break;
default:
return 0;
@@ -1893,9 +1901,14 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai
dai_index, nhlt_type, dir, dev_type);
/* find NHLT blob with matching params */
- cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt, dai_index, nhlt_type,
- bit_depth, bit_depth, channel_count, sample_rate,
- dir, dev_type);
+ list_for_each_entry(entry, &ipc4_data->nhlt_list, list) {
+ cfg = intel_nhlt_get_endpoint_blob(sdev->dev, entry->nhlt, dai_index,
+ nhlt_type, bit_depth, bit_depth,
+ channel_count, sample_rate, dir,
+ dev_type);
+ if (cfg)
+ break;
+ }
if (!cfg) {
bool get_new_blob = false;
@@ -1929,13 +1942,15 @@ snd_sof_get_nhlt_endpoint_data(struct snd_sof_dev *sdev, struct snd_sof_dai *dai
}
if (get_new_blob) {
- cfg = intel_nhlt_get_endpoint_blob(sdev->dev, ipc4_data->nhlt,
- dai_index, nhlt_type,
- bit_depth, bit_depth,
- channel_count, sample_rate,
- dir, dev_type);
- if (cfg)
- goto out;
+ list_for_each_entry(entry, &ipc4_data->nhlt_list, list) {
+ cfg = intel_nhlt_get_endpoint_blob(sdev->dev, entry->nhlt,
+ dai_index, nhlt_type,
+ bit_depth, bit_depth,
+ channel_count, sample_rate,
+ dir, dev_type);
+ if (cfg)
+ goto out;
+ }
}
dev_err(sdev->dev,
@@ -4069,6 +4084,7 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
struct snd_sof_dev *sdev = snd_soc_component_get_drvdata(scomp);
struct sof_ipc4_fw_data *ipc4_data = sdev->private;
struct sof_manifest_tlv *manifest_tlv;
+ struct snd_ipc4_nhlt *tplg_nhlt;
struct sof_manifest *manifest;
u32 size = le32_to_cpu(man->priv.size);
u8 *man_ptr = man->priv.data;
@@ -4104,13 +4120,20 @@ static int sof_ipc4_parse_manifest(struct snd_soc_component *scomp, int index,
switch (le32_to_cpu(manifest_tlv->type)) {
case SOF_MANIFEST_DATA_TYPE_NHLT:
- /* no NHLT in BIOS, so use the one from topology manifest */
- if (ipc4_data->nhlt)
- break;
- ipc4_data->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data,
+ /* Get the nhlt from topology manifest */
+ tplg_nhlt = devm_kzalloc(sdev->dev, sizeof(*tplg_nhlt), GFP_KERNEL);
+ if (!tplg_nhlt)
+ return -ENOMEM;
+
+ tplg_nhlt->nhlt = devm_kmemdup(sdev->dev, manifest_tlv->data,
le32_to_cpu(manifest_tlv->size), GFP_KERNEL);
- if (!ipc4_data->nhlt)
+ if (!tplg_nhlt->nhlt)
return -ENOMEM;
+
+ tplg_nhlt->from_acpi = false;
+
+ list_add(&tplg_nhlt->list, &ipc4_data->nhlt_list);
+
break;
default:
dev_warn(scomp->dev, "Skipping unknown manifest data type %d\n",
diff --git a/sound/soc/sof/mediatek/mt8186/mt8186.c b/sound/soc/sof/mediatek/mt8186/mt8186.c
index c1bea967737d..a5ccee87e684 100644
--- a/sound/soc/sof/mediatek/mt8186/mt8186.c
+++ b/sound/soc/sof/mediatek/mt8186/mt8186.c
@@ -49,7 +49,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
struct mtk_adsp_chip_info *adsp = data;
int ret;
- ret = of_reserved_mem_device_init(dev);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret) {
dev_err(dev, "of_reserved_mem_device_init failed\n");
return ret;
diff --git a/sound/soc/sof/mediatek/mt8195/mt8195.c b/sound/soc/sof/mediatek/mt8195/mt8195.c
index 4d6e9300a9c0..e4b6b6c18e02 100644
--- a/sound/soc/sof/mediatek/mt8195/mt8195.c
+++ b/sound/soc/sof/mediatek/mt8195/mt8195.c
@@ -49,7 +49,7 @@ static int platform_parse_resource(struct platform_device *pdev, void *data)
struct mtk_adsp_chip_info *adsp = data;
int ret;
- ret = of_reserved_mem_device_init(dev);
+ ret = devm_of_reserved_mem_device_init(dev);
if (ret) {
dev_err(dev, "of_reserved_mem_device_init failed\n");
return ret;
diff --git a/sound/soc/sof/topology.c b/sound/soc/sof/topology.c
index 8338133899ab..145bad472118 100644
--- a/sound/soc/sof/topology.c
+++ b/sound/soc/sof/topology.c
@@ -2517,7 +2517,8 @@ int snd_sof_load_topology(struct snd_soc_component *scomp, const char *file)
int i;
const char **tplg_files __free(kfree) =
- kcalloc(scomp->card->num_links, sizeof(char *), GFP_KERNEL);
+ kcalloc(scomp->card->num_links, sizeof(*tplg_files),
+ GFP_KERNEL);
if (!tplg_files)
return -ENOMEM;
diff --git a/sound/soc/sprd/sprd-pcm-compress.c b/sound/soc/sprd/sprd-pcm-compress.c
index e5249924b54d..f790b959cd4e 100644
--- a/sound/soc/sprd/sprd-pcm-compress.c
+++ b/sound/soc/sprd/sprd-pcm-compress.c
@@ -145,6 +145,7 @@ static int sprd_platform_compr_dma_config(struct snd_soc_component *component,
enum dma_slave_buswidth bus_width;
int period, period_cnt, sg_num = 2;
dma_addr_t src_addr, dst_addr;
+ struct dma_chan *chan;
unsigned long flags;
int ret, j;
@@ -153,12 +154,12 @@ static int sprd_platform_compr_dma_config(struct snd_soc_component *component,
return -EINVAL;
}
- dma->chan = dma_request_slave_channel(dev,
- dma_params->chan_name[channel]);
- if (!dma->chan) {
+ chan = dma_request_chan(dev, dma_params->chan_name[channel]);
+ if (IS_ERR(chan)) {
dev_err(dev, "failed to request dma channel\n");
return -ENODEV;
}
+ dma->chan = chan;
sgt = sg = kzalloc_objs(*sg, sg_num);
if (!sg) {
diff --git a/sound/soc/sprd/sprd-pcm-dma.c b/sound/soc/sprd/sprd-pcm-dma.c
index f509a4601de2..bbd7516f8195 100644
--- a/sound/soc/sprd/sprd-pcm-dma.c
+++ b/sound/soc/sprd/sprd-pcm-dma.c
@@ -170,15 +170,16 @@ static int sprd_pcm_request_dma_channel(struct snd_soc_component *component,
for (i = 0; i < channels; i++) {
struct sprd_pcm_dma_data *data = &dma_private->data[i];
+ struct dma_chan *chan;
- data->chan = dma_request_slave_channel(dev,
- dma_params->chan_name[i]);
- if (!data->chan) {
+ chan = dma_request_chan(dev, dma_params->chan_name[i]);
+ if (IS_ERR(chan)) {
dev_err(dev, "failed to request dma channel:%s\n",
dma_params->chan_name[i]);
sprd_pcm_release_dma_channel(substream);
return -ENODEV;
}
+ data->chan = chan;
}
return 0;
@@ -459,10 +460,9 @@ static const struct snd_soc_component_driver sprd_soc_component = {
static int sprd_soc_platform_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
int ret;
- ret = of_reserved_mem_device_init_by_idx(&pdev->dev, np, 0);
+ ret = devm_of_reserved_mem_device_init(&pdev->dev);
if (ret)
dev_warn(&pdev->dev,
"no reserved DMA memory for audio platform device\n");
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index e4b9799ad9b2..844ddcfc421a 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -1028,16 +1028,27 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
return 0;
}
+static const u64 uni_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops uni_player_dai_ops = {
- .startup = uni_player_startup,
- .shutdown = uni_player_shutdown,
- .prepare = uni_player_prepare,
- .probe = sti_uniperiph_dai_probe,
- .trigger = uni_player_trigger,
- .hw_params = sti_uniperiph_dai_hw_params,
- .set_fmt = sti_uniperiph_dai_set_fmt,
- .set_sysclk = uni_player_set_sysclk,
- .set_tdm_slot = sti_uniperiph_set_tdm_slot
+ .startup = uni_player_startup,
+ .shutdown = uni_player_shutdown,
+ .prepare = uni_player_prepare,
+ .probe = sti_uniperiph_dai_probe,
+ .trigger = uni_player_trigger,
+ .hw_params = sti_uniperiph_dai_hw_params,
+ .set_fmt = sti_uniperiph_dai_set_fmt,
+ .set_sysclk = uni_player_set_sysclk,
+ .set_tdm_slot = sti_uniperiph_set_tdm_slot,
+ .auto_selectable_formats = &uni_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
int uni_player_init(struct platform_device *pdev,
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c
index 5347f9620f25..acdd5206ff9c 100644
--- a/sound/soc/sti/uniperif_reader.c
+++ b/sound/soc/sti/uniperif_reader.c
@@ -391,15 +391,26 @@ static void uni_reader_shutdown(struct snd_pcm_substream *substream,
reader->substream = NULL;
}
+static const u64 uni_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops uni_reader_dai_ops = {
- .startup = uni_reader_startup,
- .shutdown = uni_reader_shutdown,
- .prepare = uni_reader_prepare,
- .probe = sti_uniperiph_dai_probe,
- .trigger = uni_reader_trigger,
- .hw_params = sti_uniperiph_dai_hw_params,
- .set_fmt = sti_uniperiph_dai_set_fmt,
- .set_tdm_slot = sti_uniperiph_set_tdm_slot
+ .startup = uni_reader_startup,
+ .shutdown = uni_reader_shutdown,
+ .prepare = uni_reader_prepare,
+ .probe = sti_uniperiph_dai_probe,
+ .trigger = uni_reader_trigger,
+ .hw_params = sti_uniperiph_dai_hw_params,
+ .set_fmt = sti_uniperiph_dai_set_fmt,
+ .set_tdm_slot = sti_uniperiph_set_tdm_slot,
+ .auto_selectable_formats = &uni_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
int uni_reader_init(struct platform_device *pdev,
diff --git a/sound/soc/stm/stm32_i2s.c b/sound/soc/stm/stm32_i2s.c
index 83b51893b37c..a59ad9442372 100644
--- a/sound/soc/stm/stm32_i2s.c
+++ b/sound/soc/stm/stm32_i2s.c
@@ -1089,6 +1089,16 @@ static const struct regmap_config stm32_h7_i2s_regmap_conf = {
.cache_type = REGCACHE_FLAT,
};
+static const u64 stm32_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = {
.probe = stm32_i2s_dai_probe,
.set_sysclk = stm32_i2s_set_sysclk,
@@ -1097,6 +1107,8 @@ static const struct snd_soc_dai_ops stm32_i2s_pcm_dai_ops = {
.hw_params = stm32_i2s_hw_params,
.trigger = stm32_i2s_trigger,
.shutdown = stm32_i2s_shutdown,
+ .auto_selectable_formats = &stm32_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_pcm_hardware stm32_i2s_pcm_hw = {
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
index 9a201bdb306f..d00377efe3f9 100644
--- a/sound/soc/stm/stm32_sai_sub.c
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -1358,6 +1358,17 @@ static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
return stm32_sai_sub_reg_up(sai, STM_SAI_CR1_REGX, cr1_mask, cr1);
}
+static const u64 stm32_sai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {
.probe = stm32_sai_dai_probe,
.set_sysclk = stm32_sai_set_sysclk,
@@ -1368,6 +1379,8 @@ static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {
.trigger = stm32_sai_trigger,
.shutdown = stm32_sai_shutdown,
.pcm_new = stm32_sai_pcm_new,
+ .auto_selectable_formats = &stm32_sai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops2 = {
@@ -1379,6 +1392,8 @@ static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops2 = {
.hw_params = stm32_sai_hw_params,
.trigger = stm32_sai_trigger,
.shutdown = stm32_sai_shutdown,
+ .auto_selectable_formats = &stm32_sai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static int stm32_sai_pcm_process_spdif(struct snd_pcm_substream *substream,
diff --git a/sound/soc/stm/stm32_spdifrx.c b/sound/soc/stm/stm32_spdifrx.c
index e0fef47a227e..0ece54740773 100644
--- a/sound/soc/stm/stm32_spdifrx.c
+++ b/sound/soc/stm/stm32_spdifrx.c
@@ -400,7 +400,7 @@ static int stm32_spdifrx_dma_ctrl_register(struct device *dev,
spdifrx->ctrl_chan = dma_request_chan(dev, "rx-ctrl");
if (IS_ERR(spdifrx->ctrl_chan))
return dev_err_probe(dev, PTR_ERR(spdifrx->ctrl_chan),
- "dma_request_slave_channel error\n");
+ "dma_request_chan error\n");
spdifrx->dmab = devm_kzalloc(dev, sizeof(struct snd_dma_buffer),
GFP_KERNEL);
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
index 716fa4d872ff..43e6ea2563fe 100644
--- a/sound/soc/sunxi/sun4i-i2s.c
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -1106,6 +1106,17 @@ static int sun4i_i2s_dai_startup(struct snd_pcm_substream *sub, struct snd_soc_d
i2s->variant->pcm_formats);
}
+static const u64 sun4i_i2s_selectable_format =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = {
.probe = sun4i_i2s_dai_probe,
.startup = sun4i_i2s_dai_startup,
@@ -1114,6 +1125,8 @@ static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = {
.set_sysclk = sun4i_i2s_set_sysclk,
.set_tdm_slot = sun4i_i2s_set_tdm_slot,
.trigger = sun4i_i2s_trigger,
+ .auto_selectable_formats = &sun4i_i2s_selectable_format,
+ .num_auto_selectable_formats = 1,
};
#define SUN4I_FORMATS_ALL (SNDRV_PCM_FMTBIT_S16_LE | \
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 6bd42da57ac0..2e4a205e4e56 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -688,12 +688,25 @@ done:
return 0;
}
+static const u64 sun8i_codec_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops sun8i_codec_dai_ops = {
.set_fmt = sun8i_codec_set_fmt,
.set_tdm_slot = sun8i_codec_set_tdm_slot,
.startup = sun8i_codec_startup,
.hw_params = sun8i_codec_hw_params,
.hw_free = sun8i_codec_hw_free,
+ .auto_selectable_formats = &sun8i_codec_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver sun8i_codec_dais[] = {
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index d9ab210ad69a..b31f9477cb2e 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -309,12 +309,22 @@ static int tegra20_i2s_startup(struct snd_pcm_substream *substream,
SNDRV_PCM_HW_PARAM_RATE, -1);
}
+static const u64 tegra20_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
static const struct snd_soc_dai_ops tegra20_i2s_dai_ops = {
.probe = tegra20_i2s_probe,
.set_fmt = tegra20_i2s_set_fmt,
.hw_params = tegra20_i2s_hw_params,
.trigger = tegra20_i2s_trigger,
.startup = tegra20_i2s_startup,
+ .auto_selectable_formats = &tegra20_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_driver tegra20_i2s_dai_template = {
diff --git a/sound/soc/tegra/tegra210_adx.c b/sound/soc/tegra/tegra210_adx.c
index e75bf50f8fb0..f2adf1c6e37e 100644
--- a/sound/soc/tegra/tegra210_adx.c
+++ b/sound/soc/tegra/tegra210_adx.c
@@ -676,11 +676,13 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev)
void __iomem *regs;
int err, i;
- adx = devm_kzalloc(dev, sizeof(*adx), GFP_KERNEL);
+ soc_data = of_device_get_match_data(dev);
+ adx = devm_kzalloc(dev,
+ struct_size(adx, map, soc_data->ram_depth * TEGRA_ADX_SLOTS_PER_WORD),
+ GFP_KERNEL);
if (!adx)
return -ENOMEM;
- soc_data = of_device_get_match_data(dev);
adx->soc_data = soc_data;
dev_set_drvdata(dev, adx);
@@ -697,12 +699,6 @@ static int tegra210_adx_platform_probe(struct platform_device *pdev)
regcache_cache_only(adx->regmap, true);
- adx->map = devm_kcalloc(dev,
- soc_data->ram_depth * TEGRA_ADX_SLOTS_PER_WORD,
- sizeof(*adx->map), GFP_KERNEL);
- if (!adx->map)
- return -ENOMEM;
-
adx->byte_mask = devm_kcalloc(dev, soc_data->byte_mask_size,
sizeof(*adx->byte_mask), GFP_KERNEL);
if (!adx->byte_mask)
diff --git a/sound/soc/tegra/tegra210_adx.h b/sound/soc/tegra/tegra210_adx.h
index a6298c3dcca5..e5f1b44b2e76 100644
--- a/sound/soc/tegra/tegra210_adx.h
+++ b/sound/soc/tegra/tegra210_adx.h
@@ -92,8 +92,8 @@ struct tegra210_adx_soc_data {
struct tegra210_adx {
struct regmap *regmap;
unsigned int *byte_mask;
- u16 *map;
const struct tegra210_adx_soc_data *soc_data;
+ u16 map[];
};
#endif
diff --git a/sound/soc/tegra/tegra210_i2s.c b/sound/soc/tegra/tegra210_i2s.c
index 79a2f898ea28..f2cfe3b5ccce 100644
--- a/sound/soc/tegra/tegra210_i2s.c
+++ b/sound/soc/tegra/tegra210_i2s.c
@@ -739,11 +739,24 @@ static int tegra210_i2s_hw_params(struct snd_pcm_substream *substream,
cif_conf.client_ch);
}
+static const u64 tegra210_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops tegra210_i2s_dai_ops = {
.set_fmt = tegra210_i2s_set_fmt,
.hw_params = tegra210_i2s_hw_params,
.set_bclk_ratio = tegra210_i2s_set_dai_bclk_ratio,
.set_tdm_slot = tegra210_i2s_set_tdm_slot,
+ .auto_selectable_formats = &tegra210_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver tegra210_i2s_dais[] = {
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 1d7b3aed51fd..69acff73f68a 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -302,12 +302,22 @@ static int tegra30_i2s_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 tegra30_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops tegra30_i2s_dai_ops = {
.probe = tegra30_i2s_probe,
.set_fmt = tegra30_i2s_set_fmt,
.hw_params = tegra30_i2s_hw_params,
.trigger = tegra30_i2s_trigger,
.set_tdm_slot = tegra30_i2s_set_tdm,
+ .auto_selectable_formats = &tegra30_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static const struct snd_soc_dai_driver tegra30_i2s_dai_template = {
diff --git a/sound/soc/ti/davinci-i2s.c b/sound/soc/ti/davinci-i2s.c
index 059967f0e632..3614f614117d 100644
--- a/sound/soc/ti/davinci-i2s.c
+++ b/sound/soc/ti/davinci-i2s.c
@@ -49,7 +49,7 @@
* incompatible with ASP and with either McBSP.
*
* In short: this uses ASP to implement I2S, not McBSP.
- * And it won't be the only DaVinci implemention of I2S.
+ * And it won't be the only DaVinci implementation of I2S.
*/
#define DAVINCI_MCBSP_DRR_REG 0x00
#define DAVINCI_MCBSP_DXR_REG 0x04
@@ -743,6 +743,17 @@ static int davinci_i2s_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 davinci_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_CONT |
+ SND_SOC_POSSIBLE_DAIFMT_GATED |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.probe = davinci_i2s_dai_probe,
.shutdown = davinci_i2s_shutdown,
@@ -752,7 +763,8 @@ static const struct snd_soc_dai_ops davinci_i2s_dai_ops = {
.set_fmt = davinci_i2s_set_dai_fmt,
.set_clkdiv = davinci_i2s_dai_set_clkdiv,
.set_tdm_slot = davinci_i2s_set_tdm_slot,
-
+ .auto_selectable_formats = &davinci_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver davinci_i2s_dai = {
diff --git a/sound/soc/ti/davinci-mcasp.c b/sound/soc/ti/davinci-mcasp.c
index 79239131b659..481d1042ddb6 100644
--- a/sound/soc/ti/davinci-mcasp.c
+++ b/sound/soc/ti/davinci-mcasp.c
@@ -799,6 +799,7 @@ static int __davinci_mcasp_set_clkdiv(struct davinci_mcasp *mcasp, int div_id,
break;
default:
+ pm_runtime_put(mcasp->dev);
return -EINVAL;
}
@@ -1818,7 +1819,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
!mcasp->async_mode)
max_channels = mcasp->channels;
/*
- * But we can always allow channels upto the amount of
+ * But we can always allow channels up to the amount of
* the available tdm_slots.
*/
if (max_channels < tdm_slots)
@@ -1993,6 +1994,18 @@ static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 davinci_mcasp_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_AC97 |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF;
+
static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
.probe = davinci_mcasp_dai_probe,
.startup = davinci_mcasp_startup,
@@ -2004,6 +2017,8 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
.set_clkdiv = davinci_mcasp_set_clkdiv,
.set_sysclk = davinci_mcasp_set_sysclk,
.set_tdm_slot = davinci_mcasp_set_tdm_slot,
+ .auto_selectable_formats = &davinci_mcasp_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
#define DAVINCI_MCASP_RATES SNDRV_PCM_RATE_8000_192000
diff --git a/sound/soc/ti/omap-mcbsp.c b/sound/soc/ti/omap-mcbsp.c
index 26af616c33f5..889e357b64d0 100644
--- a/sound/soc/ti/omap-mcbsp.c
+++ b/sound/soc/ti/omap-mcbsp.c
@@ -1237,7 +1237,7 @@ static int omap_mcbsp_dai_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
break;
case OMAP_MCBSP_SYSCLK_CLKR_EXT:
regs->pcr0 |= SCLKME;
- /* Disable ouput on CLKR pin in master mode */
+ /* Disable output on CLKR pin in master mode */
regs->pcr0 &= ~CLKRM;
break;
default:
@@ -1269,6 +1269,16 @@ static int omap_mcbsp_remove(struct snd_soc_dai *dai)
return 0;
}
+static const u64 mcbsp_mcbsp_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_IB_IF;
+
static const struct snd_soc_dai_ops mcbsp_dai_ops = {
.probe = omap_mcbsp_probe,
.remove = omap_mcbsp_remove,
@@ -1281,6 +1291,8 @@ static const struct snd_soc_dai_ops mcbsp_dai_ops = {
.set_fmt = omap_mcbsp_dai_set_dai_fmt,
.set_clkdiv = omap_mcbsp_dai_set_clkdiv,
.set_sysclk = omap_mcbsp_dai_set_dai_sysclk,
+ .auto_selectable_formats = &mcbsp_mcbsp_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver omap_mcbsp_dai = {
diff --git a/sound/soc/uniphier/aio-cpu.c b/sound/soc/uniphier/aio-cpu.c
index 78c82bf42a8f..b263526f1990 100644
--- a/sound/soc/uniphier/aio-cpu.c
+++ b/sound/soc/uniphier/aio-cpu.c
@@ -440,6 +440,11 @@ static int uniphier_aio_pxs2_probe(struct snd_soc_dai *dai)
return 0;
}
+static const u64 uniphier_aio_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
+
const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops = {
.probe = uniphier_aio_ld11_probe,
.remove = uniphier_aio_dai_remove,
@@ -451,6 +456,8 @@ const struct snd_soc_dai_ops uniphier_aio_i2s_ld11_ops = {
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
+ .auto_selectable_formats = &uniphier_aio_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_ld11_ops);
@@ -492,6 +499,8 @@ const struct snd_soc_dai_ops uniphier_aio_i2s_pxs2_ops = {
.hw_params = uniphier_aio_hw_params,
.hw_free = uniphier_aio_hw_free,
.prepare = uniphier_aio_prepare,
+ .auto_selectable_formats = &uniphier_aio_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
EXPORT_SYMBOL_GPL(uniphier_aio_i2s_pxs2_ops);
diff --git a/sound/soc/uniphier/aio-dma.c b/sound/soc/uniphier/aio-dma.c
index c01eae55d4fc..389a45319dfc 100644
--- a/sound/soc/uniphier/aio-dma.c
+++ b/sound/soc/uniphier/aio-dma.c
@@ -66,8 +66,7 @@ static void aiodma_compr_irq(struct uniphier_aio_sub *sub)
static irqreturn_t aiodma_irq(int irq, void *p)
{
- struct platform_device *pdev = p;
- struct uniphier_aio_chip *chip = platform_get_drvdata(pdev);
+ struct uniphier_aio_chip *chip = p;
irqreturn_t ret = IRQ_NONE;
int i, j;
@@ -263,7 +262,7 @@ int uniphier_aiodma_soc_register_platform(struct platform_device *pdev)
return irq;
ret = devm_request_irq(dev, irq, aiodma_irq,
- IRQF_SHARED, dev_name(dev), pdev);
+ IRQF_SHARED, dev_name(dev), chip);
if (ret)
return ret;
diff --git a/sound/soc/uniphier/aio.h b/sound/soc/uniphier/aio.h
index 1900ea01ce3d..87b27256cf80 100644
--- a/sound/soc/uniphier/aio.h
+++ b/sound/soc/uniphier/aio.h
@@ -183,7 +183,7 @@ struct uniphier_aio_selector {
*
* These are examples for sound data paths:
*
- * For caputure device:
+ * For capture device:
* (outer of AIO) -> iport -> iif -> ch -> rb -> (CPU)
* For playback device:
* (CPU) -> rb -> ch -> oif -> oport -> (outer of AIO)
diff --git a/sound/soc/ux500/Kconfig b/sound/soc/ux500/Kconfig
index 113058060350..f3c7c216267c 100644
--- a/sound/soc/ux500/Kconfig
+++ b/sound/soc/ux500/Kconfig
@@ -6,28 +6,19 @@ menuconfig SND_SOC_UX500
tristate "Ux500"
depends on SND_SOC
depends on MFD_DB8500_PRCMU
+ depends on AB8500_CORE && AB8500_GPADC
+ select SND_SOC_AB8500_CODEC
+ select SND_SOC_UX500_PLAT_MSP_I2S
+ select SND_SOC_UX500_PLAT_DMA
help
- Say Y if you want to enable ASoC-support for
- any of the Ux500 platforms (e.g. U8500).
+ Say Y if you want to enable ASoC support for Ux500 platforms
+ using the AB8500 or AB8505 codec.
config SND_SOC_UX500_PLAT_MSP_I2S
tristate
depends on SND_SOC_UX500
config SND_SOC_UX500_PLAT_DMA
- tristate "Platform - DB8500 (DMA)"
+ tristate
depends on SND_SOC_UX500
select SND_SOC_GENERIC_DMAENGINE_PCM
- help
- Say Y if you want to enable the Ux500 platform-driver.
-
-config SND_SOC_UX500_MACH_MOP500
- tristate "Machine - MOP500 (Ux500 + AB8500)"
- depends on AB8500_CORE && AB8500_GPADC && SND_SOC_UX500
- select SND_SOC_AB8500_CODEC
- select SND_SOC_UX500_PLAT_MSP_I2S
- select SND_SOC_UX500_PLAT_DMA
- help
- Select this to enable the MOP500 machine-driver.
- This will enable platform-drivers for: Ux500
- This will enable codec-drivers for: AB8500
diff --git a/sound/soc/ux500/Makefile b/sound/soc/ux500/Makefile
index a63787d9d664..83e7aeff70f8 100644
--- a/sound/soc/ux500/Makefile
+++ b/sound/soc/ux500/Makefile
@@ -6,6 +6,3 @@ obj-$(CONFIG_SND_SOC_UX500_PLAT_MSP_I2S) += snd-soc-ux500-plat-msp-i2s.o
snd-soc-ux500-plat-dma-y := ux500_pcm.o
obj-$(CONFIG_SND_SOC_UX500_PLAT_DMA) += snd-soc-ux500-plat-dma.o
-
-snd-soc-ux500-mach-mop500-y := mop500.o mop500_ab8500.o
-obj-$(CONFIG_SND_SOC_UX500_MACH_MOP500) += snd-soc-ux500-mach-mop500.o
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
deleted file mode 100644
index 6d196b4b8802..000000000000
--- a/sound/soc/ux500/mop500.c
+++ /dev/null
@@ -1,167 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Author: Ola Lilja (ola.o.lilja@stericsson.com)
- * for ST-Ericsson.
- */
-
-#include <asm/mach-types.h>
-
-#include <linux/module.h>
-#include <linux/io.h>
-#include <linux/spi/spi.h>
-#include <linux/of.h>
-
-#include <sound/soc.h>
-#include <sound/initval.h>
-
-#include "ux500_pcm.h"
-#include "ux500_msp_dai.h"
-
-#include "mop500_ab8500.h"
-
-/* Define the whole MOP500 soundcard, linking platform to the codec-drivers */
-SND_SOC_DAILINK_DEFS(link1,
- DAILINK_COMP_ARRAY(COMP_CPU("ux500-msp-i2s.1")),
- DAILINK_COMP_ARRAY(COMP_CODEC("ab8500-codec.0", "ab8500-codec-dai.0")),
- DAILINK_COMP_ARRAY(COMP_PLATFORM("ux500-msp-i2s.1")));
-
-SND_SOC_DAILINK_DEFS(link2,
- DAILINK_COMP_ARRAY(COMP_CPU("ux500-msp-i2s.3")),
- DAILINK_COMP_ARRAY(COMP_CODEC("ab8500-codec.0", "ab8500-codec-dai.1")),
- DAILINK_COMP_ARRAY(COMP_PLATFORM("ux500-msp-i2s.3")));
-
-static struct snd_soc_dai_link mop500_dai_links[] = {
- {
- .name = "ab8500_0",
- .stream_name = "ab8500_0",
- .init = mop500_ab8500_machine_init,
- .ops = mop500_ab8500_ops,
- SND_SOC_DAILINK_REG(link1),
- },
- {
- .name = "ab8500_1",
- .stream_name = "ab8500_1",
- .init = NULL,
- .ops = mop500_ab8500_ops,
- SND_SOC_DAILINK_REG(link2),
- },
-};
-
-static struct snd_soc_card mop500_card = {
- .name = "MOP500-card",
- .owner = THIS_MODULE,
- .probe = NULL,
- .dai_link = mop500_dai_links,
- .num_links = ARRAY_SIZE(mop500_dai_links),
-};
-
-static void mop500_of_node_put(void)
-{
- int i;
-
- for (i = 0; i < 2; i++)
- of_node_put(mop500_dai_links[i].cpus->of_node);
-
- /* Both links use the same codec, which is refcounted only once */
- of_node_put(mop500_dai_links[0].codecs->of_node);
-}
-
-static int mop500_of_probe(struct snd_soc_card *card)
-{
- struct device *dev = card->dev;
- struct device_node *codec_np, *msp_np[2];
- struct device_node *np = dev->of_node;
- int i;
-
- msp_np[0] = of_parse_phandle(np, "stericsson,cpu-dai", 0);
- msp_np[1] = of_parse_phandle(np, "stericsson,cpu-dai", 1);
- codec_np = of_parse_phandle(np, "stericsson,audio-codec", 0);
-
- if (!(msp_np[0] && msp_np[1] && codec_np)) {
- dev_err(dev, "Phandle missing or invalid\n");
- for (i = 0; i < 2; i++)
- of_node_put(msp_np[i]);
- of_node_put(codec_np);
- return -EINVAL;
- }
-
- for (i = 0; i < 2; i++) {
- mop500_dai_links[i].cpus->of_node = msp_np[i];
- mop500_dai_links[i].cpus->dai_name = NULL;
- mop500_dai_links[i].platforms->of_node = msp_np[i];
- mop500_dai_links[i].platforms->name = NULL;
- mop500_dai_links[i].codecs->of_node = codec_np;
- mop500_dai_links[i].codecs->name = NULL;
- }
-
- snd_soc_of_parse_card_name(card, "stericsson,card-name");
-
- return 0;
-}
-
-static int mop500_probe(struct platform_device *pdev)
-{
- int ret;
-
- dev_dbg(&pdev->dev, "%s: Enter.\n", __func__);
-
- mop500_card.dev = &pdev->dev;
-
- ret = mop500_of_probe(&mop500_card);
- if (ret)
- return ret;
-
- dev_dbg(&pdev->dev, "%s: Card %s: Set platform drvdata.\n",
- __func__, mop500_card.name);
-
- snd_soc_card_set_drvdata(&mop500_card, NULL);
-
- dev_dbg(&pdev->dev, "%s: Card %s: num_links = %d\n",
- __func__, mop500_card.name, mop500_card.num_links);
- dev_dbg(&pdev->dev, "%s: Card %s: DAI-link 0: name = %s\n",
- __func__, mop500_card.name, mop500_card.dai_link[0].name);
- dev_dbg(&pdev->dev, "%s: Card %s: DAI-link 0: stream_name = %s\n",
- __func__, mop500_card.name,
- mop500_card.dai_link[0].stream_name);
-
- ret = snd_soc_register_card(&mop500_card);
- if (ret)
- dev_err(&pdev->dev,
- "Error: snd_soc_register_card failed (%d)!\n", ret);
-
- return ret;
-}
-
-static void mop500_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
-
- pr_debug("%s: Enter.\n", __func__);
-
- snd_soc_unregister_card(card);
- mop500_ab8500_remove(card);
- mop500_of_node_put();
-}
-
-static const struct of_device_id snd_soc_mop500_match[] = {
- { .compatible = "stericsson,snd-soc-mop500", },
- {},
-};
-MODULE_DEVICE_TABLE(of, snd_soc_mop500_match);
-
-static struct platform_driver snd_soc_mop500_driver = {
- .driver = {
- .name = "snd-soc-mop500",
- .of_match_table = snd_soc_mop500_match,
- },
- .probe = mop500_probe,
- .remove = mop500_remove,
-};
-
-module_platform_driver(snd_soc_mop500_driver);
-
-MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("ASoC MOP500 board driver");
-MODULE_AUTHOR("Ola Lilja");
diff --git a/sound/soc/ux500/mop500_ab8500.c b/sound/soc/ux500/mop500_ab8500.c
deleted file mode 100644
index feb683c55d11..000000000000
--- a/sound/soc/ux500/mop500_ab8500.c
+++ /dev/null
@@ -1,437 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Author: Ola Lilja <ola.o.lilja@stericsson.com>,
- * Kristoffer Karlsson <kristoffer.karlsson@stericsson.com>
- * for ST-Ericsson.
- */
-
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/mutex.h>
-
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-
-#include "ux500_pcm.h"
-#include "ux500_msp_dai.h"
-#include "mop500_ab8500.h"
-#include "../codecs/ab8500-codec.h"
-
-#define TX_SLOT_MONO 0x0008
-#define TX_SLOT_STEREO 0x000a
-#define RX_SLOT_MONO 0x0001
-#define RX_SLOT_STEREO 0x0003
-#define TX_SLOT_8CH 0x00FF
-#define RX_SLOT_8CH 0x00FF
-
-#define DEF_TX_SLOTS TX_SLOT_STEREO
-#define DEF_RX_SLOTS RX_SLOT_MONO
-
-#define DRIVERMODE_NORMAL 0
-#define DRIVERMODE_CODEC_ONLY 1
-
-/* Slot configuration */
-static unsigned int tx_slots = DEF_TX_SLOTS;
-static unsigned int rx_slots = DEF_RX_SLOTS;
-
-/* Configuration consistency parameters */
-static DEFINE_MUTEX(mop500_ab8500_params_lock);
-static unsigned long mop500_ab8500_usage;
-static int mop500_ab8500_rate;
-static int mop500_ab8500_channels;
-
-/* Clocks */
-static const char * const enum_mclk[] = {
- "SYSCLK",
- "ULPCLK"
-};
-enum mclk {
- MCLK_SYSCLK,
- MCLK_ULPCLK,
-};
-
-static SOC_ENUM_SINGLE_EXT_DECL(soc_enum_mclk, enum_mclk);
-
-/* Private data for machine-part MOP500<->AB8500 */
-struct mop500_ab8500_drvdata {
- /* Clocks */
- enum mclk mclk_sel;
- struct clk *clk_ptr_intclk;
- struct clk *clk_ptr_sysclk;
- struct clk *clk_ptr_ulpclk;
-};
-
-static inline const char *get_mclk_str(enum mclk mclk_sel)
-{
- switch (mclk_sel) {
- case MCLK_SYSCLK:
- return "SYSCLK";
- case MCLK_ULPCLK:
- return "ULPCLK";
- default:
- return "Unknown";
- }
-}
-
-static int mop500_ab8500_set_mclk(struct device *dev,
- struct mop500_ab8500_drvdata *drvdata)
-{
- int status;
- struct clk *clk_ptr;
-
- if (IS_ERR(drvdata->clk_ptr_intclk)) {
- dev_err(dev,
- "%s: ERROR: intclk not initialized!\n", __func__);
- return -EIO;
- }
-
- switch (drvdata->mclk_sel) {
- case MCLK_SYSCLK:
- clk_ptr = drvdata->clk_ptr_sysclk;
- break;
- case MCLK_ULPCLK:
- clk_ptr = drvdata->clk_ptr_ulpclk;
- break;
- default:
- return -EINVAL;
- }
-
- if (IS_ERR(clk_ptr)) {
- dev_err(dev, "%s: ERROR: %s not initialized!\n", __func__,
- get_mclk_str(drvdata->mclk_sel));
- return -EIO;
- }
-
- status = clk_set_parent(drvdata->clk_ptr_intclk, clk_ptr);
- if (status)
- dev_err(dev,
- "%s: ERROR: Setting intclk parent to %s failed (ret = %d)!",
- __func__, get_mclk_str(drvdata->mclk_sel), status);
- else
- dev_dbg(dev,
- "%s: intclk parent changed to %s.\n",
- __func__, get_mclk_str(drvdata->mclk_sel));
-
- return status;
-}
-
-/*
- * Control-events
- */
-
-static int mclk_input_control_get(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct mop500_ab8500_drvdata *drvdata =
- snd_soc_card_get_drvdata(card);
-
- ucontrol->value.enumerated.item[0] = drvdata->mclk_sel;
-
- return 0;
-}
-
-static int mclk_input_control_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_card *card = snd_kcontrol_chip(kcontrol);
- struct mop500_ab8500_drvdata *drvdata =
- snd_soc_card_get_drvdata(card);
- unsigned int val = ucontrol->value.enumerated.item[0];
-
- if (val > (unsigned int)MCLK_ULPCLK)
- return -EINVAL;
- if (drvdata->mclk_sel == val)
- return 0;
-
- drvdata->mclk_sel = val;
-
- return 1;
-}
-
-/*
- * Controls
- */
-
-static struct snd_kcontrol_new mop500_ab8500_ctrls[] = {
- SOC_ENUM_EXT("Master Clock Select",
- soc_enum_mclk,
- mclk_input_control_get, mclk_input_control_put),
- SOC_DAPM_PIN_SWITCH("Headset Left"),
- SOC_DAPM_PIN_SWITCH("Headset Right"),
- SOC_DAPM_PIN_SWITCH("Earpiece"),
- SOC_DAPM_PIN_SWITCH("Speaker Left"),
- SOC_DAPM_PIN_SWITCH("Speaker Right"),
- SOC_DAPM_PIN_SWITCH("LineOut Left"),
- SOC_DAPM_PIN_SWITCH("LineOut Right"),
- SOC_DAPM_PIN_SWITCH("Vibra 1"),
- SOC_DAPM_PIN_SWITCH("Vibra 2"),
- SOC_DAPM_PIN_SWITCH("Mic 1"),
- SOC_DAPM_PIN_SWITCH("Mic 2"),
- SOC_DAPM_PIN_SWITCH("LineIn Left"),
- SOC_DAPM_PIN_SWITCH("LineIn Right"),
- SOC_DAPM_PIN_SWITCH("DMic 1"),
- SOC_DAPM_PIN_SWITCH("DMic 2"),
- SOC_DAPM_PIN_SWITCH("DMic 3"),
- SOC_DAPM_PIN_SWITCH("DMic 4"),
- SOC_DAPM_PIN_SWITCH("DMic 5"),
- SOC_DAPM_PIN_SWITCH("DMic 6"),
-};
-
-/* ASoC */
-
-static int mop500_ab8500_startup(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
-
- /* Set audio-clock source */
- return mop500_ab8500_set_mclk(rtd->card->dev,
- snd_soc_card_get_drvdata(rtd->card));
-}
-
-static void mop500_ab8500_shutdown(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct device *dev = rtd->card->dev;
-
- dev_dbg(dev, "%s: Enter\n", __func__);
-
- /* Reset slots configuration to default(s) */
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- tx_slots = DEF_TX_SLOTS;
- else
- rx_slots = DEF_RX_SLOTS;
-}
-
-static int mop500_ab8500_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_soc_dai *codec_dai = snd_soc_rtd_to_codec(rtd, 0);
- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
- struct device *dev = rtd->card->dev;
- unsigned int fmt;
- int channels, ret = 0, driver_mode, slots;
- unsigned int sw_codec, sw_cpu;
- bool is_playback;
-
- dev_dbg(dev, "%s: Enter\n", __func__);
-
- dev_dbg(dev, "%s: substream->pcm->name = %s\n"
- "substream->pcm->id = %s.\n"
- "substream->name = %s.\n"
- "substream->number = %d.\n",
- __func__,
- substream->pcm->name,
- substream->pcm->id,
- substream->name,
- substream->number);
-
- /* Ensure configuration consistency between DAIs */
- scoped_guard(mutex, &mop500_ab8500_params_lock) {
- if (mop500_ab8500_usage) {
- if (mop500_ab8500_rate != params_rate(params) ||
- mop500_ab8500_channels != params_channels(params)) {
- return -EBUSY;
- }
- } else {
- mop500_ab8500_rate = params_rate(params);
- mop500_ab8500_channels = params_channels(params);
- }
- __set_bit(cpu_dai->id, &mop500_ab8500_usage);
- }
-
- channels = params_channels(params);
-
- switch (params_format(params)) {
- case SNDRV_PCM_FORMAT_S32_LE:
- sw_cpu = 32;
- break;
-
- case SNDRV_PCM_FORMAT_S16_LE:
- sw_cpu = 16;
- break;
-
- default:
- return -EINVAL;
- }
-
- /* Setup codec depending on driver-mode */
- if (channels == 8)
- driver_mode = DRIVERMODE_CODEC_ONLY;
- else
- driver_mode = DRIVERMODE_NORMAL;
- dev_dbg(dev, "%s: Driver-mode: %s.\n", __func__,
- (driver_mode == DRIVERMODE_NORMAL) ? "NORMAL" : "CODEC_ONLY");
-
- /* Setup format */
-
- if (driver_mode == DRIVERMODE_NORMAL) {
- fmt = SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_CBP_CFP |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_CONT;
- } else {
- fmt = SND_SOC_DAIFMT_DSP_A |
- SND_SOC_DAIFMT_CBP_CFP |
- SND_SOC_DAIFMT_NB_NF |
- SND_SOC_DAIFMT_GATED;
- }
-
- ret = snd_soc_runtime_set_dai_fmt(rtd, fmt);
- if (ret)
- return ret;
-
- /* Setup TDM-slots */
-
- is_playback = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
- switch (channels) {
- case 1:
- slots = 16;
- tx_slots = (is_playback) ? TX_SLOT_MONO : 0;
- rx_slots = (is_playback) ? 0 : RX_SLOT_MONO;
- break;
- case 2:
- slots = 16;
- tx_slots = (is_playback) ? TX_SLOT_STEREO : 0;
- rx_slots = (is_playback) ? 0 : RX_SLOT_STEREO;
- break;
- case 8:
- slots = 16;
- tx_slots = (is_playback) ? TX_SLOT_8CH : 0;
- rx_slots = (is_playback) ? 0 : RX_SLOT_8CH;
- break;
- default:
- return -EINVAL;
- }
-
- if (driver_mode == DRIVERMODE_NORMAL)
- sw_codec = sw_cpu;
- else
- sw_codec = 20;
-
- dev_dbg(dev, "%s: CPU-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__,
- tx_slots, rx_slots);
- ret = snd_soc_dai_set_tdm_slot(cpu_dai, tx_slots, rx_slots, slots,
- sw_cpu);
- if (ret)
- return ret;
-
- dev_dbg(dev, "%s: CODEC-DAI TDM: TX=0x%04X RX=0x%04x\n", __func__,
- tx_slots, rx_slots);
- ret = snd_soc_dai_set_tdm_slot(codec_dai, tx_slots, rx_slots, slots,
- sw_codec);
- if (ret)
- return ret;
-
- return 0;
-}
-
-static int mop500_ab8500_hw_free(struct snd_pcm_substream *substream)
-{
- struct snd_soc_pcm_runtime *rtd = snd_soc_substream_to_rtd(substream);
- struct snd_soc_dai *cpu_dai = snd_soc_rtd_to_cpu(rtd, 0);
-
- guard(mutex)(&mop500_ab8500_params_lock);
- __clear_bit(cpu_dai->id, &mop500_ab8500_usage);
-
- return 0;
-}
-
-const struct snd_soc_ops mop500_ab8500_ops[] = {
- {
- .hw_params = mop500_ab8500_hw_params,
- .hw_free = mop500_ab8500_hw_free,
- .startup = mop500_ab8500_startup,
- .shutdown = mop500_ab8500_shutdown,
- }
-};
-
-int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_card_to_dapm(rtd->card);
- struct device *dev = rtd->card->dev;
- struct mop500_ab8500_drvdata *drvdata;
- int ret;
-
- dev_dbg(dev, "%s Enter.\n", __func__);
-
- /* Create driver private-data struct */
- drvdata = devm_kzalloc(dev, sizeof(struct mop500_ab8500_drvdata),
- GFP_KERNEL);
-
- if (!drvdata)
- return -ENOMEM;
-
- snd_soc_card_set_drvdata(rtd->card, drvdata);
-
- /* Setup clocks */
-
- drvdata->clk_ptr_sysclk = clk_get(dev, "sysclk");
- if (IS_ERR(drvdata->clk_ptr_sysclk))
- dev_warn(dev, "%s: WARNING: clk_get failed for 'sysclk'!\n",
- __func__);
- drvdata->clk_ptr_ulpclk = clk_get(dev, "ulpclk");
- if (IS_ERR(drvdata->clk_ptr_ulpclk))
- dev_warn(dev, "%s: WARNING: clk_get failed for 'ulpclk'!\n",
- __func__);
- drvdata->clk_ptr_intclk = clk_get(dev, "intclk");
- if (IS_ERR(drvdata->clk_ptr_intclk))
- dev_warn(dev, "%s: WARNING: clk_get failed for 'intclk'!\n",
- __func__);
-
- /* Set intclk default parent to ulpclk */
- drvdata->mclk_sel = MCLK_ULPCLK;
- ret = mop500_ab8500_set_mclk(dev, drvdata);
- if (ret < 0)
- dev_warn(dev, "%s: WARNING: mop500_ab8500_set_mclk!\n",
- __func__);
-
- drvdata->mclk_sel = MCLK_ULPCLK;
-
- /* Add controls */
- ret = snd_soc_add_card_controls(rtd->card, mop500_ab8500_ctrls,
- ARRAY_SIZE(mop500_ab8500_ctrls));
- if (ret < 0) {
- pr_err("%s: Failed to add machine-controls (%d)!\n",
- __func__, ret);
- return ret;
- }
-
- ret = snd_soc_dapm_disable_pin(dapm, "Earpiece");
- ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Left");
- ret |= snd_soc_dapm_disable_pin(dapm, "Speaker Right");
- ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Left");
- ret |= snd_soc_dapm_disable_pin(dapm, "LineOut Right");
- ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 1");
- ret |= snd_soc_dapm_disable_pin(dapm, "Vibra 2");
- ret |= snd_soc_dapm_disable_pin(dapm, "Mic 1");
- ret |= snd_soc_dapm_disable_pin(dapm, "Mic 2");
- ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Left");
- ret |= snd_soc_dapm_disable_pin(dapm, "LineIn Right");
- ret |= snd_soc_dapm_disable_pin(dapm, "DMic 1");
- ret |= snd_soc_dapm_disable_pin(dapm, "DMic 2");
- ret |= snd_soc_dapm_disable_pin(dapm, "DMic 3");
- ret |= snd_soc_dapm_disable_pin(dapm, "DMic 4");
- ret |= snd_soc_dapm_disable_pin(dapm, "DMic 5");
- ret |= snd_soc_dapm_disable_pin(dapm, "DMic 6");
-
- return ret;
-}
-
-void mop500_ab8500_remove(struct snd_soc_card *card)
-{
- struct mop500_ab8500_drvdata *drvdata = snd_soc_card_get_drvdata(card);
-
- clk_put(drvdata->clk_ptr_sysclk);
- clk_put(drvdata->clk_ptr_ulpclk);
- clk_put(drvdata->clk_ptr_intclk);
-
- snd_soc_card_set_drvdata(card, NULL);
-}
diff --git a/sound/soc/ux500/mop500_ab8500.h b/sound/soc/ux500/mop500_ab8500.h
deleted file mode 100644
index 98de80a9cc4f..000000000000
--- a/sound/soc/ux500/mop500_ab8500.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0-only */
-/*
- * Copyright (C) ST-Ericsson SA 2012
- *
- * Author: Ola Lilja <ola.o.lilja@stericsson.com>
- * for ST-Ericsson.
- */
-
-#ifndef MOP500_AB8500_H
-#define MOP500_AB8500_H
-
-extern const struct snd_soc_ops mop500_ab8500_ops[];
-
-int mop500_ab8500_machine_init(struct snd_soc_pcm_runtime *rtd);
-void mop500_ab8500_remove(struct snd_soc_card *card);
-
-#endif
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 37c48cc70394..c135a77766ca 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -644,18 +644,25 @@ static int ux500_msp_dai_of_probe(struct snd_soc_dai *dai)
return 0;
}
-static const struct snd_soc_dai_ops ux500_msp_dai_ops[] = {
- {
- .probe = ux500_msp_dai_of_probe,
- .set_sysclk = ux500_msp_dai_set_dai_sysclk,
- .set_fmt = ux500_msp_dai_set_dai_fmt,
- .set_tdm_slot = ux500_msp_dai_set_tdm_slot,
- .startup = ux500_msp_dai_startup,
- .shutdown = ux500_msp_dai_shutdown,
- .prepare = ux500_msp_dai_prepare,
- .trigger = ux500_msp_dai_trigger,
- .hw_params = ux500_msp_dai_hw_params,
- }
+static const u64 ux500_msp_dai_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF |
+ SND_SOC_POSSIBLE_DAIFMT_NB_IF |
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_A |
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
+
+static const struct snd_soc_dai_ops ux500_msp_dai_ops = {
+ .probe = ux500_msp_dai_of_probe,
+ .set_sysclk = ux500_msp_dai_set_dai_sysclk,
+ .set_fmt = ux500_msp_dai_set_dai_fmt,
+ .set_tdm_slot = ux500_msp_dai_set_tdm_slot,
+ .startup = ux500_msp_dai_startup,
+ .shutdown = ux500_msp_dai_shutdown,
+ .prepare = ux500_msp_dai_prepare,
+ .trigger = ux500_msp_dai_trigger,
+ .hw_params = ux500_msp_dai_hw_params,
+ .auto_selectable_formats = &ux500_msp_dai_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver ux500_msp_dai_drv = {
@@ -667,7 +674,7 @@ static struct snd_soc_dai_driver ux500_msp_dai_drv = {
.capture.channels_max = UX500_MSP_MAX_CHANNELS,
.capture.rates = UX500_I2S_RATES,
.capture.formats = UX500_I2S_FORMATS,
- .ops = ux500_msp_dai_ops,
+ .ops = &ux500_msp_dai_ops,
};
static const struct snd_soc_component_driver ux500_msp_component = {
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index 683b485fb570..bd2cfc618290 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -19,7 +19,7 @@
#include "ux500_msp_i2s.h"
- /* Protocol desciptors */
+ /* Protocol descriptors */
static const struct msp_protdesc prot_descs[] = {
{ /* I2S */
MSP_SINGLE_PHASE,
diff --git a/sound/soc/xilinx/xlnx_formatter_pcm.c b/sound/soc/xilinx/xlnx_formatter_pcm.c
index 95312b4ddc5b..304220d9aaf5 100644
--- a/sound/soc/xilinx/xlnx_formatter_pcm.c
+++ b/sound/soc/xilinx/xlnx_formatter_pcm.c
@@ -81,7 +81,6 @@ struct xlnx_pcm_drv_data {
int mm2s_irq;
struct snd_pcm_substream *play_stream;
struct snd_pcm_substream *capture_stream;
- struct clk *axi_clk;
unsigned int sysclk;
};
@@ -595,100 +594,68 @@ static int xlnx_formatter_pcm_probe(struct platform_device *pdev)
u32 val;
struct xlnx_pcm_drv_data *aud_drv_data;
struct device *dev = &pdev->dev;
+ struct clk *axi_clk;
+
+ axi_clk = devm_clk_get_enabled(dev, "s_axi_lite_aclk");
+ if (IS_ERR(axi_clk))
+ return dev_err_probe(dev, PTR_ERR(axi_clk), "failed to get s_axi_lite_aclk");
aud_drv_data = devm_kzalloc(dev, sizeof(*aud_drv_data), GFP_KERNEL);
if (!aud_drv_data)
return -ENOMEM;
- aud_drv_data->axi_clk = devm_clk_get(dev, "s_axi_lite_aclk");
- if (IS_ERR(aud_drv_data->axi_clk)) {
- ret = PTR_ERR(aud_drv_data->axi_clk);
- dev_err(dev, "failed to get s_axi_lite_aclk(%d)\n", ret);
- return ret;
- }
- ret = clk_prepare_enable(aud_drv_data->axi_clk);
- if (ret) {
- dev_err(dev,
- "failed to enable s_axi_lite_aclk(%d)\n", ret);
- return ret;
- }
-
aud_drv_data->mmio = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(aud_drv_data->mmio)) {
- dev_err(dev, "audio formatter ioremap failed\n");
- ret = PTR_ERR(aud_drv_data->mmio);
- goto clk_err;
- }
+ if (IS_ERR(aud_drv_data->mmio))
+ return PTR_ERR(aud_drv_data->mmio);
val = readl(aud_drv_data->mmio + XLNX_AUD_CORE_CONFIG);
if (val & AUD_CFG_MM2S_MASK) {
aud_drv_data->mm2s_presence = true;
ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio +
XLNX_MM2S_OFFSET);
- if (ret) {
- dev_err(dev, "audio formatter reset failed\n");
- goto clk_err;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "audio formatter reset failed\n");
xlnx_formatter_disable_irqs(aud_drv_data->mmio +
XLNX_MM2S_OFFSET,
SNDRV_PCM_STREAM_PLAYBACK);
aud_drv_data->mm2s_irq = platform_get_irq_byname(pdev,
"irq_mm2s");
- if (aud_drv_data->mm2s_irq < 0) {
- ret = aud_drv_data->mm2s_irq;
- goto clk_err;
- }
+ if (aud_drv_data->mm2s_irq < 0)
+ return aud_drv_data->mm2s_irq;
+
ret = devm_request_irq(dev, aud_drv_data->mm2s_irq,
xlnx_mm2s_irq_handler, 0,
"xlnx_formatter_pcm_mm2s_irq", aud_drv_data);
- if (ret) {
- dev_err(dev, "xlnx audio mm2s irq request failed\n");
- goto clk_err;
- }
+ if (ret)
+ return ret;
}
if (val & AUD_CFG_S2MM_MASK) {
aud_drv_data->s2mm_presence = true;
ret = xlnx_formatter_pcm_reset(aud_drv_data->mmio +
XLNX_S2MM_OFFSET);
- if (ret) {
- dev_err(dev, "audio formatter reset failed\n");
- goto clk_err;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "audio formatter reset failed\n");
xlnx_formatter_disable_irqs(aud_drv_data->mmio +
XLNX_S2MM_OFFSET,
SNDRV_PCM_STREAM_CAPTURE);
aud_drv_data->s2mm_irq = platform_get_irq_byname(pdev,
"irq_s2mm");
- if (aud_drv_data->s2mm_irq < 0) {
- ret = aud_drv_data->s2mm_irq;
- goto clk_err;
- }
+ if (aud_drv_data->s2mm_irq < 0)
+ return aud_drv_data->s2mm_irq;
+
ret = devm_request_irq(dev, aud_drv_data->s2mm_irq,
xlnx_s2mm_irq_handler, 0,
"xlnx_formatter_pcm_s2mm_irq",
aud_drv_data);
- if (ret) {
- dev_err(dev, "xlnx audio s2mm irq request failed\n");
- goto clk_err;
- }
+ if (ret)
+ return ret;
}
dev_set_drvdata(dev, aud_drv_data);
- ret = devm_snd_soc_register_component(dev, &xlnx_asoc_component,
- NULL, 0);
- if (ret) {
- dev_err(dev, "pcm platform device register failed\n");
- goto clk_err;
- }
-
- return 0;
-
-clk_err:
- clk_disable_unprepare(aud_drv_data->axi_clk);
- return ret;
+ return devm_snd_soc_register_component(dev, &xlnx_asoc_component, NULL, 0);
}
static void xlnx_formatter_pcm_remove(struct platform_device *pdev)
@@ -705,8 +672,6 @@ static void xlnx_formatter_pcm_remove(struct platform_device *pdev)
if (ret)
dev_err(&pdev->dev, "audio formatter reset failed\n");
-
- clk_disable_unprepare(adata->axi_clk);
}
static const struct of_device_id xlnx_formatter_pcm_of_match[] = {
diff --git a/sound/soc/xtensa/xtfpga-i2s.c b/sound/soc/xtensa/xtfpga-i2s.c
index fd0990201b40..e5e8b72fd817 100644
--- a/sound/soc/xtensa/xtfpga-i2s.c
+++ b/sound/soc/xtensa/xtfpga-i2s.c
@@ -485,10 +485,16 @@ static const struct snd_soc_component_driver xtfpga_i2s_component = {
.legacy_dai_naming = 1,
};
+static const u64 xtfpga_i2s_selectable_formats =
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_NB_NF;
+
static const struct snd_soc_dai_ops xtfpga_i2s_dai_ops = {
- .startup = xtfpga_i2s_startup,
- .hw_params = xtfpga_i2s_hw_params,
- .set_fmt = xtfpga_i2s_set_fmt,
+ .startup = xtfpga_i2s_startup,
+ .hw_params = xtfpga_i2s_hw_params,
+ .set_fmt = xtfpga_i2s_set_fmt,
+ .auto_selectable_formats = &xtfpga_i2s_selectable_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver xtfpga_i2s_dai[] = {