summaryrefslogtreecommitdiff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig40
-rw-r--r--sound/soc/codecs/Makefile8
-rw-r--r--sound/soc/codecs/ad193x-i2c.c4
-rw-r--r--sound/soc/codecs/adau1372-i2c.c3
-rw-r--r--sound/soc/codecs/adau1372-spi.c1
-rw-r--r--sound/soc/codecs/adau1372.c6
-rw-r--r--sound/soc/codecs/adau1373.c2
-rw-r--r--sound/soc/codecs/adau1701.c8
-rw-r--r--sound/soc/codecs/adau1761-i2c.c9
-rw-r--r--sound/soc/codecs/adau1761-spi.c1
-rw-r--r--sound/soc/codecs/adau1781-i2c.c5
-rw-r--r--sound/soc/codecs/adau1781-spi.c1
-rw-r--r--sound/soc/codecs/adau1977-i2c.c7
-rw-r--r--sound/soc/codecs/adau1977-spi.c1
-rw-r--r--sound/soc/codecs/adau7118-hw.c1
-rw-r--r--sound/soc/codecs/adau7118-i2c.c4
-rw-r--r--sound/soc/codecs/adav803.c2
-rw-r--r--sound/soc/codecs/ak4104.c1
-rw-r--r--sound/soc/codecs/ak4118.c4
-rw-r--r--sound/soc/codecs/ak4535.c2
-rw-r--r--sound/soc/codecs/ak4613.c7
-rw-r--r--sound/soc/codecs/ak4619.c10
-rw-r--r--sound/soc/codecs/ak4642.c8
-rw-r--r--sound/soc/codecs/ak4671.c2
-rw-r--r--sound/soc/codecs/alc5623.c8
-rw-r--r--sound/soc/codecs/alc5632.c4
-rw-r--r--sound/soc/codecs/audio-iio-aux.c1
-rw-r--r--sound/soc/codecs/aw87390.c4
-rw-r--r--sound/soc/codecs/aw88081.c16
-rw-r--r--sound/soc/codecs/aw88166.c2
-rw-r--r--sound/soc/codecs/aw88261.c600
-rw-r--r--sound/soc/codecs/aw88261.h185
-rw-r--r--sound/soc/codecs/aw88395/aw88395.c13
-rw-r--r--sound/soc/codecs/aw88395/aw88395_device.h2
-rw-r--r--sound/soc/codecs/aw88399.c2
-rw-r--r--sound/soc/codecs/cs-amp-lib.c5
-rw-r--r--sound/soc/codecs/cs35l32.c4
-rw-r--r--sound/soc/codecs/cs35l33.c4
-rw-r--r--sound/soc/codecs/cs35l34.c4
-rw-r--r--sound/soc/codecs/cs35l35.c4
-rw-r--r--sound/soc/codecs/cs35l36.c4
-rw-r--r--sound/soc/codecs/cs35l41-i2c.c10
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c4
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c6
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c231
-rw-r--r--sound/soc/codecs/cs35l56-shared-test.c6
-rw-r--r--sound/soc/codecs/cs35l56-shared.c6
-rw-r--r--sound/soc/codecs/cs35l56.c91
-rw-r--r--sound/soc/codecs/cs35l56.h7
-rw-r--r--sound/soc/codecs/cs4234.c1
-rw-r--r--sound/soc/codecs/cs4265.c2
-rw-r--r--sound/soc/codecs/cs4270.c5
-rw-r--r--sound/soc/codecs/cs4271-i2c.c2
-rw-r--r--sound/soc/codecs/cs42l42-i2c.c4
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c12
-rw-r--r--sound/soc/codecs/cs42l43-jack.c8
-rw-r--r--sound/soc/codecs/cs42l43.c1
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c2
-rw-r--r--sound/soc/codecs/cs42l52.c2
-rw-r--r--sound/soc/codecs/cs42l56.c2
-rw-r--r--sound/soc/codecs/cs42l73.c4
-rw-r--r--sound/soc/codecs/cs42l84.c4
-rw-r--r--sound/soc/codecs/cs42xx8-i2c.c7
-rw-r--r--sound/soc/codecs/cs42xx8-spi.c103
-rw-r--r--sound/soc/codecs/cs42xx8.c3
-rw-r--r--sound/soc/codecs/cs43130.c10
-rw-r--r--sound/soc/codecs/cs4341.c2
-rw-r--r--sound/soc/codecs/cs4349.c5
-rw-r--r--sound/soc/codecs/cs530x-i2c.c14
-rw-r--r--sound/soc/codecs/cs530x.c29
-rw-r--r--sound/soc/codecs/cs530x.h6
-rw-r--r--sound/soc/codecs/cs53l30.c4
-rw-r--r--sound/soc/codecs/cx20442.c11
-rw-r--r--sound/soc/codecs/cx20442.h5
-rw-r--r--sound/soc/codecs/cx2072x.c6
-rw-r--r--sound/soc/codecs/da7210.c2
-rw-r--r--sound/soc/codecs/da7213.c7
-rw-r--r--sound/soc/codecs/da7218.c4
-rw-r--r--sound/soc/codecs/da7219.c2
-rw-r--r--sound/soc/codecs/da732x.c2
-rw-r--r--sound/soc/codecs/da9055.c2
-rw-r--r--sound/soc/codecs/es8311.c2
-rw-r--r--sound/soc/codecs/es8316.c5
-rw-r--r--sound/soc/codecs/es8323.c3
-rw-r--r--sound/soc/codecs/es8326.c4
-rw-r--r--sound/soc/codecs/es8328-i2c.c4
-rw-r--r--sound/soc/codecs/es8375.c2
-rw-r--r--sound/soc/codecs/es8389.c2
-rw-r--r--sound/soc/codecs/es9356.c1143
-rw-r--r--sound/soc/codecs/es9356.h208
-rw-r--r--sound/soc/codecs/framer-codec.c8
-rw-r--r--sound/soc/codecs/fs210x.c6
-rw-r--r--sound/soc/codecs/hdac_hdmi.c4
-rw-r--r--sound/soc/codecs/idt821034.c9
-rw-r--r--sound/soc/codecs/isabelle.c2
-rw-r--r--sound/soc/codecs/lm4857.c2
-rw-r--r--sound/soc/codecs/lm49453.c2
-rw-r--r--sound/soc/codecs/lpass-va-macro.c7
-rw-r--r--sound/soc/codecs/max9768.c2
-rw-r--r--sound/soc/codecs/max98088.c4
-rw-r--r--sound/soc/codecs/max98090.c14
-rw-r--r--sound/soc/codecs/max98090.h3
-rw-r--r--sound/soc/codecs/max98095.c2
-rw-r--r--sound/soc/codecs/max98357a.c1
-rw-r--r--sound/soc/codecs/max98363.c17
-rw-r--r--sound/soc/codecs/max98371.c2
-rw-r--r--sound/soc/codecs/max98373-i2c.c5
-rw-r--r--sound/soc/codecs/max98373-sdw.c17
-rw-r--r--sound/soc/codecs/max98388.c5
-rw-r--r--sound/soc/codecs/max98390.c4
-rw-r--r--sound/soc/codecs/max98396.c6
-rw-r--r--sound/soc/codecs/max9850.c2
-rw-r--r--sound/soc/codecs/max98504.c2
-rw-r--r--sound/soc/codecs/max98520.c4
-rw-r--r--sound/soc/codecs/max9860.c2
-rw-r--r--sound/soc/codecs/max9867.c2
-rw-r--r--sound/soc/codecs/max9877.c2
-rw-r--r--sound/soc/codecs/max98925.c2
-rw-r--r--sound/soc/codecs/max98926.c2
-rw-r--r--sound/soc/codecs/max98927.c4
-rw-r--r--sound/soc/codecs/ml26124.c2
-rw-r--r--sound/soc/codecs/mt6351.c1
-rw-r--r--sound/soc/codecs/mt6358.c1
-rw-r--r--sound/soc/codecs/mt6660.c4
-rw-r--r--sound/soc/codecs/nau8325.c2
-rw-r--r--sound/soc/codecs/nau8540.c2
-rw-r--r--sound/soc/codecs/nau8810.c6
-rw-r--r--sound/soc/codecs/nau8821.c2
-rw-r--r--sound/soc/codecs/nau8822.c48
-rw-r--r--sound/soc/codecs/nau8822.h3
-rw-r--r--sound/soc/codecs/nau8824.c2
-rw-r--r--sound/soc/codecs/nau8825.c14
-rw-r--r--sound/soc/codecs/ntp8835.c4
-rw-r--r--sound/soc/codecs/ntp8918.c4
-rw-r--r--sound/soc/codecs/pcm1681.c4
-rw-r--r--sound/soc/codecs/pcm1789-i2c.c2
-rw-r--r--sound/soc/codecs/pcm179x-i2c.c2
-rw-r--r--sound/soc/codecs/pcm186x-i2c.c8
-rw-r--r--sound/soc/codecs/pcm3168a-i2c.c3
-rw-r--r--sound/soc/codecs/pcm3168a.c30
-rw-r--r--sound/soc/codecs/pcm512x-i2c.c14
-rw-r--r--sound/soc/codecs/pcm512x.c2
-rw-r--r--sound/soc/codecs/pcm6240.c80
-rw-r--r--sound/soc/codecs/pcm6240.h2
-rw-r--r--sound/soc/codecs/peb2466.c9
-rw-r--r--sound/soc/codecs/pm4125.c11
-rw-r--r--sound/soc/codecs/rk3328_codec.c54
-rw-r--r--sound/soc/codecs/rt1011.c2
-rw-r--r--sound/soc/codecs/rt1015.c2
-rw-r--r--sound/soc/codecs/rt1016.c2
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c17
-rw-r--r--sound/soc/codecs/rt1019.c2
-rw-r--r--sound/soc/codecs/rt1305.c4
-rw-r--r--sound/soc/codecs/rt1308-sdw.c17
-rw-r--r--sound/soc/codecs/rt1308.c2
-rw-r--r--sound/soc/codecs/rt1316-sdw.c17
-rw-r--r--sound/soc/codecs/rt1318-sdw.c17
-rw-r--r--sound/soc/codecs/rt1318.c2
-rw-r--r--sound/soc/codecs/rt1320-sdw.c17
-rw-r--r--sound/soc/codecs/rt274.c4
-rw-r--r--sound/soc/codecs/rt286.c6
-rw-r--r--sound/soc/codecs/rt298.c4
-rw-r--r--sound/soc/codecs/rt5514.c2
-rw-r--r--sound/soc/codecs/rt5575-spi.c2
-rw-r--r--sound/soc/codecs/rt5575.c2
-rw-r--r--sound/soc/codecs/rt5616.c6
-rw-r--r--sound/soc/codecs/rt5631.c10
-rw-r--r--sound/soc/codecs/rt5640.c43
-rw-r--r--sound/soc/codecs/rt5645.c10
-rw-r--r--sound/soc/codecs/rt5645.h1
-rw-r--r--sound/soc/codecs/rt5651.c2
-rw-r--r--sound/soc/codecs/rt5659.c4
-rw-r--r--sound/soc/codecs/rt5660.c2
-rw-r--r--sound/soc/codecs/rt5663.c4
-rw-r--r--sound/soc/codecs/rt5665.c4
-rw-r--r--sound/soc/codecs/rt5668.c4
-rw-r--r--sound/soc/codecs/rt5670.c6
-rw-r--r--sound/soc/codecs/rt5677.c2
-rw-r--r--sound/soc/codecs/rt5682-i2c.c4
-rw-r--r--sound/soc/codecs/rt5682-sdw.c14
-rw-r--r--sound/soc/codecs/rt5682.c14
-rw-r--r--sound/soc/codecs/rt5682s.c4
-rw-r--r--sound/soc/codecs/rt700-sdw.c17
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c15
-rw-r--r--sound/soc/codecs/rt711-sdw.c15
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c18
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c15
-rw-r--r--sound/soc/codecs/rt712-sdca.c11
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c17
-rw-r--r--sound/soc/codecs/rt715-sdw.c17
-rw-r--r--sound/soc/codecs/rt721-sdca-sdw.c15
-rw-r--r--sound/soc/codecs/rt722-sdca-sdw.c54
-rw-r--r--sound/soc/codecs/rt722-sdca.c369
-rw-r--r--sound/soc/codecs/rt722-sdca.h27
-rw-r--r--sound/soc/codecs/rt9123.c1
-rw-r--r--sound/soc/codecs/rt9123p.c1
-rw-r--r--sound/soc/codecs/rtq9124.c1
-rw-r--r--sound/soc/codecs/rtq9128.c1
-rw-r--r--sound/soc/codecs/sdw-mockup.c1
-rw-r--r--sound/soc/codecs/sgtl5000.c4
-rw-r--r--sound/soc/codecs/sigmadsp.c4
-rw-r--r--sound/soc/codecs/simple-amplifier.c917
-rw-r--r--sound/soc/codecs/sma1303.c5
-rw-r--r--sound/soc/codecs/sma1307.c39
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c3
-rw-r--r--sound/soc/codecs/ssm2518.c2
-rw-r--r--sound/soc/codecs/ssm2602-i2c.c6
-rw-r--r--sound/soc/codecs/ssm4567.c2
-rw-r--r--sound/soc/codecs/sta32x.c6
-rw-r--r--sound/soc/codecs/sta350.c8
-rw-r--r--sound/soc/codecs/sta529.c2
-rw-r--r--sound/soc/codecs/tac5xx2-sdw.c2039
-rw-r--r--sound/soc/codecs/tac5xx2.h259
-rw-r--r--sound/soc/codecs/tas2552.c2
-rw-r--r--sound/soc/codecs/tas2562.c11
-rw-r--r--sound/soc/codecs/tas2764.c2
-rw-r--r--sound/soc/codecs/tas2770.c2
-rw-r--r--sound/soc/codecs/tas2780.c2
-rw-r--r--sound/soc/codecs/tas2781-fmwlib.c7
-rw-r--r--sound/soc/codecs/tas2781-i2c.c49
-rw-r--r--sound/soc/codecs/tas2783-sdw.c97
-rw-r--r--sound/soc/codecs/tas2783.h8
-rw-r--r--sound/soc/codecs/tas5086.c12
-rw-r--r--sound/soc/codecs/tas571x.c14
-rw-r--r--sound/soc/codecs/tas5720.c6
-rw-r--r--sound/soc/codecs/tas5805m.c2
-rw-r--r--sound/soc/codecs/tas6424.c2
-rw-r--r--sound/soc/codecs/tas675x.c2194
-rw-r--r--sound/soc/codecs/tas675x.h367
-rw-r--r--sound/soc/codecs/tda7419.c2
-rw-r--r--sound/soc/codecs/tfa9879.c2
-rw-r--r--sound/soc/codecs/tlv320adc3xxx.c6
-rw-r--r--sound/soc/codecs/tlv320adcx140.c8
-rw-r--r--sound/soc/codecs/tlv320aic23-i2c.c4
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c16
-rw-r--r--sound/soc/codecs/tlv320aic32x4-i2c.c6
-rw-r--r--sound/soc/codecs/tlv320aic3x-i2c.c10
-rw-r--r--sound/soc/codecs/tlv320aic3x.c32
-rw-r--r--sound/soc/codecs/ts3a227e.c2
-rw-r--r--sound/soc/codecs/tscs42xx.c4
-rw-r--r--sound/soc/codecs/tscs454.c2
-rw-r--r--sound/soc/codecs/uda1334.c1
-rw-r--r--sound/soc/codecs/uda1342.c2
-rw-r--r--sound/soc/codecs/uda1380.c15
-rw-r--r--sound/soc/codecs/wcd937x.c11
-rw-r--r--sound/soc/codecs/wcd938x.c11
-rw-r--r--sound/soc/codecs/wcd939x.c11
-rw-r--r--sound/soc/codecs/wm1250-ev1.c2
-rw-r--r--sound/soc/codecs/wm2000.c2
-rw-r--r--sound/soc/codecs/wm2200.c2
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8510.c3
-rw-r--r--sound/soc/codecs/wm8523.c3
-rw-r--r--sound/soc/codecs/wm8524.c1
-rw-r--r--sound/soc/codecs/wm8580.c5
-rw-r--r--sound/soc/codecs/wm8711.c3
-rw-r--r--sound/soc/codecs/wm8728.c3
-rw-r--r--sound/soc/codecs/wm8731-i2c.c3
-rw-r--r--sound/soc/codecs/wm8731-spi.c1
-rw-r--r--sound/soc/codecs/wm8737.c3
-rw-r--r--sound/soc/codecs/wm8741.c2
-rw-r--r--sound/soc/codecs/wm8750.c4
-rw-r--r--sound/soc/codecs/wm8753.c3
-rw-r--r--sound/soc/codecs/wm8770.c1
-rw-r--r--sound/soc/codecs/wm8776.c5
-rw-r--r--sound/soc/codecs/wm8804-i2c.c2
-rw-r--r--sound/soc/codecs/wm8900.c8
-rw-r--r--sound/soc/codecs/wm8903.c2
-rw-r--r--sound/soc/codecs/wm8904.c6
-rw-r--r--sound/soc/codecs/wm8940.c2
-rw-r--r--sound/soc/codecs/wm8955.c2
-rw-r--r--sound/soc/codecs/wm8960.c2
-rw-r--r--sound/soc/codecs/wm8961.c2
-rw-r--r--sound/soc/codecs/wm8962.c2
-rw-r--r--sound/soc/codecs/wm8971.c2
-rw-r--r--sound/soc/codecs/wm8974.c2
-rw-r--r--sound/soc/codecs/wm8978.c2
-rw-r--r--sound/soc/codecs/wm8983.c2
-rw-r--r--sound/soc/codecs/wm8985.c4
-rw-r--r--sound/soc/codecs/wm8988.c2
-rw-r--r--sound/soc/codecs/wm8990.c2
-rw-r--r--sound/soc/codecs/wm8991.c2
-rw-r--r--sound/soc/codecs/wm8993.c2
-rw-r--r--sound/soc/codecs/wm8995.c4
-rw-r--r--sound/soc/codecs/wm8996.c2
-rw-r--r--sound/soc/codecs/wm9081.c8
-rw-r--r--sound/soc/codecs/wm9090.c4
-rw-r--r--sound/soc/codecs/wm_adsp.c10
-rw-r--r--sound/soc/codecs/wsa881x.c45
289 files changed, 9163 insertions, 1540 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 269c31ce0814..252f683be3c1 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -93,6 +93,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS4271_I2C
imply SND_SOC_CS4271_SPI
imply SND_SOC_CS42XX8_I2C
+ imply SND_SOC_CS42XX8_SPI
imply SND_SOC_CS43130
imply SND_SOC_CS4341
imply SND_SOC_CS4349
@@ -122,6 +123,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_ES8328_I2C
imply SND_SOC_ES8375
imply SND_SOC_ES8389
+ imply SND_SOC_ES9356
imply SND_SOC_ES7134
imply SND_SOC_ES7241
imply SND_SOC_FRAMER
@@ -264,6 +266,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_STA529
imply SND_SOC_STAC9766
imply SND_SOC_STI_SAS
+ imply SND_SOC_TAC5XX2_SDW
imply SND_SOC_TAS2552
imply SND_SOC_TAS2562
imply SND_SOC_TAS2764
@@ -275,6 +278,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TAS571X
imply SND_SOC_TAS5720
imply SND_SOC_TAS6424
+ imply SND_SOC_TAS675X
imply SND_SOC_TDA7419
imply SND_SOC_TFA9879
imply SND_SOC_TFA989X
@@ -884,7 +888,7 @@ config SND_SOC_CS35L56_SPI
config SND_SOC_CS35L56_SDW
tristate "Cirrus Logic CS35L56 CODEC (SDW)"
depends on SOUNDWIRE
- select REGMAP
+ select REGMAP_SOUNDWIRE
select SND_SOC_CS35L56
select SND_SOC_CS35L56_SHARED
help
@@ -1074,6 +1078,12 @@ config SND_SOC_CS4271_SPI
config SND_SOC_CS42XX8
tristate
+config SND_SOC_CS42XX8_SPI
+ tristate "Cirrus Logic CS42448/CS42888 CODEC (SPI)"
+ depends on SPI_MASTER
+ select SND_SOC_CS42XX8
+ select REGMAP_SPI
+
config SND_SOC_CS42XX8_I2C
tristate "Cirrus Logic CS42448/CS42888 CODEC (I2C)"
depends on I2C
@@ -1297,6 +1307,13 @@ config SND_SOC_ES8389
tristate "Everest Semi ES8389 CODEC"
depends on I2C
+config SND_SOC_ES9356
+ tristate "Everest Semi ES9356 CODEC SDW"
+ depends on SND_SOC_SDCA
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
config SND_SOC_FRAMER
tristate "Framer codec"
depends on GENERIC_FRAMER
@@ -2136,6 +2153,16 @@ config SND_SOC_STAC9766
config SND_SOC_STI_SAS
tristate "codec Audio support for STI SAS codec"
+config SND_SOC_TAC5XX2_SDW
+ tristate "Texas Instruments TAC5XX2 SoundWire Smart Amplifier"
+ depends on SOUNDWIRE
+ depends on SND_SOC_SDCA
+ help
+ This option enables support for Texas Instruments TAC5XX2 family
+ of SoundWire Smart Amplifiers. This includes TAC5572, TAC5672,
+ TAC5682 and TAS2883. To compile this driver as a module, choose
+ M here: the module will be called snd-soc-tac5xx2.
+
config SND_SOC_TAS2552
tristate "Texas Instruments TAS2552 Mono Audio amplifier"
depends on I2C
@@ -2234,6 +2261,17 @@ config SND_SOC_TAS6424
Enable support for Texas Instruments TAS6424 high-efficiency
digital input quad-channel Class-D audio power amplifiers.
+config SND_SOC_TAS675X
+ tristate "Texas Instruments TAS675x Quad-Channel Audio Amplifier"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Enable support for Texas Instruments TAS675x quad-channel Class-D
+ audio power amplifiers (TAS6754, TAS67524). The devices support I2S
+ and TDM interfaces with real-time voltage and current sense feedback
+ via SDOUT, and provide DC/AC/real-time load diagnostics for speaker
+ monitoring.
+
config SND_SOC_TDA7419
tristate "ST TDA7419 audio processor"
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 172861d17cfd..aa0396e5b575 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -103,6 +103,7 @@ snd-soc-cs4271-i2c-y := cs4271-i2c.o
snd-soc-cs4271-spi-y := cs4271-spi.o
snd-soc-cs42xx8-y := cs42xx8.o
snd-soc-cs42xx8-i2c-y := cs42xx8-i2c.o
+snd-soc-cs42xx8-spi-y := cs42xx8-spi.o
snd-soc-cs43130-y := cs43130.o
snd-soc-cs4341-y := cs4341.o
snd-soc-cs4349-y := cs4349.o
@@ -138,6 +139,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-es9356-y := es9356.o
snd-soc-framer-y := framer-codec.o
snd-soc-fs-amp-lib-y := fs-amp-lib.o
snd-soc-fs210x-y := fs210x.o
@@ -313,11 +315,13 @@ snd-soc-sta350-y := sta350.o
snd-soc-sta529-y := sta529.o
snd-soc-stac9766-y := stac9766.o
snd-soc-sti-sas-y := sti-sas.o
+snd-soc-tac5xx2-sdw-y := tac5xx2-sdw.o
snd-soc-tas5086-y := tas5086.o
snd-soc-tas571x-y := tas571x.o
snd-soc-tas5720-y := tas5720.o
snd-soc-tas5805m-y := tas5805m.o
snd-soc-tas6424-y := tas6424.o
+snd-soc-tas675x-y := tas675x.o
snd-soc-tda7419-y := tda7419.o
snd-soc-tas2770-y := tas2770.o
snd-soc-tas2781-comlib-y := tas2781-comlib.o
@@ -540,6 +544,7 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o
obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o
obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
+obj-$(CONFIG_SND_SOC_CS42XX8_SPI) += snd-soc-cs42xx8-spi.o
obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o
obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
@@ -575,6 +580,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_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
obj-$(CONFIG_SND_SOC_FS210X) += snd-soc-fs210x.o
@@ -746,6 +752,7 @@ obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o
obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o
obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o
+obj-$(CONFIG_SND_SOC_TAC5XX2_SDW) += snd-soc-tac5xx2-sdw.o
obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o
obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o
obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o
@@ -760,6 +767,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o
obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o
obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o
obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o
+obj-$(CONFIG_SND_SOC_TAS675X) += snd-soc-tas675x.o
obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o
obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o
obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o
diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c
index 6aa168e01fbb..d4d6560bdb80 100644
--- a/sound/soc/codecs/ad193x-i2c.c
+++ b/sound/soc/codecs/ad193x-i2c.c
@@ -14,8 +14,8 @@
#include "ad193x.h"
static const struct i2c_device_id ad193x_id[] = {
- { "ad1936", AD193X },
- { "ad1937", AD193X },
+ { .name = "ad1936", .driver_data = AD193X },
+ { .name = "ad1937", .driver_data = AD193X },
{ }
};
MODULE_DEVICE_TABLE(i2c, ad193x_id);
diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c
index 73f83be38f74..bdb3e3a8509d 100644
--- a/sound/soc/codecs/adau1372-i2c.c
+++ b/sound/soc/codecs/adau1372-i2c.c
@@ -7,7 +7,6 @@
*/
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -21,7 +20,7 @@ static int adau1372_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id adau1372_i2c_ids[] = {
- { "adau1372" },
+ { .name = "adau1372" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids);
diff --git a/sound/soc/codecs/adau1372-spi.c b/sound/soc/codecs/adau1372-spi.c
index 656bd1fabeb3..a12961e2fb1e 100644
--- a/sound/soc/codecs/adau1372-spi.c
+++ b/sound/soc/codecs/adau1372-spi.c
@@ -6,7 +6,6 @@
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c
index d7363f9d53bb..cc174ec3a1f8 100644
--- a/sound/soc/codecs/adau1372.c
+++ b/sound/soc/codecs/adau1372.c
@@ -11,7 +11,6 @@
#include <linux/gpio/consumer.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/pm.h>
#include <linux/slab.h>
@@ -813,6 +812,11 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable)
if (adau1372->use_pll) {
ret = adau1372_enable_pll(adau1372);
if (ret) {
+ if (!adau1372->pd_gpio)
+ regmap_update_bits(adau1372->regmap,
+ ADAU1372_REG_CLK_CTRL,
+ ADAU1372_CLK_CTRL_PLL_EN,
+ 0);
regcache_cache_only(adau1372->regmap, true);
if (adau1372->pd_gpio)
gpiod_set_value(adau1372->pd_gpio, 1);
diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c
index 16b9b2658341..5dd961b233ce 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -1599,7 +1599,7 @@ static int adau1373_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id adau1373_i2c_id[] = {
- { "adau1373" },
+ { .name = "adau1373" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id);
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index 6876462d8bdb..329ab01b62c0 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -860,10 +860,10 @@ exit_regulators_disable:
}
static const struct i2c_device_id adau1701_i2c_id[] = {
- { "adau1401" },
- { "adau1401a" },
- { "adau1701" },
- { "adau1702" },
+ { .name = "adau1401" },
+ { .name = "adau1401a" },
+ { .name = "adau1701" },
+ { .name = "adau1702" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index eba7e4f42c78..ae73136d0a6e 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -7,7 +7,6 @@
*/
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -33,10 +32,10 @@ static void adau1761_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id adau1761_i2c_ids[] = {
- { "adau1361", ADAU1361 },
- { "adau1461", ADAU1761 },
- { "adau1761", ADAU1761 },
- { "adau1961", ADAU1361 },
+ { .name = "adau1361", .driver_data = ADAU1361 },
+ { .name = "adau1461", .driver_data = ADAU1761 },
+ { .name = "adau1761", .driver_data = ADAU1761 },
+ { .name = "adau1961", .driver_data = ADAU1361 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index 7c9242c2ff94..eb6f63d63783 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -6,7 +6,6 @@
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index cb67fde8d9a8..3ab624417f12 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -7,7 +7,6 @@
*/
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -33,8 +32,8 @@ static void adau1781_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id adau1781_i2c_ids[] = {
- { "adau1381", ADAU1381 },
- { "adau1781", ADAU1781 },
+ { .name = "adau1381", .driver_data = ADAU1381 },
+ { .name = "adau1781", .driver_data = ADAU1781 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 1a09633d5a88..0e6d42b10077 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -6,7 +6,6 @@
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c
index 441c8079246a..d1c6c4ddf506 100644
--- a/sound/soc/codecs/adau1977-i2c.c
+++ b/sound/soc/codecs/adau1977-i2c.c
@@ -7,7 +7,6 @@
*/
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/soc.h>
@@ -28,9 +27,9 @@ static int adau1977_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id adau1977_i2c_ids[] = {
- { "adau1977", ADAU1977 },
- { "adau1978", ADAU1978 },
- { "adau1979", ADAU1978 },
+ { .name = "adau1977", .driver_data = ADAU1977 },
+ { .name = "adau1978", .driver_data = ADAU1978 },
+ { .name = "adau1979", .driver_data = ADAU1978 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids);
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
index e7e95e5d1911..878cde9d1014 100644
--- a/sound/soc/codecs/adau1977-spi.c
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -6,7 +6,6 @@
* Author: Lars-Peter Clausen <lars@metafoo.de>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/of.h>
diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c
index 45a5d2dcc0f2..92b226b8b4bb 100644
--- a/sound/soc/codecs/adau7118-hw.c
+++ b/sound/soc/codecs/adau7118-hw.c
@@ -6,7 +6,6 @@
// Copyright 2019 Analog Devices Inc.
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include "adau7118.h"
diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c
index f9dc8f4ef9a4..7cccb4c6cff7 100644
--- a/sound/soc/codecs/adau7118-i2c.c
+++ b/sound/soc/codecs/adau7118-i2c.c
@@ -68,8 +68,8 @@ static const struct of_device_id adau7118_of_match[] = {
MODULE_DEVICE_TABLE(of, adau7118_of_match);
static const struct i2c_device_id adau7118_id[] = {
- {"adau7118"},
- {}
+ { .name = "adau7118" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adau7118_id);
diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c
index 8b96c41f0354..9e5f8e866acb 100644
--- a/sound/soc/codecs/adav803.c
+++ b/sound/soc/codecs/adav803.c
@@ -14,7 +14,7 @@
#include "adav80x.h"
static const struct i2c_device_id adav803_id[] = {
- { "adav803" },
+ { .name = "adav803" },
{ }
};
MODULE_DEVICE_TABLE(i2c, adav803_id);
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index a33cb329865c..6ea7cb78cd44 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -5,7 +5,6 @@
* Copyright (c) 2009 Daniel Mack <daniel@caiaq.de>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/spi/spi.h>
diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c
index 23e868e4e3fb..03dadba0b038 100644
--- a/sound/soc/codecs/ak4118.c
+++ b/sound/soc/codecs/ak4118.c
@@ -395,8 +395,8 @@ MODULE_DEVICE_TABLE(of, ak4118_of_match);
#endif
static const struct i2c_device_id ak4118_id_table[] = {
- { "ak4118" },
- {}
+ { .name = "ak4118" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ak4118_id_table);
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index aadc46a47280..37555674cd15 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -430,7 +430,7 @@ static int ak4535_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id ak4535_i2c_id[] = {
- { "ak4535" },
+ { .name = "ak4535" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id);
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index de9e43185555..3e0696b5abf5 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -303,7 +303,7 @@ static const struct of_device_id ak4613_of_match[] = {
MODULE_DEVICE_TABLE(of, ak4613_of_match);
static const struct i2c_device_id ak4613_i2c_id[] = {
- { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+ { .name = "ak4613", .driver_data = (kernel_ulong_t)&ak4613_regmap_cfg },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
@@ -748,11 +748,6 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd,
return 0;
}
-/*
- * Select below from Sound Card, not Auto
- * SND_SOC_DAIFMT_CBC_CFC
- * SND_SOC_DAIFMT_CBP_CFP
- */
static const u64 ak4613_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J;
diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c
index daf6e15b7077..d9c9f6b20028 100644
--- a/sound/soc/codecs/ak4619.c
+++ b/sound/soc/codecs/ak4619.c
@@ -778,17 +778,13 @@ static int ak4619_dai_startup(struct snd_pcm_substream *substream,
}
static u64 ak4619_dai_formats[] = {
- /*
- * Select below from Sound Card, not here
- * SND_SOC_DAIFMT_CBC_CFC
- * SND_SOC_DAIFMT_CBP_CFP
- */
-
/* First Priority */
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J,
/* Second Priority */
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
};
@@ -831,7 +827,7 @@ static const struct of_device_id ak4619_of_match[] = {
MODULE_DEVICE_TABLE(of, ak4619_of_match);
static const struct i2c_device_id ak4619_i2c_id[] = {
- { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg },
+ { .name = "ak4619", .driver_data = (kernel_ulong_t)&ak4619_regmap_cfg },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id);
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index fe035d2fc913..08ec2035b270 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -672,10 +672,10 @@ static const struct of_device_id ak4642_of_match[] = {
MODULE_DEVICE_TABLE(of, ak4642_of_match);
static const struct i2c_device_id ak4642_i2c_id[] = {
- { "ak4642", (kernel_ulong_t)&ak4642_drvdata },
- { "ak4643", (kernel_ulong_t)&ak4643_drvdata },
- { "ak4648", (kernel_ulong_t)&ak4648_drvdata },
- {}
+ { .name = "ak4642", .driver_data = (kernel_ulong_t)&ak4642_drvdata },
+ { .name = "ak4643", .driver_data = (kernel_ulong_t)&ak4643_drvdata },
+ { .name = "ak4648", .driver_data = (kernel_ulong_t)&ak4648_drvdata },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id);
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index d545aa2e0a39..aac54e9d54ee 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -646,7 +646,7 @@ static int ak4671_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id ak4671_i2c_id[] = {
- { "ak4671" },
+ { .name = "ak4671" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id);
diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c
index ec229b315f9f..f5a666c49361 100644
--- a/sound/soc/codecs/alc5623.c
+++ b/sound/soc/codecs/alc5623.c
@@ -963,10 +963,10 @@ static const struct regmap_config alc5623_regmap = {
};
static const struct i2c_device_id alc5623_i2c_table[] = {
- {"alc5621", 0x21},
- {"alc5622", 0x22},
- {"alc5623", 0x23},
- {}
+ { .name = "alc5621", .driver_data = 0x21 },
+ { .name = "alc5622", .driver_data = 0x22 },
+ { .name = "alc5623", .driver_data = 0x23 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table);
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 72f4622204ff..2fd3dd7ef8b3 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1092,8 +1092,8 @@ static const struct regmap_config alc5632_regmap = {
};
static const struct i2c_device_id alc5632_i2c_table[] = {
- {"alc5632", 0x5c},
- {}
+ { .name = "alc5632", .driver_data = 0x5c },
+ { }
};
MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table);
diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c
index 066e401912b0..964b9a5b2990 100644
--- a/sound/soc/codecs/audio-iio-aux.c
+++ b/sound/soc/codecs/audio-iio-aux.c
@@ -9,7 +9,6 @@
#include <linux/cleanup.h>
#include <linux/iio/consumer.h>
#include <linux/minmax.h>
-#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/string_helpers.h>
diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c
index 37ca42a25889..020213e0ca4b 100644
--- a/sound/soc/codecs/aw87390.c
+++ b/sound/soc/codecs/aw87390.c
@@ -599,8 +599,8 @@ static const struct of_device_id aw87390_of_match[] = {
MODULE_DEVICE_TABLE(of, aw87390_of_match);
static const struct i2c_device_id aw87390_i2c_id[] = {
- { AW87390_I2C_NAME },
- { AW87391_I2C_NAME },
+ { .name = AW87390_I2C_NAME },
+ { .name = AW87391_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id);
diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c
index 8c5bb3ea0227..d5e886a8f106 100644
--- a/sound/soc/codecs/aw88081.c
+++ b/sound/soc/codecs/aw88081.c
@@ -1137,6 +1137,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg
static int aw88081_request_firmware_file(struct aw88081 *aw88081)
{
const struct firmware *cont = NULL;
+ struct aw_container *aw_cfg;
int ret;
aw88081->aw_pa->fw_status = AW88081_DEV_FW_FAILED;
@@ -1148,13 +1149,16 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081)
dev_dbg(aw88081->aw_pa->dev, "loaded %s - size: %zu\n",
AW88081_ACF_FILE, cont ? cont->size : 0);
- aw88081->aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
- if (!aw88081->aw_cfg) {
+ aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw_cfg) {
release_firmware(cont);
return -ENOMEM;
}
- aw88081->aw_cfg->len = (int)cont->size;
- memcpy(aw88081->aw_cfg->data, cont->data, cont->size);
+ aw_cfg->len = (int)cont->size;
+ memcpy(aw_cfg->data, cont->data, cont->size);
+
+ aw88081->aw_cfg = aw_cfg;
+
release_firmware(cont);
ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg);
@@ -1240,8 +1244,8 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88081 = {
};
static const struct i2c_device_id aw88081_i2c_id[] = {
- { AW88081_I2C_NAME, AW88081},
- { AW88083_I2C_NAME, AW88083},
+ { .name = AW88081_I2C_NAME, .driver_data = AW88081 },
+ { .name = AW88083_I2C_NAME, .driver_data = AW88083 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id);
diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c
index ea277a940c44..3f15f4ac51f7 100644
--- a/sound/soc/codecs/aw88166.c
+++ b/sound/soc/codecs/aw88166.c
@@ -1801,7 +1801,7 @@ static int aw88166_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id aw88166_i2c_id[] = {
- { AW88166_I2C_NAME },
+ { .name = AW88166_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88166_i2c_id);
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
index a6805d5405cd..549783d3e75e 100644
--- a/sound/soc/codecs/aw88261.c
+++ b/sound/soc/codecs/aw88261.c
@@ -10,9 +10,12 @@
#include <linux/i2c.h>
#include <linux/firmware.h>
+#include <linux/bitops.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
#include "aw88261.h"
#include "aw88395/aw88395_data_type.h"
#include "aw88395/aw88395_device.h"
@@ -27,64 +30,10 @@ static const struct regmap_config aw88261_remap_config = {
static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
{
- struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
- unsigned int real_value, volume;
- unsigned int reg_value;
-
- volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL);
- real_value = DB_TO_REG_VAL(volume);
-
- regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, &reg_value);
-
- real_value = (real_value | (reg_value & AW88261_VOL_START_MASK));
-
- dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value);
-
- regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
-}
-
-static void aw88261_dev_fade_in(struct aw_device *aw_dev)
-{
- struct aw_volume_desc *desc = &aw_dev->volume_desc;
- int fade_in_vol = desc->ctl_volume;
- int fade_step = aw_dev->fade_step;
- int i;
-
- if (fade_step == 0 || aw_dev->fade_in_time == 0) {
- aw88261_dev_set_volume(aw_dev, fade_in_vol);
- return;
- }
-
- for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
- aw88261_dev_set_volume(aw_dev, i);
- usleep_range(aw_dev->fade_in_time,
- aw_dev->fade_in_time + 10);
- }
-
- if (i != fade_in_vol)
- aw88261_dev_set_volume(aw_dev, fade_in_vol);
-}
-
-static void aw88261_dev_fade_out(struct aw_device *aw_dev)
-{
- struct aw_volume_desc *desc = &aw_dev->volume_desc;
- int fade_step = aw_dev->fade_step;
- int i;
-
- if (fade_step == 0 || aw_dev->fade_out_time == 0) {
- aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
- return;
- }
+ unsigned int volume = min(value, (unsigned int)AW88261_MUTE_VOL);
- for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) {
- aw88261_dev_set_volume(aw_dev, i);
- usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
- }
-
- if (i != AW88261_MUTE_VOL) {
- aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
- usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
- }
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL2_REG,
+ ~AW88261_VOL_MASK, DB_TO_REG_VAL(volume));
}
static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
@@ -120,13 +69,13 @@ static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd)
static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
{
if (is_mute) {
- aw88261_dev_fade_out(aw_dev);
+ aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
} else {
regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
- aw88261_dev_fade_in(aw_dev);
+ aw88261_dev_set_volume(aw_dev, aw_dev->volume_desc.ctl_volume);
}
}
@@ -151,21 +100,21 @@ static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
if (ret)
return ret;
if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
- dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ dev_dbg(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
return -EINVAL;
}
return ret;
}
-static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
+static int aw88261_dev_check_pll(struct aw_device *aw_dev)
{
int ret, i;
for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
ret = aw88261_dev_get_iis_status(aw_dev);
if (ret) {
- dev_err(aw_dev->dev, "mode1 iis signal check error");
+ dev_dbg(aw_dev->dev, "mode1 iis signal check error");
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
} else {
return ret;
@@ -175,71 +124,74 @@ static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
return -EPERM;
}
-static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev)
+static int aw88261_dev_configure_syspll(struct aw88261 *aw88261)
{
- unsigned int reg_val;
- int ret, i;
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
- ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, &reg_val);
+ /* Configure TDM slots (I2S is represented as no slots) */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+ ~AW88261_SLOT_NUM_MASK, aw88261->slot_num_value);
if (ret)
return ret;
- reg_val &= (~AW88261_CCO_MUX_MASK);
- if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) {
- dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
- return -EPERM;
- }
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+ ~AW88261_I2S_TX_SLOTVLD_MASK,
+ aw88261->tx_slotvld_mask);
+ if (ret)
+ return ret;
- /* change mode2 */
- ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
- ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE);
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+ ~AW88261_I2S_RXL_SLOTVLD_MASK,
+ aw88261->rxl_slotvld_mask);
if (ret)
return ret;
- for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
- ret = aw88261_dev_get_iis_status(aw_dev);
- if (ret) {
- dev_err(aw_dev->dev, "mode2 iis signal check error");
- usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
- } else {
- break;
- }
- }
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG,
+ ~AW88261_I2S_RXR_SLOTVLD_MASK,
+ aw88261->rxr_slotvld_mask);
+ if (ret)
+ return ret;
- /* change mode1 */
+ /* PLL divider must be used for 8/16/32 kHz modes */
ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
- ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE);
- if (ret == 0) {
- usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
- for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
- ret = aw88261_dev_check_mode1_pll(aw_dev);
- if (ret) {
- dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
- usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
- } else {
- break;
- }
- }
- }
+ ~AW88261_CCO_MUX_MASK, aw88261->cco_mux_value);
+ if (ret)
+ return ret;
- return ret;
-}
+ /* The word clock (WCK) defines the beginning of a frame */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+ ~AW88261_I2SSR_MASK, aw88261->sr_value);
+ if (ret)
+ return ret;
-static int aw88261_dev_check_syspll(struct aw_device *aw_dev)
-{
- int ret;
+ /* The bit clock (BCK) defines the length of a frame */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+ ~AW88261_I2SBCK_MASK,
+ (aw88261->tdm_bck_value != AW88261_TDM_BCK_UNSET)
+ ? aw88261->tdm_bck_value : aw88261->bck_value);
+ if (ret)
+ return ret;
- ret = aw88261_dev_check_mode1_pll(aw_dev);
- if (ret) {
- dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
- ret = aw88261_dev_check_mode2_pll(aw_dev);
- if (ret) {
- dev_err(aw_dev->dev, "mode2 check iis failed");
- return ret;
- }
- }
+ /* The logical frame size is the width of data for 1 slot */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+ ~AW88261_I2SFS_MASK, aw88261->fs_value);
+ if (ret)
+ return ret;
- return ret;
+ /* The I2S interface mode (Philips standard, LSB/MSB justified) */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG,
+ ~AW88261_I2SMD_MASK, aw88261->md_value);
+ if (ret)
+ return ret;
+
+ /* The polarity of the bit clock (BCK) */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_BCKINV_MASK, aw88261->bck_inv_value);
+ if (ret)
+ return ret;
+
+ return aw88261_dev_check_pll(aw_dev);
}
static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
@@ -256,7 +208,7 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
& AW88261_BIT_SYSST_CHECK;
if (check_val != AW88261_BIT_SYSST_CHECK) {
- dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+ dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
reg_val, AW88261_BIT_SYSST_CHECK);
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
} else {
@@ -284,22 +236,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261)
if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
/* set FORCE_PWM */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
- AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
+ ~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
/* set BOOST_OS_WIDTH */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
- AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
+ ~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
/* set BURST_LOOPR */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
- AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
+ ~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
/* set RSQN_DLY */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
- AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
+ ~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
/* set BURST_SSMODE */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
- AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
+ ~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
/* set BST_BURST */
regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
- AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
+ ~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
} else {
dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
}
@@ -550,7 +502,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
int ret;
if (aw_dev->status == AW88261_DEV_PW_ON) {
- dev_info(aw_dev->dev, "already power on");
+ dev_dbg(aw_dev->dev, "already power on");
return 0;
}
@@ -558,9 +510,9 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
aw88261_dev_pwd(aw_dev, false);
usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
- ret = aw88261_dev_check_syspll(aw_dev);
+ ret = aw88261_dev_configure_syspll(aw88261);
if (ret) {
- dev_err(aw_dev->dev, "pll check failed cannot start");
+ dev_dbg(aw_dev->dev, "pll check failed");
goto pll_check_fail;
}
@@ -571,7 +523,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261)
/* check i2s status */
ret = aw88261_dev_check_sysst(aw_dev);
if (ret) {
- dev_err(aw_dev->dev, "sysst check failed");
+ dev_dbg(aw_dev->dev, "sysst check failed");
goto sysst_check_fail;
}
@@ -672,31 +624,25 @@ static void aw88261_start_pa(struct aw88261 *aw88261)
for (i = 0; i < AW88261_START_RETRIES; i++) {
ret = aw88261_reg_update(aw88261, aw88261->phase_sync);
if (ret) {
- dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+ dev_dbg(aw88261->aw_pa->dev,
+ "aw88261_reg_update failed, cnt:%d, ret:%d\n", i, ret);
continue;
}
ret = aw88261_dev_start(aw88261);
if (ret) {
- dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i);
+ dev_dbg(aw88261->aw_pa->dev,
+ "aw88261_dev_start failed, cnt:%d, ret:%d\n", i, ret);
continue;
} else {
- dev_info(aw88261->aw_pa->dev, "start success\n");
+ dev_dbg(aw88261->aw_pa->dev, "start success\n");
break;
}
}
+ if (ret != 0)
+ dev_err(aw88261->aw_pa->dev, "start failure (%d)\n", ret);
}
-static void aw88261_startup_work(struct work_struct *work)
-{
- struct aw88261 *aw88261 =
- container_of(work, struct aw88261, start_work.work);
-
- mutex_lock(&aw88261->lock);
- aw88261_start_pa(aw88261);
- mutex_unlock(&aw88261->lock);
-}
-
-static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
+static void aw88261_start(struct aw88261 *aw88261)
{
if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
return;
@@ -704,104 +650,248 @@ static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
return;
- if (sync_start == AW88261_SYNC_START)
- aw88261_start_pa(aw88261);
- else
- queue_delayed_work(system_dfl_wq,
- &aw88261->start_work,
- AW88261_START_WORK_DELAY_MS);
+ aw88261_start_pa(aw88261);
}
-static struct snd_soc_dai_driver aw88261_dai[] = {
- {
- .name = "aw88261-aif",
- .id = 1,
- .playback = {
- .stream_name = "Speaker_Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AW88261_RATES,
- .formats = AW88261_FORMATS,
- },
- .capture = {
- .stream_name = "Speaker_Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = AW88261_RATES,
- .formats = AW88261_FORMATS,
- },
- },
-};
-
-static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int aw88261_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_component *component = dai->component;
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
- struct aw_device *aw_dev = aw88261->aw_pa;
- ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ aw88261->bck_inv_value = AW88261_BCKINV_INVERTED_VALUE;
+ break;
+ default:
+ dev_err(aw88261->aw_pa->dev, "unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_DSP_A:
+ aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
+ break;
+ case SND_SOC_DAIFMT_MSB:
+ case SND_SOC_DAIFMT_DSP_B:
+ aw88261->md_value = AW88261_I2SMD_MSB_JUSTIFIED_VALUE;
+ break;
+ case SND_SOC_DAIFMT_LSB:
+ aw88261->md_value = AW88261_I2SMD_LSB_JUSTIFIED_VALUE;
+ break;
+ default:
+ dev_err(aw88261->aw_pa->dev, "unsupported DAI format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
return 0;
}
-static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int aw88261_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_component *component = dai->component;
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct aw_device *aw_dev = aw88261->aw_pa;
- int time;
- time = ucontrol->value.integer.value[0];
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ return 0;
- if (time < mc->min || time > mc->max)
+ aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
+ switch (params_rate(params)) {
+ case 8000:
+ aw88261->sr_value = AW88261_I2SSR_8KHZ_VALUE;
+ aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
+ break;
+ case 11025:
+ aw88261->sr_value = AW88261_I2SSR_11P025KHZ_VALUE;
+ break;
+ case 12000:
+ aw88261->sr_value = AW88261_I2SSR_12KHZ_VALUE;
+ break;
+ case 16000:
+ aw88261->sr_value = AW88261_I2SSR_16KHZ_VALUE;
+ aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
+ break;
+ case 22050:
+ aw88261->sr_value = AW88261_I2SSR_22P05KHZ_VALUE;
+ break;
+ case 24000:
+ aw88261->sr_value = AW88261_I2SSR_24KHZ_VALUE;
+ break;
+ case 32000:
+ aw88261->sr_value = AW88261_I2SSR_32KHZ_VALUE;
+ aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE;
+ break;
+ case 44100:
+ aw88261->sr_value = AW88261_I2SSR_44P1KHZ_VALUE;
+ break;
+ case 48000:
+ aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
+ break;
+ case 96000:
+ aw88261->sr_value = AW88261_I2SSR_96KHZ_VALUE;
+ break;
+ case 192000:
+ aw88261->sr_value = AW88261_I2SSR_192KHZ_VALUE;
+ break;
+ default:
+ dev_err(aw88261->aw_pa->dev, "unsupported sample rate %d\n",
+ params_rate(params));
return -EINVAL;
+ }
- if (time != aw_dev->fade_in_time) {
- aw_dev->fade_in_time = time;
- return 1;
+ switch (params_width(params)) {
+ case 16:
+ aw88261->fs_value = AW88261_I2SFS_16_BITS_VALUE;
+ break;
+ case 20:
+ aw88261->fs_value = AW88261_I2SFS_20_BITS_VALUE;
+ break;
+ case 24:
+ aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
+ break;
+ case 32:
+ aw88261->fs_value = AW88261_I2SFS_32_BITS_VALUE;
+ break;
+ default:
+ dev_err(aw88261->aw_pa->dev, "unsupported bit width %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+
+ switch (params_physical_width(params)) {
+ case 16:
+ aw88261->bck_value = AW88261_I2SBCK_32FS_VALUE;
+ break;
+ case 24:
+ aw88261->bck_value = AW88261_I2SBCK_48FS_VALUE;
+ break;
+ case 32:
+ aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
+ break;
+ default:
+ dev_err(aw88261->aw_pa->dev, "unsupported physical bit width %d\n",
+ params_physical_width(params));
+ return -EINVAL;
}
return 0;
}
-static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
+static int aw88261_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct snd_soc_component *component = dai->component;
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
- struct aw_device *aw_dev = aw88261->aw_pa;
+ int chan;
+
+ switch (slots) {
+ case 0:
+ /* Just reset everything TDM related to I2S values */
+ aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
+ aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
+ aw88261->tx_slotvld_mask = 0 << AW88261_I2S_TX_SLOTVLD_START_BIT;
+ aw88261->rxl_slotvld_mask = 0 << AW88261_I2S_RXL_SLOTVLD_START_BIT;
+ aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
+ return 0;
+ case 1:
+ aw88261->slot_num_value = AW88261_SLOT_NUM_TDM1S_VALUE;
+ break;
+ case 2:
+ aw88261->slot_num_value = AW88261_SLOT_NUM_TDM2S_VALUE;
+ break;
+ case 4:
+ aw88261->slot_num_value = AW88261_SLOT_NUM_TDM4S_VALUE;
+ break;
+ case 6:
+ aw88261->slot_num_value = AW88261_SLOT_NUM_TDM6S_VALUE;
+ break;
+ case 8:
+ aw88261->slot_num_value = AW88261_SLOT_NUM_TDM8S_VALUE;
+ break;
+ case 16:
+ aw88261->slot_num_value = AW88261_SLOT_NUM_TDM16S_VALUE;
+ break;
+ default:
+ dev_err(aw88261->aw_pa->dev, "unsupported slot count %d\n", slots);
+ return -EINVAL;
+ }
- ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+ switch (slot_width) {
+ case 16:
+ aw88261->tdm_bck_value = AW88261_I2SBCK_32FS_VALUE;
+ break;
+ case 20:
+ case 24:
+ aw88261->tdm_bck_value = AW88261_I2SBCK_48FS_VALUE;
+ break;
+ case 32:
+ aw88261->tdm_bck_value = AW88261_I2SBCK_64FS_VALUE;
+ break;
+ default:
+ dev_err(aw88261->aw_pa->dev, "unsupported slot width %d\n",
+ slot_width);
+ return -EINVAL;
+ }
- return 0;
-}
+ if (tx_mask != 0) {
+ if ((chan = __ffs(tx_mask)) > 16)
+ return -EINVAL;
-static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
- struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- struct aw_device *aw_dev = aw88261->aw_pa;
- int time;
+ aw88261->tx_slotvld_mask = chan << AW88261_I2S_TX_SLOTVLD_START_BIT;
+ }
- time = ucontrol->value.integer.value[0];
- if (time < mc->min || time > mc->max)
- return -EINVAL;
+ if (rx_mask != 0) {
+ if ((chan = __ffs(rx_mask)) > 16)
+ return -EINVAL;
- if (time != aw_dev->fade_out_time) {
- aw_dev->fade_out_time = time;
- return 1;
+ aw88261->rxl_slotvld_mask = chan << AW88261_I2S_RXL_SLOTVLD_START_BIT;
+ }
+
+ if ((rx_mask & ~BIT(chan)) != 0) {
+ if ((chan = __ffs(rx_mask & ~BIT(chan))) > 16)
+ return -EINVAL;
+
+ aw88261->rxr_slotvld_mask = chan << AW88261_I2S_RXR_SLOTVLD_START_BIT;
}
return 0;
}
+static const struct snd_soc_dai_ops aw88261_dai_ops = {
+ .set_fmt = aw88261_set_fmt,
+ .hw_params = aw88261_hw_params,
+ .set_tdm_slot = aw88261_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver aw88261_dai[] = {
+ {
+ .name = "aw88261-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ .ops = &aw88261_dai_ops,
+ },
+};
+
static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index)
{
/* check the index whether is valid */
@@ -880,7 +970,7 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
if (aw88261->aw_pa->status) {
aw88261_dev_stop(aw88261->aw_pa);
- aw88261_start(aw88261, AW88261_SYNC_START);
+ aw88261_start(aw88261);
}
mutex_unlock(&aw88261->lock);
@@ -895,7 +985,8 @@ static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
- ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+ ucontrol->value.integer.value[0] =
+ (AW88261_MUTE_VOL - vol_desc->ctl_volume) / 2;
return 0;
}
@@ -908,13 +999,13 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- int value;
-
- value = ucontrol->value.integer.value[0];
+ int value = ucontrol->value.integer.value[0];
if (value < mc->min || value > mc->max)
return -EINVAL;
+ value = AW88261_MUTE_VOL - (value * 2);
+
if (vol_desc->ctl_volume != value) {
vol_desc->ctl_volume = value;
aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
@@ -925,48 +1016,18 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
return 0;
}
-static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
- struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
-
- ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step;
-
- return 0;
-}
-
-static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol)
-{
- struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol);
- struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
- struct soc_mixer_control *mc =
- (struct soc_mixer_control *)kcontrol->private_value;
- int value;
-
- value = ucontrol->value.integer.value[0];
- if (value < mc->min || value > mc->max)
- return -EINVAL;
-
- if (aw88261->aw_pa->fade_step != value) {
- aw88261->aw_pa->fade_step = value;
- return 1;
- }
-
- return 0;
-}
+/*
+ * The field contains 4 bits in units of 6dB + 6 bits in units of 0.125dB
+ * which is too precise for TLV (!) so we have to multiply the scale by 2.
+ *
+ * The range is clamped at -90dB to prevent overflowing the 4-bit part.
+ */
+static const DECLARE_TLV_DB_SCALE(volume_tlv, -9000, 25, 0);
static const struct snd_kcontrol_new aw88261_controls[] = {
- SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
- 6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
- aw88261_volume_set),
- SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0,
- aw88261_get_fade_step, aw88261_set_fade_step),
- SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
- aw88261_get_fade_in_time, aw88261_set_fade_in_time),
- SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
- aw88261_get_fade_out_time, aw88261_set_fade_out_time),
+ SOC_SINGLE_EXT_TLV("PCM Playback Volume", AW88261_SYSCTRL2_REG,
+ 6, AW88261_CTL_MAX_VOL, 1,
+ aw88261_volume_get, aw88261_volume_set, volume_tlv),
AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
aw88261_profile_get, aw88261_profile_set),
};
@@ -980,7 +1041,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w,
mutex_lock(&aw88261->lock);
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
- aw88261_start(aw88261, AW88261_ASYNC_START);
+ aw88261_start(aw88261);
break;
case SND_SOC_DAPM_POST_PMD:
aw88261_dev_stop(aw88261->aw_pa);
@@ -1057,8 +1118,6 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg
if (ret)
return ret;
- aw_dev->fade_in_time = AW88261_500_US;
- aw_dev->fade_out_time = AW88261_500_US;
aw_dev->prof_cur = AW88261_INIT_PROFILE;
aw_dev->prof_index = AW88261_INIT_PROFILE;
@@ -1094,6 +1153,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg
static int aw88261_request_firmware_file(struct aw88261 *aw88261)
{
const struct firmware *cont = NULL;
+ struct aw_container *aw_cfg;
const char *fw_name;
int ret;
@@ -1111,15 +1171,17 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261)
dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
fw_name, cont ? cont->size : 0);
- aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
- if (!aw88261->aw_cfg) {
+ aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw_cfg) {
release_firmware(cont);
return -ENOMEM;
}
- aw88261->aw_cfg->len = (int)cont->size;
- memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
+ aw_cfg->len = (int)cont->size;
+ memcpy(aw_cfg->data, cont->data, cont->size);
release_firmware(cont);
+ aw88261->aw_cfg = aw_cfg;
+
ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
if (ret) {
dev_err(aw88261->aw_pa->dev, "load [%s] failed !", fw_name);
@@ -1142,8 +1204,6 @@ static int aw88261_codec_probe(struct snd_soc_component *component)
struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
int ret;
- INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
-
ret = aw88261_request_firmware_file(aw88261);
if (ret)
return dev_err_probe(aw88261->aw_pa->dev, ret,
@@ -1167,16 +1227,8 @@ static int aw88261_codec_probe(struct snd_soc_component *component)
return ret;
}
-static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
-{
- struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
-
- cancel_delayed_work_sync(&aw88261->start_work);
-}
-
static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
.probe = aw88261_codec_probe,
- .remove = aw88261_codec_remove,
};
static void aw88261_parse_channel_dt(struct aw88261 *aw88261)
@@ -1208,7 +1260,7 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
return ret;
}
if (chip_id != AW88261_CHIP_ID) {
- dev_err(&i2c->dev, "unsupported device");
+ dev_err(&i2c->dev, "unsupported device id = %x", chip_id);
return -ENXIO;
}
@@ -1229,8 +1281,7 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct
aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
aw_dev->channel = 0;
aw_dev->fw_status = AW88261_DEV_FW_FAILED;
- aw_dev->fade_step = AW88261_VOLUME_STEP_DB;
- aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
+ aw_dev->volume_desc.ctl_volume = AW88261_CTL_DEFAULT_VOL;
aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
aw88261_parse_channel_dt(aw88261);
@@ -1249,6 +1300,17 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
if (!aw88261)
return -ENOMEM;
+ /* set defaults */
+ aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE;
+ aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE;
+ aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE;
+ aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE;
+ aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE;
+ aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE;
+ aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET;
+ aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE;
+ aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT;
+
mutex_init(&aw88261->lock);
i2c_set_clientdata(i2c, aw88261);
@@ -1274,7 +1336,7 @@ static int aw88261_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id aw88261_i2c_id[] = {
- { "aw88261" },
+ { .name = "aw88261" },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
index 1fee589608d6..270ccf375f36 100644
--- a/sound/soc/codecs/aw88261.h
+++ b/sound/soc/codecs/aw88261.h
@@ -116,6 +116,19 @@
#define AW88261_VCALK_SHIFT (0)
#define AW88261_VCALKL_SHIFT (0)
+#define AW88261_BCKINV_START_BIT (4)
+#define AW88261_BCKINV_BITS_LEN (1)
+#define AW88261_BCKINV_MASK \
+ (~(((1<<AW88261_BCKINV_BITS_LEN)-1) << AW88261_BCKINV_START_BIT))
+
+#define AW88261_BCKINV_NOT_INVERT (0)
+#define AW88261_BCKINV_NOT_INVERT_VALUE \
+ (AW88261_BCKINV_NOT_INVERT << AW88261_BCKINV_START_BIT)
+
+#define AW88261_BCKINV_INVERTED (1)
+#define AW88261_BCKINV_INVERTED_VALUE \
+ (AW88261_BCKINV_INVERTED << AW88261_BCKINV_START_BIT)
+
#define AW88261_AMPPD_START_BIT (1)
#define AW88261_AMPPD_BITS_LEN (1)
#define AW88261_AMPPD_MASK \
@@ -249,7 +262,8 @@
#define AW88261_VOL_MASK \
(~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
-#define AW88261_VOL_DEFAULT_VALUE (0)
+#define AW88261_CTL_MAX_VOL (AW88261_MUTE_VOL / 2)
+#define AW88261_CTL_DEFAULT_VOL (AW88261_CTL_MAX_VOL / 2)
#define AW88261_I2STXEN_START_BIT (6)
#define AW88261_I2STXEN_BITS_LEN (1)
@@ -264,7 +278,148 @@
#define AW88261_I2STXEN_ENABLE_VALUE \
(AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
-#define AW88261_CCO_MUX_START_BIT (14)
+#define AW88261_I2SMD_START_BIT (8)
+#define AW88261_I2SMD_BITS_LEN (2)
+#define AW88261_I2SMD_MASK \
+ (~(((1<<AW88261_I2SMD_BITS_LEN)-1) << AW88261_I2SMD_START_BIT))
+
+#define AW88261_I2SMD_PHILIPS_STANDARD (0)
+#define AW88261_I2SMD_PHILIPS_STANDARD_VALUE \
+ (AW88261_I2SMD_PHILIPS_STANDARD << AW88261_I2SMD_START_BIT)
+
+#define AW88261_I2SMD_MSB_JUSTIFIED (1)
+#define AW88261_I2SMD_MSB_JUSTIFIED_VALUE \
+ (AW88261_I2SMD_MSB_JUSTIFIED << AW88261_I2SMD_START_BIT)
+
+#define AW88261_I2SMD_LSB_JUSTIFIED (2)
+#define AW88261_I2SMD_LSB_JUSTIFIED_VALUE \
+ (AW88261_I2SMD_LSB_JUSTIFIED << AW88261_I2SMD_START_BIT)
+
+#define AW88261_I2SFS_START_BIT (6)
+#define AW88261_I2SFS_BITS_LEN (2)
+#define AW88261_I2SFS_MASK \
+ (~(((1<<AW88261_I2SFS_BITS_LEN)-1)<<AW88261_I2SFS_START_BIT))
+
+#define AW88261_I2SFS_16_BITS (0)
+#define AW88261_I2SFS_16_BITS_VALUE \
+ (AW88261_I2SFS_16_BITS << AW88261_I2SFS_START_BIT)
+#define AW88261_I2SFS_20_BITS (1)
+#define AW88261_I2SFS_20_BITS_VALUE \
+ (AW88261_I2SFS_20_BITS << AW88261_I2SFS_START_BIT)
+#define AW88261_I2SFS_24_BITS (2)
+#define AW88261_I2SFS_24_BITS_VALUE \
+ (AW88261_I2SFS_24_BITS << AW88261_I2SFS_START_BIT)
+#define AW88261_I2SFS_32_BITS (3)
+#define AW88261_I2SFS_32_BITS_VALUE \
+ (AW88261_I2SFS_32_BITS << AW88261_I2SFS_START_BIT)
+
+#define AW88261_I2SBCK_START_BIT (4)
+#define AW88261_I2SBCK_BITS_LEN (2)
+#define AW88261_I2SBCK_MASK \
+ (~(((1<<AW88261_I2SBCK_BITS_LEN)-1) << AW88261_I2SBCK_START_BIT))
+
+#define AW88261_I2SBCK_32FS (0)
+#define AW88261_I2SBCK_32FS_VALUE \
+ (AW88261_I2SBCK_32FS << AW88261_I2SBCK_START_BIT)
+
+#define AW88261_I2SBCK_48FS (1)
+#define AW88261_I2SBCK_48FS_VALUE \
+ (AW88261_I2SBCK_48FS << AW88261_I2SBCK_START_BIT)
+
+#define AW88261_I2SBCK_64FS (2)
+#define AW88261_I2SBCK_64FS_VALUE \
+ (AW88261_I2SBCK_64FS << AW88261_I2SBCK_START_BIT)
+
+#define AW88261_TDM_BCK_UNSET UINT_MAX
+
+#define AW88261_I2SSR_START_BIT (0)
+#define AW88261_I2SSR_BITS_LEN (4)
+#define AW88261_I2SSR_MASK \
+ (~(((1<<AW88261_I2SSR_BITS_LEN)-1) << AW88261_I2SSR_START_BIT))
+
+#define AW88261_I2SSR_8KHZ (0)
+#define AW88261_I2SSR_8KHZ_VALUE \
+ (AW88261_I2SSR_8KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_11P025KHZ (1)
+#define AW88261_I2SSR_11P025KHZ_VALUE \
+ (AW88261_I2SSR_11P025KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_12KHZ (2)
+#define AW88261_I2SSR_12KHZ_VALUE \
+ (AW88261_I2SSR_12KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_16KHZ (3)
+#define AW88261_I2SSR_16KHZ_VALUE \
+ (AW88261_I2SSR_16KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_22P05KHZ (4)
+#define AW88261_I2SSR_22P05KHZ_VALUE \
+ (AW88261_I2SSR_22P05KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_24KHZ (5)
+#define AW88261_I2SSR_24KHZ_VALUE \
+ (AW88261_I2SSR_24KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_32KHZ (6)
+#define AW88261_I2SSR_32KHZ_VALUE \
+ (AW88261_I2SSR_32KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_44P1KHZ (7)
+#define AW88261_I2SSR_44P1KHZ_VALUE \
+ (AW88261_I2SSR_44P1KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_48KHZ (8)
+#define AW88261_I2SSR_48KHZ_VALUE \
+ (AW88261_I2SSR_48KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_96KHZ (9)
+#define AW88261_I2SSR_96KHZ_VALUE \
+ (AW88261_I2SSR_96KHZ << AW88261_I2SSR_START_BIT)
+#define AW88261_I2SSR_192KHZ (10)
+#define AW88261_I2SSR_192KHZ_VALUE \
+ (AW88261_I2SSR_192KHZ << AW88261_I2SSR_START_BIT)
+
+#define AW88261_SLOT_NUM_START_BIT (12)
+#define AW88261_SLOT_NUM_BITS_LEN (3)
+#define AW88261_SLOT_NUM_MASK \
+ (~(((1<<AW88261_SLOT_NUM_BITS_LEN)-1) << AW88261_SLOT_NUM_START_BIT))
+
+#define AW88261_SLOT_NUM_I2S_MODE (0)
+#define AW88261_SLOT_NUM_I2S_MODE_VALUE \
+ (AW88261_SLOT_NUM_I2S_MODE << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM1S (1)
+#define AW88261_SLOT_NUM_TDM1S_VALUE \
+ (AW88261_SLOT_NUM_TDM1S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM2S (2)
+#define AW88261_SLOT_NUM_TDM2S_VALUE \
+ (AW88261_SLOT_NUM_TDM2S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM4S (3)
+#define AW88261_SLOT_NUM_TDM4S_VALUE \
+ (AW88261_SLOT_NUM_TDM4S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM6S (4)
+#define AW88261_SLOT_NUM_TDM6S_VALUE \
+ (AW88261_SLOT_NUM_TDM6S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM8S (5)
+#define AW88261_SLOT_NUM_TDM8S_VALUE \
+ (AW88261_SLOT_NUM_TDM8S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_SLOT_NUM_TDM16S (6)
+#define AW88261_SLOT_NUM_TDM16S_VALUE \
+ (AW88261_SLOT_NUM_TDM16S << AW88261_SLOT_NUM_START_BIT)
+
+#define AW88261_I2S_TX_SLOTVLD_START_BIT (8)
+#define AW88261_I2S_TX_SLOTVLD_BITS_LEN (4)
+#define AW88261_I2S_TX_SLOTVLD_MASK \
+ (~(((1<<AW88261_I2S_TX_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_TX_SLOTVLD_START_BIT))
+
+#define AW88261_I2S_RXR_SLOTVLD_START_BIT (4)
+#define AW88261_I2S_RXR_SLOTVLD_BITS_LEN (4)
+#define AW88261_I2S_RXR_SLOTVLD_MASK \
+ (~(((1<<AW88261_I2S_RXR_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXR_SLOTVLD_START_BIT))
+
+#define AW88261_I2S_RXL_SLOTVLD_START_BIT (0)
+#define AW88261_I2S_RXL_SLOTVLD_BITS_LEN (4)
+#define AW88261_I2S_RXL_SLOTVLD_MASK \
+ (~(((1<<AW88261_I2S_RXL_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXL_SLOTVLD_START_BIT))
+
+#define AW88261_CCO_MUX_START_BIT (6)
#define AW88261_CCO_MUX_BITS_LEN (1)
#define AW88261_CCO_MUX_MASK \
(~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
@@ -370,21 +525,20 @@
#define AW88261_START_RETRIES (5)
#define AW88261_START_WORK_DELAY_MS (0)
+/* NOTE: 192000 has a reg value donwstream but not listed in datasheet */
#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_12000 | \
+ SNDRV_PCM_RATE_24000 | \
SNDRV_PCM_RATE_96000)
#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE | \
SNDRV_PCM_FMTBIT_S32_LE)
-#define FADE_TIME_MAX 100000
-#define FADE_TIME_MIN 0
-
#define AW88261_DEV_DEFAULT_CH (0)
#define AW88261_ACF_FILE "aw88261_acf.bin"
#define AW88261_DEV_SYSST_CHECK_MAX (10)
#define AW88261_SOFT_RESET_VALUE (0x55aa)
#define AW88261_REG_TO_DB (0x3f)
-#define AW88261_VOL_START_MASK (0xfc00)
#define AW88261_INIT_PROFILE (0)
#define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \
@@ -403,11 +557,6 @@
.put = profile_set, \
}
-enum {
- AW88261_SYNC_START = 0,
- AW88261_ASYNC_START,
-};
-
enum aw88261_id {
AW88261_CHIP_ID = 0x2113,
};
@@ -442,7 +591,6 @@ struct aw88261 {
struct aw_device *aw_pa;
struct mutex lock;
struct gpio_desc *reset_gpio;
- struct delayed_work start_work;
struct regmap *regmap;
struct aw_container *aw_cfg;
@@ -451,6 +599,19 @@ struct aw88261 {
unsigned int mute_st;
unsigned int amppd_st;
+ unsigned int sr_value;
+ unsigned int cco_mux_value;
+ unsigned int fs_value;
+ unsigned int bck_value;
+ unsigned int bck_inv_value;
+ unsigned int tdm_bck_value;
+ unsigned int md_value;
+
+ unsigned int slot_num_value;
+ unsigned int tx_slotvld_mask;
+ unsigned int rxl_slotvld_mask;
+ unsigned int rxr_slotvld_mask;
+
bool phase_sync;
};
diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c
index dd09bac652f7..ee0e8bd8c54c 100644
--- a/sound/soc/codecs/aw88395/aw88395.c
+++ b/sound/soc/codecs/aw88395/aw88395.c
@@ -462,6 +462,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395)
static int aw88395_request_firmware_file(struct aw88395 *aw88395)
{
const struct firmware *cont = NULL;
+ struct aw_container *aw_cfg;
int ret;
aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED;
@@ -475,15 +476,17 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395)
dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n",
AW88395_ACF_FILE, cont ? cont->size : 0);
- aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
- if (!aw88395->aw_cfg) {
+ aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL);
+ if (!aw_cfg) {
release_firmware(cont);
return -ENOMEM;
}
- aw88395->aw_cfg->len = (int)cont->size;
- memcpy(aw88395->aw_cfg->data, cont->data, cont->size);
+ aw_cfg->len = (int)cont->size;
+ memcpy(aw_cfg->data, cont->data, cont->size);
release_firmware(cont);
+ aw88395->aw_cfg = aw_cfg;
+
ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg);
if (ret < 0) {
dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE);
@@ -557,7 +560,7 @@ static int aw88395_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id aw88395_i2c_id[] = {
- { AW88395_I2C_NAME },
+ { .name = AW88395_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id);
diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h
index 3626f222899d..7b74eeb84c43 100644
--- a/sound/soc/codecs/aw88395/aw88395_device.h
+++ b/sound/soc/codecs/aw88395/aw88395_device.h
@@ -152,7 +152,7 @@ struct aw_cali_desc {
struct aw_container {
int len;
- u8 data[];
+ u8 data[] __counted_by(len);
};
struct aw_device {
diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c
index b588c27909b5..b2ec3503f7e2 100644
--- a/sound/soc/codecs/aw88399.c
+++ b/sound/soc/codecs/aw88399.c
@@ -2146,7 +2146,7 @@ static int aw88399_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id aw88399_i2c_id[] = {
- { AW88399_I2C_NAME },
+ { .name = AW88399_I2C_NAME },
{ }
};
MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id);
diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c
index fb5b950e584c..371e99205b58 100644
--- a/sound/soc/codecs/cs-amp-lib.c
+++ b/sound/soc/codecs/cs-amp-lib.c
@@ -748,10 +748,7 @@ static const char *cs_amp_devm_get_dell_ssidex(struct device *dev,
char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME,
&DELL_SSIDEXV2_EFI_GUID,
NULL);
- ret = PTR_ERR_OR_ZERO(ssidex_buf);
- if (ret == -ENOENT)
- return ERR_PTR(-ENOENT);
- else if (ret < 0)
+ if (IS_ERR(ssidex_buf))
return ssidex_buf;
/*
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index 0bb4bdb3deec..c835088de578 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -555,8 +555,8 @@ MODULE_DEVICE_TABLE(of, cs35l32_of_match);
static const struct i2c_device_id cs35l32_id[] = {
- {"cs35l32"},
- {}
+ { .name = "cs35l32" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l32_id);
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
index 98b4d371d931..f49edb9ea1f2 100644
--- a/sound/soc/codecs/cs35l33.c
+++ b/sound/soc/codecs/cs35l33.c
@@ -1262,8 +1262,8 @@ static const struct of_device_id cs35l33_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l33_of_match);
static const struct i2c_device_id cs35l33_id[] = {
- {"cs35l33"},
- {}
+ { .name = "cs35l33" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l33_id);
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
index a5a8075598ff..e80984b22159 100644
--- a/sound/soc/codecs/cs35l34.c
+++ b/sound/soc/codecs/cs35l34.c
@@ -1175,8 +1175,8 @@ static const struct of_device_id cs35l34_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l34_of_match);
static const struct i2c_device_id cs35l34_id[] = {
- {"cs35l34"},
- {}
+ { .name = "cs35l34" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l34_id);
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
index 7a01b1d9fc9d..b2439ec3c19f 100644
--- a/sound/soc/codecs/cs35l35.c
+++ b/sound/soc/codecs/cs35l35.c
@@ -1639,8 +1639,8 @@ static const struct of_device_id cs35l35_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l35_of_match);
static const struct i2c_device_id cs35l35_id[] = {
- {"cs35l35"},
- {}
+ { .name = "cs35l35" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l35_id);
diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c
index 93818d7ec1a7..89645327945f 100644
--- a/sound/soc/codecs/cs35l36.c
+++ b/sound/soc/codecs/cs35l36.c
@@ -1918,8 +1918,8 @@ static const struct of_device_id cs35l36_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l36_of_match);
static const struct i2c_device_id cs35l36_id[] = {
- {"cs35l36"},
- {}
+ { .name = "cs35l36" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l36_id);
diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c
index 34097996b784..23f59f3025bd 100644
--- a/sound/soc/codecs/cs35l41-i2c.c
+++ b/sound/soc/codecs/cs35l41-i2c.c
@@ -20,11 +20,11 @@
#include "cs35l41.h"
static const struct i2c_device_id cs35l41_id_i2c[] = {
- { "cs35l40" },
- { "cs35l41" },
- { "cs35l51" },
- { "cs35l53" },
- {}
+ { .name = "cs35l40" },
+ { .name = "cs35l41" },
+ { .name = "cs35l51" },
+ { .name = "cs35l53" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c);
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
index a09aa3b92ae1..3eba61e107a5 100644
--- a/sound/soc/codecs/cs35l45-i2c.c
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -53,8 +53,8 @@ static const struct of_device_id cs35l45_of_match[] = {
MODULE_DEVICE_TABLE(of, cs35l45_of_match);
static const struct i2c_device_id cs35l45_id_i2c[] = {
- { "cs35l45" },
- {}
+ { .name = "cs35l45" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c);
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
index 0492ddc4102d..0f64ab628b03 100644
--- a/sound/soc/codecs/cs35l56-i2c.c
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -72,9 +72,9 @@ static void cs35l56_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id cs35l56_id_i2c[] = {
- { "cs35l56", 0x3556 },
- { "cs35l63", 0x3563 },
- {}
+ { .name = "cs35l56", .driver_data = 0x3556 },
+ { .name = "cs35l63", .driver_data = 0x3563 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
index 88e0aac540d6..0a55b93b96f9 100644
--- a/sound/soc/codecs/cs35l56-sdw.c
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -17,6 +17,7 @@
#include <linux/string_choices.h>
#include <linux/swab.h>
#include <linux/types.h>
+#include <linux/unaligned.h>
#include <linux/workqueue.h>
#include "cs35l56.h"
@@ -59,8 +60,6 @@ static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg,
{
int ret, i;
- reg += CS35L56_SDW_ADDR_OFFSET;
-
for (i = 0; i < val_size; i += sizeof(u32)) {
/* Poll for bus bridge idle */
ret = cs35l56_sdw_poll_mem_status(peripheral,
@@ -97,57 +96,23 @@ static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg,
return 0;
}
-static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf)
-{
- int ret;
-
- ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf);
- if (ret != 0) {
- dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret);
- return ret;
- }
-
- swab32s((u32 *)buf);
-
- return 0;
-}
-
static int cs35l56_sdw_read(void *context, const void *reg_buf,
const size_t reg_size, void *val_buf,
size_t val_size)
{
struct sdw_slave *peripheral = context;
- u8 *buf8 = val_buf;
- unsigned int reg, bytes;
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ unsigned int reg_addr = get_unaligned_le32(reg_buf);
int ret;
- reg = le32_to_cpu(*(const __le32 *)reg_buf);
-
- if (cs35l56_is_otp_register(reg))
- return cs35l56_sdw_slow_read(peripheral, reg, buf8, val_size);
+ if (cs35l56_is_otp_register(reg_addr - CS35L56_SDW_ADDR_OFFSET))
+ return cs35l56_sdw_slow_read(peripheral, reg_addr, (u8 *)val_buf, val_size);
- reg += CS35L56_SDW_ADDR_OFFSET;
-
- if (val_size == 4)
- return cs35l56_sdw_read_one(peripheral, reg, val_buf);
-
- while (val_size) {
- bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
- if (bytes > val_size)
- bytes = val_size;
-
- ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8);
- if (ret != 0) {
- dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n",
- reg, reg + bytes - 1, ret);
- return ret;
- }
+ ret = regmap_raw_read(cs35l56->sdw_bus_regmap, reg_addr, val_buf, val_size);
+ if (ret)
+ return ret;
- swab32_array((u32 *)buf8, bytes / 4);
- val_size -= bytes;
- reg += bytes;
- buf8 += bytes;
- }
+ swab32_array((u32 *)val_buf, val_size / sizeof(u32));
return 0;
}
@@ -161,58 +126,34 @@ static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes)
*dest32++ = swab32(*src32++);
}
-static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf)
-{
- u32 val_le = swab32(*(u32 *)buf);
- int ret;
-
- ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le);
- if (ret != 0) {
- dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret);
- return ret;
- }
-
- return 0;
-}
-
static int cs35l56_sdw_gather_write(void *context,
const void *reg_buf, size_t reg_size,
const void *val_buf, size_t val_size)
{
struct sdw_slave *peripheral = context;
- const u8 *src_be = val_buf;
- u32 val_le_buf[64]; /* Define u32 so it is 32-bit aligned */
- unsigned int reg, bytes;
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ unsigned int reg_addr = get_unaligned_le32(reg_buf);
+ u32 swab_buf[64]; /* Define u32 so it is 32-bit aligned */
int ret;
- reg = le32_to_cpu(*(const __le32 *)reg_buf);
- reg += CS35L56_SDW_ADDR_OFFSET;
-
- if (val_size == 4)
- return cs35l56_sdw_write_one(peripheral, reg, src_be);
-
- while (val_size) {
- bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
- if (bytes > val_size)
- bytes = val_size;
- if (bytes > sizeof(val_le_buf))
- bytes = sizeof(val_le_buf);
-
- cs35l56_swab_copy(val_le_buf, src_be, bytes);
-
- ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf);
- if (ret != 0) {
- dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n",
- reg, reg + bytes - 1, ret);
+ while (val_size > sizeof(swab_buf)) {
+ cs35l56_swab_copy(swab_buf, val_buf, sizeof(swab_buf));
+ ret = regmap_raw_write(cs35l56->sdw_bus_regmap, reg_addr,
+ swab_buf, sizeof(swab_buf));
+ if (ret)
return ret;
- }
- val_size -= bytes;
- reg += bytes;
- src_be += bytes;
+ val_size -= sizeof(swab_buf);
+ reg_addr += sizeof(swab_buf);
+ val_buf += sizeof(swab_buf);
}
- return 0;
+ if (val_size == 0)
+ return 0;
+
+ cs35l56_swab_copy(swab_buf, val_buf, val_size);
+
+ return regmap_raw_write(cs35l56->sdw_bus_regmap, reg_addr, swab_buf, val_size);
}
static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size)
@@ -231,7 +172,7 @@ static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size
* byte controls always have the same byte order, and firmware file blobs
* can be written verbatim.
*/
-static const struct regmap_bus cs35l56_regmap_bus_sdw = {
+static const struct regmap_bus cs35l56_regmap_swab_bus_sdw = {
.read = cs35l56_sdw_read,
.write = cs35l56_sdw_write,
.gather_write = cs35l56_sdw_gather_write,
@@ -239,6 +180,18 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = {
.val_format_endian_default = REGMAP_ENDIAN_BIG,
};
+/* Low-level SoundWire regmap to transfer the data over the bus */
+static const struct regmap_config cs35l56_sdw_bus_regmap = {
+ .name = "sdw-le32",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+ .max_register = CS35L56_DSP1_PMEM_5114 + 0x8000,
+ .cache_type = REGCACHE_NONE,
+};
+
static int cs35l56_sdw_get_unique_id(struct cs35l56_private *cs35l56)
{
int ret;
@@ -277,11 +230,8 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral)
* cs35l56_init can return with !init_done if it triggered
* a soft reset.
*/
- if (cs35l56->base.init_done) {
- /* Enable SoundWire interrupts */
- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1,
- CS35L56_SDW_INT_MASK_CODEC_IRQ);
- }
+ if (cs35l56->base.init_done)
+ cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral);
out:
pm_runtime_put_autosuspend(cs35l56->base.dev);
@@ -306,15 +256,11 @@ static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
pm_runtime_get_noresume(cs35l56->base.dev);
/*
- * Mask and clear until it has been handled. The read of GEN_INT_STAT_1
- * is required as per the SoundWire spec for interrupt status bits
- * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1.
+ * Mask and clear until it has been handled.
* None of the interrupts are time-critical so use the
* power-efficient queue.
*/
- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
- sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ cs35l56_mask_soundwire_interrupts(peripheral);
queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work);
return 0;
@@ -330,8 +276,7 @@ static void cs35l56_sdw_irq_work(struct work_struct *work)
/* unmask interrupts */
if (!cs35l56->sdw_irq_no_unmask)
- sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
- CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral);
pm_runtime_put_autosuspend(cs35l56->base.dev);
}
@@ -385,7 +330,6 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
switch (status) {
case SDW_SLAVE_ATTACHED:
- cs35l56->sdw_in_clock_stop_1 = false;
if (cs35l56->sdw_attached)
break;
@@ -407,57 +351,29 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
return 0;
}
-static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
- enum sdw_clk_stop_mode mode,
- enum sdw_clk_stop_type type)
-{
- struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
-
- dev_dbg(cs35l56->base.dev, "%s: clock_stop_mode%d stage:%d\n", __func__, mode, type);
-
- switch (type) {
- case SDW_CLK_POST_PREPARE:
- if (mode == SDW_CLK_STOP_MODE1)
- cs35l56->sdw_in_clock_stop_1 = true;
- else
- cs35l56->sdw_in_clock_stop_1 = false;
- return 0;
- default:
- return 0;
- }
-}
-
static const struct sdw_slave_ops cs35l56_sdw_ops = {
.read_prop = cs35l56_sdw_read_prop,
.interrupt_callback = cs35l56_sdw_interrupt,
.update_status = cs35l56_sdw_update_status,
- .clk_stop = cs35l56_sdw_clk_stop,
};
static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56)
{
struct sdw_slave *peripheral = cs35l56->sdw_peripheral;
+ int ret;
- dev_dbg(cs35l56->base.dev, "attached:%u unattach_request:%u in_clock_stop_1:%u\n",
- cs35l56->sdw_attached, peripheral->unattach_request, cs35l56->sdw_in_clock_stop_1);
-
- if (cs35l56->sdw_in_clock_stop_1 || peripheral->unattach_request) {
- /* Cannot access registers until bus is re-initialized. */
- dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n");
- if (!wait_for_completion_timeout(&peripheral->initialization_complete,
- msecs_to_jiffies(5000))) {
- dev_err(cs35l56->base.dev, "initialization_complete timed out\n");
- return -ETIMEDOUT;
- }
+ dev_dbg(cs35l56->base.dev, "attached:%u unattach_request:%u\n",
+ cs35l56->sdw_attached, peripheral->unattach_request);
- peripheral->unattach_request = 0;
- cs35l56->sdw_in_clock_stop_1 = false;
+ /* Cannot access registers until bus is re-initialized. */
+ ret = sdw_slave_wait_for_init(peripheral, 5000);
+ if (ret)
+ return ret;
- /*
- * Don't call regcache_mark_dirty(), we can't be sure that the
- * Manager really did issue a Bus Reset.
- */
- }
+ /*
+ * Don't call regcache_mark_dirty(), we can't be sure that the
+ * Manager really did issue a Bus Reset.
+ */
return 0;
}
@@ -490,9 +406,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
if (ret)
return ret;
- /* Re-enable SoundWire interrupts */
- sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
- CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral);
return 0;
}
@@ -504,18 +418,7 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
if (!cs35l56->base.init_done)
return 0;
- /*
- * Disable SoundWire interrupts.
- * Flush - don't cancel because that could leave an unbalanced pm_runtime_get.
- */
- cs35l56->sdw_irq_no_unmask = true;
- flush_work(&cs35l56->sdw_irq_work);
-
- /* Mask interrupts and flush in case sdw_irq_work was queued again */
- sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
- sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
- sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
- flush_work(&cs35l56->sdw_irq_work);
+ cs35l56_disable_sdw_interrupts(cs35l56);
return cs35l56_system_suspend(dev);
}
@@ -562,8 +465,16 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi
cs35l56->base.type = ((unsigned int)id->driver_data) & 0xff;
- cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
- peripheral, regmap_config);
+ /* Low-level regmap to transfer read/writes over SoundWire bus */
+ cs35l56->sdw_bus_regmap = devm_regmap_init_sdw(peripheral, &cs35l56_sdw_bus_regmap);
+ if (IS_ERR(cs35l56->sdw_bus_regmap)) {
+ ret = PTR_ERR(cs35l56->sdw_bus_regmap);
+ return dev_err_probe(dev, ret, "Failed to allocate bus register map\n");
+ }
+
+ /* Wrapper regmap to simulate big-endian ordering */
+ cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_swab_bus_sdw,
+ peripheral, regmap_config);
if (IS_ERR(cs35l56->base.regmap)) {
ret = PTR_ERR(cs35l56->base.regmap);
return dev_err_probe(dev, ret, "Failed to allocate register map\n");
@@ -583,13 +494,7 @@ static void cs35l56_sdw_remove(struct sdw_slave *peripheral)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
- /* Disable SoundWire interrupts */
- cs35l56->sdw_irq_no_unmask = true;
- flush_work(&cs35l56->sdw_irq_work);
- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
- sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
- sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
- flush_work(&cs35l56->sdw_irq_work);
+ cs35l56_disable_sdw_interrupts(cs35l56);
cs35l56_remove(cs35l56);
}
diff --git a/sound/soc/codecs/cs35l56-shared-test.c b/sound/soc/codecs/cs35l56-shared-test.c
index cfe7938065f9..4f52c8a192e5 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;
+ unsigned int reg_offset;
struct cs35l56_base *cs35l56_base;
u8 applied_pad_pull_state[CS35L56_MAX_GPIO];
};
@@ -194,6 +195,8 @@ static int cs35l56_shared_test_reg_read(void *context, unsigned int reg, unsigne
{
struct cs35l56_shared_test_priv *priv = context;
+ reg -= priv->reg_offset;
+
switch (reg) {
case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG:
case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1:
@@ -214,6 +217,8 @@ static int cs35l56_shared_test_reg_write(void *context, unsigned int reg, unsign
{
struct cs35l56_shared_test_priv *priv = context;
+ reg -= priv->reg_offset;
+
switch (reg) {
case CS35L56_UPDATE_REGS:
return cs35l56_shared_test_updt_gpio_pres(priv, reg, val);
@@ -673,6 +678,7 @@ static int cs35l56_shared_test_case_base_init(struct kunit *test, u8 type, u8 re
priv->cs35l56_base->rev = rev;
if (regmap_config) {
+ priv->reg_offset = regmap_config->reg_base;
ret = cs35l56_shared_test_case_regmap_init(test, regmap_config);
if (ret)
return ret;
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
index 795e2764d67e..f14e2eaaa4ee 100644
--- a/sound/soc/codecs/cs35l56-shared.c
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -534,6 +534,7 @@ static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base)
* The regmap must remain in cache-only until the chip has
* booted, so use a bypassed read.
*/
+ val = 0;
ret = read_poll_timeout(regmap_read_bypassed, read_ret,
(val > 0) && (val < 0xffffffff),
CS35L56_HALO_STATE_POLL_US,
@@ -1259,7 +1260,7 @@ ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base,
return -EMSGSIZE;
ret = simple_write_to_buffer(&cal_data, sizeof(cal_data), ppos, from, count);
- if (ret)
+ if (ret < 0)
return ret;
ret = cs35l56_stash_calibration(cs35l56_base, &cal_data);
@@ -1293,6 +1294,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_create_cal_debugfs, "SND_SOC_CS35L56_SHARED");
void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base)
{
debugfs_remove_recursive(cs35l56_base->debugfs);
+ cs35l56_base->debugfs = ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL_NS_GPL(cs35l56_remove_cal_debugfs, "SND_SOC_CS35L56_SHARED");
@@ -1880,6 +1882,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, "SND_SOC_CS35L56_SHARED");
const struct regmap_config cs35l56_regmap_sdw = {
.reg_bits = 32,
+ .reg_base = 0x8000,
.val_bits = 32,
.reg_stride = 4,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
@@ -1915,6 +1918,7 @@ const struct regmap_config cs35l63_regmap_sdw = {
.reg_bits = 32,
.val_bits = 32,
.reg_stride = 4,
+ .reg_base = 0x8000,
.reg_format_endian = REGMAP_ENDIAN_LITTLE,
.val_format_endian = REGMAP_ENDIAN_BIG,
.max_register = CS35L56_DSP1_PMEM_5114,
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index 4fbbdcc87151..570a68829ccd 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -37,6 +37,49 @@
#include "wm_adsp.h"
#include "cs35l56.h"
+void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral)
+{
+ /*
+ * The read of GEN_INT_STAT_1 is required as per the SoundWire spec
+ * for interrupt status bits to clear.
+ * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1.
+ */
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE");
+
+void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral)
+{
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE");
+
+void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56)
+{
+ if (!cs35l56->sdw_peripheral)
+ return;
+
+ cs35l56->sdw_irq_no_unmask = true;
+ flush_work(&cs35l56->sdw_irq_work);
+
+ /* Mask interrupts and flush in case sdw_irq_work was queued again */
+ cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral);
+ flush_work(&cs35l56->sdw_irq_work);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE");
+
+void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56)
+{
+ if (!cs35l56->sdw_peripheral)
+ return;
+
+ cs35l56->sdw_irq_no_unmask = false;
+ cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE");
+
static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
@@ -426,6 +469,8 @@ static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned
reg_val &= ~(0x3f << channel_shift);
reg_val |= bit_num << channel_shift;
channel_shift += 8;
+ if (channel_shift > 24)
+ break;
}
return reg_val;
@@ -788,14 +833,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing
* Setting sdw_irq_no_unmask prevents the handler re-enabling
* the SoundWire interrupt.
*/
- if (cs35l56->sdw_peripheral) {
- cs35l56->sdw_irq_no_unmask = true;
- flush_work(&cs35l56->sdw_irq_work);
- sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
- sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
- sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
- flush_work(&cs35l56->sdw_irq_work);
- }
+ cs35l56_disable_sdw_interrupts(cs35l56);
ret = cs35l56_firmware_shutdown(&cs35l56->base);
if (ret)
@@ -847,12 +885,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing
err_unlock:
mutex_unlock(&cs35l56->base.irq_lock);
err:
- /* Re-enable SoundWire interrupts */
- if (cs35l56->sdw_peripheral) {
- cs35l56->sdw_irq_no_unmask = false;
- sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
- CS35L56_SDW_INT_MASK_CODEC_IRQ);
- }
+ cs35l56_enable_sdw_interrupts(cs35l56);
}
static void cs35l56_dsp_work(struct work_struct *work)
@@ -1326,7 +1359,7 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component)
}
EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name);
-static int cs35l56_component_probe(struct snd_soc_component *component)
+static int _cs35l56_component_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
@@ -1426,6 +1459,17 @@ static void cs35l56_component_remove(struct snd_soc_component *component)
cs35l56->component = NULL;
}
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = _cs35l56_component_probe(component);
+ if (ret < 0)
+ cs35l56_component_remove(component);
+
+ return ret;
+}
+
static int cs35l56_set_bias_level(struct snd_soc_component *component,
enum snd_soc_bias_level level)
{
@@ -1480,6 +1524,7 @@ static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
int cs35l56_system_suspend(struct device *dev)
{
struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
dev_dbg(dev, "system_suspend\n");
@@ -1495,7 +1540,11 @@ int cs35l56_system_suspend(struct device *dev)
if (cs35l56->base.irq)
disable_irq(cs35l56->base.irq);
- return pm_runtime_force_suspend(dev);
+ ret = pm_runtime_force_suspend(dev);
+ if ((ret < 0) && cs35l56->base.irq)
+ enable_irq(cs35l56->base.irq);
+
+ return ret;
}
EXPORT_SYMBOL_GPL(cs35l56_system_suspend);
@@ -1964,11 +2013,14 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
if (ret < 0) {
dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n");
- goto err;
+ goto err_remove_wm_adsp;
}
return 0;
+err_remove_wm_adsp:
+ wm_adsp2_remove(&cs35l56->dsp);
+
err:
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
@@ -1994,7 +2046,8 @@ int cs35l56_init(struct cs35l56_private *cs35l56)
if (cs35l56->base.init_done)
return 0;
- pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 100);
+ pm_runtime_set_autosuspend_delay(cs35l56->base.dev,
+ CS35L56_FW_REQ_ACTIVE_TIMEOUT_MS + 50);
pm_runtime_use_autosuspend(cs35l56->base.dev);
pm_runtime_set_active(cs35l56->base.dev);
pm_runtime_enable(cs35l56->base.dev);
@@ -2076,6 +2129,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56)
destroy_workqueue(cs35l56->dsp_wq);
+ wm_adsp2_remove(&cs35l56->dsp);
+
pm_runtime_dont_use_autosuspend(cs35l56->base.dev);
pm_runtime_suspend(cs35l56->base.dev);
pm_runtime_disable(cs35l56->base.dev);
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
index cd71b23b2a3a..9acd2e7e17c9 100644
--- a/sound/soc/codecs/cs35l56.h
+++ b/sound/soc/codecs/cs35l56.h
@@ -37,12 +37,12 @@ struct cs35l56_private {
struct snd_soc_component *component;
struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
struct sdw_slave *sdw_peripheral;
+ struct regmap *sdw_bus_regmap;
const char *fallback_fw_suffix;
struct work_struct sdw_irq_work;
bool sdw_irq_no_unmask;
bool soft_resetting;
bool sdw_attached;
- bool sdw_in_clock_stop_1;
struct completion init_completion;
int speaker_id;
@@ -65,6 +65,11 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b
extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
+void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral);
+void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral);
+void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56);
+void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56);
+
int cs35l56_system_suspend(struct device *dev);
int cs35l56_system_suspend_late(struct device *dev);
int cs35l56_system_suspend_no_irq(struct device *dev);
diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c
index 89c424dd838b..a889fbd519a8 100644
--- a/sound/soc/codecs/cs4234.c
+++ b/sound/soc/codecs/cs4234.c
@@ -11,7 +11,6 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/jiffies.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c
index 3f759c13d6d1..286d5ca29854 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -638,7 +638,7 @@ static const struct of_device_id cs4265_of_match[] = {
MODULE_DEVICE_TABLE(of, cs4265_of_match);
static const struct i2c_device_id cs4265_id[] = {
- { "cs4265" },
+ { .name = "cs4265" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4265_id);
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index a48980e746ff..47b2f903a32c 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -19,7 +19,6 @@
* - Power management is supported
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -732,8 +731,8 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client)
* cs4270_id - I2C device IDs supported by this driver
*/
static const struct i2c_device_id cs4270_id[] = {
- {"cs4270"},
- {}
+ { .name = "cs4270" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs4270_id);
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
index 1d210b969173..869a5317f677 100644
--- a/sound/soc/codecs/cs4271-i2c.c
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -23,7 +23,7 @@ static int cs4271_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id cs4271_i2c_id[] = {
- { "cs4271" },
+ { .name = "cs4271" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c
index 98b6718ccabf..c0942f28723b 100644
--- a/sound/soc/codecs/cs42l42-i2c.c
+++ b/sound/soc/codecs/cs42l42-i2c.c
@@ -78,8 +78,8 @@ static const struct acpi_device_id __maybe_unused cs42l42_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match);
static const struct i2c_device_id cs42l42_id[] = {
- {"cs42l42"},
- {}
+ { .name = "cs42l42" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs42l42_id);
diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c
index d5999ad9ff9b..ad1256910a18 100644
--- a/sound/soc/codecs/cs42l42-sdw.c
+++ b/sound/soc/codecs/cs42l42-sdw.c
@@ -9,7 +9,6 @@
#include <linux/gpio/consumer.h>
#include <linux/iopoll.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw.h>
@@ -433,19 +432,16 @@ static const struct reg_sequence cs42l42_soft_reboot_seq[] = {
static int cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42)
{
struct sdw_slave *peripheral = cs42l42->sdw_peripheral;
+ int ret;
if (!peripheral->unattach_request)
return 0;
/* Cannot access registers until master re-attaches. */
dev_dbg(&peripheral->dev, "Wait for initialization_complete\n");
- if (!wait_for_completion_timeout(&peripheral->initialization_complete,
- msecs_to_jiffies(5000))) {
- dev_err(&peripheral->dev, "initialization_complete timed out\n");
- return -ETIMEDOUT;
- }
-
- peripheral->unattach_request = 0;
+ ret = sdw_slave_wait_for_init(peripheral, 5000);
+ if (ret)
+ return ret;
/*
* After a bus reset there must be a reconfiguration reset to
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
index 3e04e6897b14..dbf45cabfffe 100644
--- a/sound/soc/codecs/cs42l43-jack.c
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -310,6 +310,7 @@ irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data)
#define CS42L43_JACK_ABSENT 0x0
#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
+#define CS42L43_JACK_MICROPHONE (SND_JACK_MECHANICAL | SND_JACK_MICROPHONE)
#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
@@ -805,7 +806,7 @@ irqreturn_t cs42l43_tip_sense(int irq, void *data)
if (priv->suspend_jack_debounce)
db_delay += priv->tip_fall_db_ms + priv->tip_rise_db_ms;
- queue_delayed_work(system_long_wq, &priv->tip_sense_work,
+ queue_delayed_work(system_dfl_long_wq, &priv->tip_sense_work,
msecs_to_jiffies(db_delay));
return IRQ_HANDLED;
@@ -871,7 +872,7 @@ static const struct cs42l43_jack_override_mode {
.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
.mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) |
CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK,
- .report = CS42L43_JACK_LINEIN,
+ .report = CS42L43_JACK_MICROPHONE,
},
[CS42L43_JACK_RAW_OPTICAL] = {
.hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
@@ -932,7 +933,8 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
if (!override) {
- queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0);
+ queue_delayed_work(system_dfl_long_wq, &priv->tip_sense_work,
+ 0);
} else {
override--;
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
index f0d6ff0b2976..1d133577702e 100644
--- a/sound/soc/codecs/cs42l43.c
+++ b/sound/soc/codecs/cs42l43.c
@@ -19,7 +19,6 @@
#include <linux/jiffies.h>
#include <linux/mfd/cs42l43.h>
#include <linux/mfd/cs42l43-regs.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index ba7e237619f2..a20a1030c498 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -14,7 +14,7 @@
#include "cs42l51.h"
static const struct i2c_device_id cs42l51_i2c_id[] = {
- { "cs42l51" },
+ { .name = "cs42l51" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 662dc1a4835b..9d6bcfbbf0b2 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1226,7 +1226,7 @@ MODULE_DEVICE_TABLE(of, cs42l52_of_match);
static const struct i2c_device_id cs42l52_id[] = {
- { "cs42l52" },
+ { .name = "cs42l52" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l52_id);
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index aabb74f1f43c..4d9a22a1029c 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1353,7 +1353,7 @@ MODULE_DEVICE_TABLE(of, cs42l56_of_match);
static const struct i2c_device_id cs42l56_id[] = {
- { "cs42l56" },
+ { .name = "cs42l56" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs42l56_id);
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index bda8442c1d66..6ddc6549492a 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1369,8 +1369,8 @@ static const struct of_device_id cs42l73_of_match[] = {
MODULE_DEVICE_TABLE(of, cs42l73_of_match);
static const struct i2c_device_id cs42l73_id[] = {
- {"cs42l73"},
- {}
+ { .name = "cs42l73" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs42l73_id);
diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c
index e590a43559e4..f2a58163de0e 100644
--- a/sound/soc/codecs/cs42l84.c
+++ b/sound/soc/codecs/cs42l84.c
@@ -1101,8 +1101,8 @@ static const struct of_device_id cs42l84_of_match[] = {
MODULE_DEVICE_TABLE(of, cs42l84_of_match);
static const struct i2c_device_id cs42l84_id[] = {
- { "cs42l84" },
- {}
+ { .name = "cs42l84" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs42l84_id);
diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c
index 0faca384073a..4427d3214dee 100644
--- a/sound/soc/codecs/cs42xx8-i2c.c
+++ b/sound/soc/codecs/cs42xx8-i2c.c
@@ -9,7 +9,6 @@
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
@@ -49,9 +48,9 @@ static const struct of_device_id cs42xx8_of_match[] = {
MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
static const struct i2c_device_id cs42xx8_i2c_id[] = {
- {"cs42448", (kernel_ulong_t)&cs42448_data},
- {"cs42888", (kernel_ulong_t)&cs42888_data},
- {}
+ { .name = "cs42448", .driver_data = (kernel_ulong_t)&cs42448_data },
+ { .name = "cs42888", .driver_data = (kernel_ulong_t)&cs42888_data },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id);
diff --git a/sound/soc/codecs/cs42xx8-spi.c b/sound/soc/codecs/cs42xx8-spi.c
new file mode 100644
index 000000000000..2e4b8e6c4081
--- /dev/null
+++ b/sound/soc/codecs/cs42xx8-spi.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cirrus Logic CS42448/CS42888 Audio CODEC DAI SPI driver
+ *
+ * Copyright 2026 NXP
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <sound/soc.h>
+
+#include "cs42xx8.h"
+
+/*
+ * CS42448/CS42888 SPI register access (from datasheet Figure 23):
+ *
+ * The SPI frame is 3 bytes:
+ * Byte 0: chip address [7:1] = 1001111, bit[0] = R/W (0=write, 1=read)
+ * Write: 0x9E, Read: 0x9F
+ * Byte 1: MAP - Memory Address Pointer
+ * bit[7] = INCR (auto-increment for burst), bits[6:0] = address
+ * Byte 2: data byte
+ *
+ * We configure reg_bits=16 so that regmap treats the address field as 2 bytes
+ * (big-endian). The chip address byte (0x9E/0x9F) is placed in the high byte
+ * via write_flag_mask / read_flag_mask, and the MAP register address occupies
+ * the low byte. Currently INCR (MAP bit[7]) is not set and use_single_read/write
+ * are enabled. This produces the correct 3-byte on-wire frame without any
+ * custom bus implementation:
+ *
+ * write: [0x9E, MAP_addr, data]
+ * read: [0x9F, MAP_addr] -> [data]
+ */
+
+static int cs42xx8_spi_probe(struct spi_device *spi)
+{
+ struct cs42xx8_driver_data *drvdata;
+ struct regmap_config config;
+ int ret;
+
+ drvdata = (struct cs42xx8_driver_data *)spi_get_device_match_data(spi);
+ if (!drvdata)
+ return dev_err_probe(&spi->dev, -EINVAL,
+ "failed to find driver data\n");
+
+ config = cs42xx8_regmap_config;
+ /*
+ * reg_bits=16 makes regmap send a 2-byte address field (big-endian).
+ * write_flag_mask/read_flag_mask are OR'd into that address field:
+ */
+ config.reg_bits = 16;
+ config.write_flag_mask = 0x9E;
+ config.read_flag_mask = 0x9F;
+
+ ret = cs42xx8_probe(&spi->dev,
+ devm_regmap_init_spi(spi, &config), drvdata);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(&spi->dev);
+ pm_request_idle(&spi->dev);
+
+ return 0;
+}
+
+static void cs42xx8_spi_remove(struct spi_device *spi)
+{
+ pm_runtime_disable(&spi->dev);
+}
+
+static const struct of_device_id cs42xx8_of_match[] = {
+ { .compatible = "cirrus,cs42448", .data = &cs42448_data, },
+ { .compatible = "cirrus,cs42888", .data = &cs42888_data, },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, cs42xx8_of_match);
+
+static const struct spi_device_id cs42xx8_spi_id[] = {
+ { .name = "cs42448", .driver_data = (kernel_ulong_t)&cs42448_data },
+ { .name = "cs42888", .driver_data = (kernel_ulong_t)&cs42888_data },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, cs42xx8_spi_id);
+
+static struct spi_driver cs42xx8_spi_driver = {
+ .driver = {
+ .name = "cs42xx8",
+ .pm = pm_ptr(&cs42xx8_pm),
+ .of_match_table = cs42xx8_of_match,
+ },
+ .probe = cs42xx8_spi_probe,
+ .remove = cs42xx8_spi_remove,
+ .id_table = cs42xx8_spi_id,
+};
+
+module_spi_driver(cs42xx8_spi_driver);
+
+MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec SPI Driver");
+MODULE_AUTHOR("Chancel Liu <chancel.liu@nxp.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index 12fe9b3e2525..5b689549c74e 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -478,6 +478,9 @@ const struct regmap_config cs42xx8_regmap_config = {
.volatile_reg = cs42xx8_volatile_register,
.writeable_reg = cs42xx8_writeable_register,
.cache_type = REGCACHE_MAPLE,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .use_single_read = true,
+ .use_single_write = true,
};
EXPORT_SYMBOL_GPL(cs42xx8_regmap_config);
diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c
index a3bdaac9c059..e7b06f962790 100644
--- a/sound/soc/codecs/cs43130.c
+++ b/sound/soc/codecs/cs43130.c
@@ -2753,11 +2753,11 @@ MODULE_DEVICE_TABLE(acpi, cs43130_acpi_match);
static const struct i2c_device_id cs43130_i2c_id[] = {
- {"cs43130"},
- {"cs4399"},
- {"cs43131"},
- {"cs43198"},
- {}
+ { .name = "cs43130" },
+ { .name = "cs4399" },
+ { .name = "cs43131" },
+ { .name = "cs43198" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id);
diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c
index b726e22ef57d..a44e6b3c298e 100644
--- a/sound/soc/codecs/cs4341.c
+++ b/sound/soc/codecs/cs4341.c
@@ -248,7 +248,7 @@ static int cs4341_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id cs4341_i2c_id[] = {
- { "cs4341" },
+ { .name = "cs4341" },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id);
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index d9a9c34fffe3..6ac6d306b054 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -7,7 +7,6 @@
* Authors: Tim Howe <Tim.Howe@cirrus.com>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -358,8 +357,8 @@ static const struct of_device_id cs4349_of_match[] = {
MODULE_DEVICE_TABLE(of, cs4349_of_match);
static const struct i2c_device_id cs4349_i2c_id[] = {
- {"cs4349"},
- {}
+ { .name = "cs4349" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id);
diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c
index 52b02ceaa7e3..98cb61dd88c1 100644
--- a/sound/soc/codecs/cs530x-i2c.c
+++ b/sound/soc/codecs/cs530x-i2c.c
@@ -40,13 +40,13 @@ static const struct of_device_id cs530x_of_match[] = {
MODULE_DEVICE_TABLE(of, cs530x_of_match);
static const struct i2c_device_id cs530x_i2c_id[] = {
- { "cs4282", CS4282 },
- { "cs4302", CS4302 },
- { "cs4304", CS4304 },
- { "cs4308", CS4308 },
- { "cs5302", CS5302 },
- { "cs5304", CS5304 },
- { "cs5308", CS5308 },
+ { .name = "cs4282", .driver_data = CS4282 },
+ { .name = "cs4302", .driver_data = CS4302 },
+ { .name = "cs4304", .driver_data = CS4304 },
+ { .name = "cs4308", .driver_data = CS4308 },
+ { .name = "cs5302", .driver_data = CS5302 },
+ { .name = "cs5304", .driver_data = CS5304 },
+ { .name = "cs5308", .driver_data = CS5308 },
{ }
};
MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id);
diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c
index 18b5ff75feec..2c7e33135911 100644
--- a/sound/soc/codecs/cs530x.c
+++ b/sound/soc/codecs/cs530x.c
@@ -1093,6 +1093,29 @@ static int cs530x_component_probe(struct snd_soc_component *component)
return 0;
}
+static bool cs530x_mclk_freq_is_valid(struct cs530x_priv *cs530x,
+ unsigned int freq)
+{
+ /*
+ * All these chips support 48 kHz- and 44.1 kHz-related sample rates,
+ * but they differ in what MCLK frequency is required for achieving
+ * the sample rate.
+ */
+ switch (cs530x->devtype) {
+ case CS4282:
+ case CS4302:
+ case CS4304:
+ case CS4308:
+ return freq == 49152000 || freq == 45158400;
+ case CS5302:
+ case CS5304:
+ case CS5308:
+ return freq == 24576000 || freq == 22579200;
+ }
+
+ return false;
+}
+
static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
@@ -1101,11 +1124,7 @@ static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id,
switch (source) {
case CS530X_SYSCLK_SRC_MCLK:
- switch (freq) {
- case CS530X_SYSCLK_REF_45_1MHZ:
- case CS530X_SYSCLK_REF_49_1MHZ:
- break;
- default:
+ if (!cs530x_mclk_freq_is_valid(cs530x, freq)) {
dev_err(component->dev, "Invalid MCLK source rate %d\n", freq);
return -EINVAL;
}
diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h
index 1e2f6a7a589c..18aa4dfd0c86 100644
--- a/sound/soc/codecs/cs530x.h
+++ b/sound/soc/codecs/cs530x.h
@@ -200,12 +200,6 @@
/* IN_VOL_CTL5 and OUT_VOL_CTL5 */
#define CS530X_INOUT_VU BIT(0)
-/* MCLK Reference Source Frequency */
-/* 41KHz related */
-#define CS530X_SYSCLK_REF_45_1MHZ 45158400
-/* 48KHz related */
-#define CS530X_SYSCLK_REF_49_1MHZ 49152000
-
/* System Clock Source */
#define CS530X_SYSCLK_SRC_MCLK 0
#define CS530X_SYSCLK_SRC_PLL 1
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index 93ea2fb4dae9..511453b4c375 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -1083,8 +1083,8 @@ static const struct of_device_id cs53l30_of_match[] = {
MODULE_DEVICE_TABLE(of, cs53l30_of_match);
static const struct i2c_device_id cs53l30_id[] = {
- { "cs53l30" },
- {}
+ { .name = "cs53l30" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cs53l30_id);
diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c
index c0dc69ce4d0e..0dea84cd8ef2 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -236,7 +236,8 @@ err:
/* Line discipline .close() */
static void v253_close(struct tty_struct *tty)
{
- struct snd_soc_component *component = tty->disc_data;
+ struct cx20442_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = codec->component;
struct cx20442_priv *cx20442;
tty->disc_data = NULL;
@@ -248,7 +249,7 @@ static void v253_close(struct tty_struct *tty)
/* Prevent the codec driver from further accessing the modem */
cx20442->tty = NULL;
- component->card->pop_time = 0;
+ codec->ready = false;
}
/* Line discipline .hangup() */
@@ -261,7 +262,8 @@ static void v253_hangup(struct tty_struct *tty)
static void v253_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp,
size_t count)
{
- struct snd_soc_component *component = tty->disc_data;
+ struct cx20442_codec *codec = tty->disc_data;
+ struct snd_soc_component *component = codec->component;
struct cx20442_priv *cx20442;
if (!component)
@@ -274,7 +276,7 @@ static void v253_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp,
/* Set up codec driver access to modem controls */
cx20442->tty = tty;
- component->card->pop_time = 1;
+ codec->ready = true;
}
}
@@ -375,7 +377,6 @@ static int cx20442_component_probe(struct snd_soc_component *component)
cx20442->tty = NULL;
snd_soc_component_set_drvdata(component, cx20442);
- component->card->pop_time = 0;
return 0;
}
diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h
index bb897bcb2486..78ab1eab083e 100644
--- a/sound/soc/codecs/cx20442.h
+++ b/sound/soc/codecs/cx20442.h
@@ -8,6 +8,11 @@
#ifndef _CX20442_CODEC_H
#define _CX20442_CODEC_H
+struct cx20442_codec {
+ struct snd_soc_component *component;
+ bool ready;
+};
+
extern struct tty_ldisc_ops v253_ops;
#endif
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
index b0033bf9be3a..83c6cbd40804 100644
--- a/sound/soc/codecs/cx2072x.c
+++ b/sound/soc/codecs/cx2072x.c
@@ -1681,9 +1681,9 @@ static void cx2072x_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id cx2072x_i2c_id[] = {
- { "cx20721" },
- { "cx20723" },
- {}
+ { .name = "cx20721" },
+ { .name = "cx20723" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id);
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index 94e59546c2fe..4df1a25e318b 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1238,7 +1238,7 @@ static int da7210_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id da7210_i2c_id[] = {
- { "da7210" },
+ { .name = "da7210" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7210_i2c_id);
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 19f69a523f22..4bf91ab2553a 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -1720,11 +1720,6 @@ static int da7213_set_component_pll(struct snd_soc_component *component,
return _da7213_set_component_pll(component, pll_id, source, fref, fout);
}
-/*
- * Select below from Sound Card, not Auto
- * SND_SOC_DAIFMT_CBC_CFC
- * SND_SOC_DAIFMT_CBP_CFP
- */
static const u64 da7213_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_I2S |
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
@@ -2271,7 +2266,7 @@ static const struct dev_pm_ops da7213_pm = {
};
static const struct i2c_device_id da7213_i2c_id[] = {
- { "da7213" },
+ { .name = "da7213" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index 5c80839704c7..93bd045ed870 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -3282,8 +3282,8 @@ static int da7218_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id da7218_i2c_id[] = {
- { "da7217", DA7217_DEV_ID },
- { "da7218", DA7218_DEV_ID },
+ { .name = "da7217", .driver_data = DA7217_DEV_ID },
+ { .name = "da7218", .driver_data = DA7218_DEV_ID },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7218_i2c_id);
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index ec4059f381be..f0874d891e12 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -2712,7 +2712,7 @@ static int da7219_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id da7219_i2c_id[] = {
- { "da7219", },
+ { .name = "da7219" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da7219_i2c_id);
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 140e449d3ef4..12d1ac98461e 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1547,7 +1547,7 @@ err:
}
static const struct i2c_device_id da732x_i2c_id[] = {
- { "da7320"},
+ { .name = "da7320" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da732x_i2c_id);
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index a52276e32f2f..a88c13533145 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1513,7 +1513,7 @@ static int da9055_i2c_probe(struct i2c_client *i2c)
* and PMIC, which must be different to operate together.
*/
static const struct i2c_device_id da9055_i2c_id[] = {
- { "da9055-codec" },
+ { .name = "da9055-codec" },
{ }
};
MODULE_DEVICE_TABLE(i2c, da9055_i2c_id);
diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c
index 564af5c04dbb..6fe3ae41afbd 100644
--- a/sound/soc/codecs/es8311.c
+++ b/sound/soc/codecs/es8311.c
@@ -955,7 +955,7 @@ static int es8311_i2c_probe(struct i2c_client *i2c_client)
}
static const struct i2c_device_id es8311_id[] = {
- { "es8311" },
+ { .name = "es8311" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8311_id);
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 9245c33700de..3abe77423f29 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -12,7 +12,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <sound/pcm.h>
@@ -895,8 +894,8 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client)
}
static const struct i2c_device_id es8316_i2c_id[] = {
- {"es8316" },
- {}
+ { .name = "es8316" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, es8316_i2c_id);
diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c
index 605375b154c8..b926340256be 100644
--- a/sound/soc/codecs/es8323.c
+++ b/sound/soc/codecs/es8323.c
@@ -18,7 +18,6 @@
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -787,7 +786,7 @@ static int es8323_i2c_probe(struct i2c_client *i2c_client)
}
static const struct i2c_device_id es8323_i2c_id[] = {
- { "es8323" },
+ { .name = "es8323" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8323_i2c_id);
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index 55a65ef99208..a79b2da35099 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -1347,8 +1347,8 @@ static void es8326_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id es8326_i2c_id[] = {
- {"es8326" },
- {}
+ { .name = "es8326" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, es8326_i2c_id);
diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c
index 56bfbe9261ce..36a4987e1a59 100644
--- a/sound/soc/codecs/es8328-i2c.c
+++ b/sound/soc/codecs/es8328-i2c.c
@@ -16,8 +16,8 @@
#include "es8328.h"
static const struct i2c_device_id es8328_id[] = {
- { "es8328" },
- { "es8388" },
+ { .name = "es8328" },
+ { .name = "es8388" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8328_id);
diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c
index 0b9406e93c0e..e8747bc24433 100644
--- a/sound/soc/codecs/es8375.c
+++ b/sound/soc/codecs/es8375.c
@@ -752,7 +752,7 @@ static void es8375_i2c_shutdown(struct i2c_client *i2c)
}
static const struct i2c_device_id es8375_id[] = {
- {"es8375"},
+ { .name = "es8375" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8375_id);
diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c
index 449d9574b03a..3484c87853cb 100644
--- a/sound/soc/codecs/es8389.c
+++ b/sound/soc/codecs/es8389.c
@@ -1015,7 +1015,7 @@ MODULE_DEVICE_TABLE(of, es8389_if_dt_ids);
#endif
static const struct i2c_device_id es8389_i2c_id[] = {
- {"es8389"},
+ { .name = "es8389" },
{ }
};
MODULE_DEVICE_TABLE(i2c, es8389_i2c_id);
diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c
new file mode 100644
index 000000000000..1122455aab77
--- /dev/null
+++ b/sound/soc/codecs/es9356.c
@@ -0,0 +1,1143 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// es9356.c -- SoundWire codec driver
+//
+// Copyright(c) 2025 Everest Semiconductor Co., Ltd
+//
+//
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <linux/pm_runtime.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_regmap.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <sound/jack.h>
+#include <sound/sdca_asoc.h>
+#include "es9356.h"
+
+struct es9356_sdw_priv {
+ struct sdw_slave *slave;
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_component *component;
+ struct snd_soc_jack *hs_jack;
+
+ /* lock for irq*/
+ struct mutex disable_irq_lock;
+
+ /* lock for pde*/
+ struct mutex pde_lock;
+
+ bool hw_init;
+ bool first_hw_init;
+ int jack_type;
+ bool disable_irq;
+
+ struct delayed_work interrupt_handle_work;
+ struct delayed_work button_detect_work;
+ unsigned int sdca_status;
+};
+
+static int es9356_sdw_component_probe(struct snd_soc_component *component)
+{
+ struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
+
+ es9356->component = component;
+
+ return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -9600, 12, 0);
+static const DECLARE_TLV_DB_SCALE(amic_gain_tlv, 0, 3, 0);
+static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 6, 0);
+
+static const struct snd_kcontrol_new es9356_sdca_controls[] = {
+ SDCA_SINGLE_Q78_TLV("FU41 Left Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_L),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU41 Right Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_R),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU36 Left Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU36 Right Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU33 Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU33, ES9356_SDCA_CTL_FU_CH_GAIN, 0),
+ ES9356_GAIN_MIN, ES9356_AMIC_GAIN_MAX, ES9356_AMIC_GAIN_STEP, amic_gain_tlv),
+ SDCA_SINGLE_Q78_TLV("FU21 Left Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_L),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU21 Right Playback Volume",
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_R),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU113 Left Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU113 Right Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_R),
+ ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv),
+ SDCA_SINGLE_Q78_TLV("FU11 Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU11, ES9356_SDCA_CTL_FU_CH_GAIN, 0),
+ ES9356_GAIN_MIN, ES9356_DMIC_GAIN_MAX, ES9356_DMIC_GAIN_STEP, dmic_gain_tlv),
+};
+
+static const char *const es9356_left_mux_txt[] = {
+ "Left",
+ "Right",
+};
+
+static const char *const es9356_right_mux_txt[] = {
+ "Right",
+ "Left",
+};
+
+static const struct soc_enum es9356_left_mux_enum =
+ SOC_ENUM_SINGLE(ES9356_DAC_SWAP, 1,
+ ARRAY_SIZE(es9356_left_mux_txt), es9356_left_mux_txt);
+static const struct soc_enum es9356_right_mux_enum =
+ SOC_ENUM_SINGLE(ES9356_DAC_SWAP, 0,
+ ARRAY_SIZE(es9356_right_mux_txt), es9356_right_mux_txt);
+
+static const struct snd_kcontrol_new es9356_left_mux_controls =
+ SOC_DAPM_ENUM("Channel MUX", es9356_left_mux_enum);
+static const struct snd_kcontrol_new es9356_right_mux_controls =
+ SOC_DAPM_ENUM("Channel MUX", es9356_right_mux_enum);
+
+static const struct snd_soc_dapm_widget es9356_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("PDM_DIN"),
+
+ SND_SOC_DAPM_SUPPLY("DMIC Clock", ES9356_DMIC_GPIO, 1, 1, NULL, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP4RX", "DP4 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX", "DP1 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PGA("IF DP3RXL", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("IF DP3RXR", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Left Channel MUX", SND_SOC_NOPM, 0, 0, &es9356_left_mux_controls),
+ SND_SOC_DAPM_MUX("Right Channel MUX", SND_SOC_NOPM, 0, 0, &es9356_right_mux_controls),
+
+ SND_SOC_DAPM_DAC("FU 21 Left", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1),
+ SND_SOC_DAPM_DAC("FU 21 Right", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1),
+ SND_SOC_DAPM_DAC("FU 41 Left", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1),
+ SND_SOC_DAPM_DAC("FU 41 Right", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1),
+ SND_SOC_DAPM_DAC("FU 113 Left", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1),
+ SND_SOC_DAPM_DAC("FU 113 Right", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1),
+ SND_SOC_DAPM_DAC("FU 36 Left", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1),
+ SND_SOC_DAPM_DAC("FU 36 Right", NULL,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1),
+};
+
+static const struct snd_soc_dapm_route es9356_audio_map[] = {
+ {"FU 36 Left", NULL, "MIC1"},
+ {"FU 36 Right", NULL, "MIC1"},
+ {"DP2TX", NULL, "FU 36 Left"},
+ {"DP2TX", NULL, "FU 36 Right"},
+
+ {"PDM_DIN", NULL, "DMIC Clock"},
+ {"FU 113 Left", NULL, "PDM_DIN"},
+ {"FU 113 Right", NULL, "PDM_DIN"},
+ {"DP1TX", NULL, "FU 113 Left"},
+ {"DP1TX", NULL, "FU 113 Right"},
+
+ {"FU 41 Left", NULL, "DP4RX"},
+ {"FU 41 Right", NULL, "DP4RX"},
+
+ {"IF DP3RXL", NULL, "DP3RX"},
+ {"IF DP3RXR", NULL, "DP3RX"},
+
+ {"Left Channel MUX", "Left", "IF DP3RXL"},
+ {"Left Channel MUX", "Right", "IF DP3RXR"},
+ {"Right Channel MUX", "Left", "IF DP3RXL"},
+ {"Right Channel MUX", "Right", "IF DP3RXR"},
+
+ {"FU 21 Left", NULL, "Left Channel MUX"},
+ {"FU 21 Right", NULL, "Right Channel MUX"},
+
+ {"SPK", NULL, "FU 21 Left"},
+ {"SPK", NULL, "FU 21 Right"},
+
+ {"HP", NULL, "FU 41 Left"},
+ {"HP", NULL, "FU 41 Right"},
+};
+
+static int es9356_set_jack_detect(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ es9356->hs_jack = hs_jack;
+
+ /* we can only resume if the device was initialized at least once */
+ if (!es9356->first_hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+ /* pm_runtime not enabled yet */
+ dev_info(component->dev, "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ if (es9356->hs_jack)
+ sdw_write_no_pm(es9356->slave, SDW_SCP_SDCA_INTMASK1,
+ (SDW_SCP_SDCA_INTMASK_SDCA_7 | SDW_SCP_SDCA_INTMASK_SDCA_5 | SDW_SCP_SDCA_INTMASK_SDCA_1));
+
+ pm_runtime_mark_last_busy(component->dev);
+ pm_runtime_put_autosuspend(component->dev);
+
+ return 0;
+}
+static const struct snd_soc_component_driver snd_soc_es9356_sdw_component = {
+ .probe = es9356_sdw_component_probe,
+ .controls = es9356_sdca_controls,
+ .num_controls = ARRAY_SIZE(es9356_sdca_controls),
+ .dapm_widgets = es9356_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es9356_dapm_widgets),
+ .dapm_routes = es9356_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(es9356_audio_map),
+ .set_jack = es9356_set_jack_detect,
+ .endianness = 1,
+};
+
+static int es9356_sdw_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 es9356_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int es9356_sdca_button(unsigned int *buffer)
+{
+ int cur_button = -1;
+
+ if (*(buffer + 1) | *(buffer + 2))
+ return -EINVAL;
+ switch (*buffer) {
+ case 0x00:
+ cur_button = 0;
+ break;
+ case 0x20:
+ cur_button = SND_JACK_BTN_4;
+ break;
+ case 0x10:
+ cur_button = SND_JACK_BTN_2;
+ break;
+ case 0x08:
+ cur_button = SND_JACK_BTN_1;
+ break;
+ case 0x02:
+ cur_button = SND_JACK_BTN_3;
+ break;
+ case 0x01:
+ cur_button = SND_JACK_BTN_0;
+ break;
+ default:
+ break;
+ }
+
+ return cur_button;
+}
+
+static int es9356_sdca_button_detect(struct es9356_sdw_priv *es9356)
+{
+ unsigned int btn_type = 0, offset, idx, val, owner;
+ unsigned int button[3];
+ int ret;
+
+ ret = regmap_read(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, ES9356_SDCA_ENT_HID01, ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner);
+ if (ret < 0 || owner == 0x01)
+ return 0;
+
+ ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID, &offset);
+ if (ret < 0)
+ goto button_det_end;
+
+ for (idx = 0; idx < ARRAY_SIZE(button); idx++) {
+ ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID + offset + idx, &val);
+ if (ret < 0)
+ goto button_det_end;
+ button[idx] = val;
+ }
+
+ btn_type = es9356_sdca_button(&button[0]);
+
+button_det_end:
+ if (owner == 0x00)
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_HID, ES9356_SDCA_ENT_HID01, ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+
+ return btn_type;
+}
+
+static int es9356_sdca_headset_detect(struct es9356_sdw_priv *es9356)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_read(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_DETECTED_MODE, 0), &reg);
+
+ if (ret < 0)
+ goto io_error;
+
+ switch (reg) {
+ case 0x00:
+ es9356->jack_type = 0;
+ break;
+ case 0x03:
+ es9356->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 0x04:
+ es9356->jack_type = SND_JACK_HEADSET;
+ break;
+ default:
+ es9356->jack_type = 0;
+ return -1;
+ }
+
+ if (reg) {
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_SELECTED_MODE, 0), reg);
+ regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x75);
+ } else {
+ regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4);
+ }
+
+ return 0;
+
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static void es9356_interrupt_handler(struct work_struct *work)
+{
+ struct es9356_sdw_priv *es9356 =
+ container_of(work, struct es9356_sdw_priv, interrupt_handle_work.work);
+ int ret, btn_type = 0;
+
+ if (!es9356->hs_jack)
+ return;
+
+ if (!es9356->component->card || !es9356->component->card->instantiated)
+ return;
+
+ /* Handling different types of interrupts based on the mask bit */
+ if (es9356->sdca_status & SDW_SCP_SDCA_INT_SDCA_7) {
+ btn_type = es9356_sdca_button_detect(es9356);
+ if (btn_type < 0)
+ return;
+ } else {
+ ret = es9356_sdca_headset_detect(es9356);
+ if (ret < 0)
+ return;
+ }
+
+ if (es9356->jack_type != SND_JACK_HEADSET)
+ btn_type = 0;
+
+ snd_soc_jack_report(es9356->hs_jack, es9356->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4);
+
+ if (btn_type) {
+ snd_soc_jack_report(es9356->hs_jack, es9356->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4);
+ mod_delayed_work(system_power_efficient_wq,
+ &es9356->button_detect_work, msecs_to_jiffies(280));
+ }
+}
+
+static void es9356_button_detect_handler(struct work_struct *work)
+{
+ struct es9356_sdw_priv *es9356 =
+ container_of(work, struct es9356_sdw_priv, button_detect_work.work);
+ int ret, idx, btn_type = 0;
+ unsigned int reg, offset;
+ unsigned int button[3];
+
+ /* Check headset */
+ ret = regmap_read(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_DETECTED_MODE, 0), &reg);
+
+ if (ret < 0)
+ goto io_error;
+
+ if (reg == 0x04) {
+ ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID, &offset);
+ if (ret < 0)
+ goto io_error;
+ for (idx = 0; idx < ARRAY_SIZE(button); idx++) {
+ ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID + offset + idx, &reg);
+ if (ret < 0)
+ goto io_error;
+ button[idx] = reg;
+ }
+ btn_type = es9356_sdca_button(&button[0]);
+ if (btn_type < 0)
+ return;
+ }
+
+ snd_soc_jack_report(es9356->hs_jack, es9356->jack_type | btn_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4);
+
+ if (btn_type) {
+ snd_soc_jack_report(es9356->hs_jack, es9356->jack_type,
+ SND_JACK_HEADSET |
+ SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 |
+ SND_JACK_BTN_4);
+ mod_delayed_work(system_power_efficient_wq,
+ &es9356->button_detect_work, msecs_to_jiffies(280));
+ }
+
+ return;
+io_error:
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+}
+
+static int es9356_pde_transition_delay(struct es9356_sdw_priv *es9356, unsigned char func,
+ unsigned char entity, unsigned char ps)
+{
+ unsigned int retries = 10, val;
+
+ /* waiting for Actual PDE becomes to PS0/PS3 */
+ while (retries) {
+ regmap_read(es9356->regmap,
+ SDW_SDCA_CTL(func, entity, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val);
+ if (val == ps)
+ return 1;
+
+ usleep_range(1000, 1500);
+ retries--;
+ }
+ if (!retries) {
+ dev_dbg(&es9356->slave->dev, "%s PDE is NOT %s", __func__, ps?"PS3":"PS0");
+ }
+ return 0;
+}
+
+static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigned int *rate)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+ unsigned char func, cs_entity, pde_entity;
+ int ret;
+
+ switch (dai->id) {
+ case ES9356_DMIC:
+ func = FUNC_NUM_MIC;
+ cs_entity = ES9356_SDCA_ENT_CS113;
+ pde_entity = ES9356_SDCA_ENT_PDE11;
+ break;
+ case ES9356_AMP:
+ func = FUNC_NUM_AMP;
+ cs_entity = ES9356_SDCA_ENT_CS21;
+ pde_entity = ES9356_SDCA_ENT_PDE23;
+ break;
+ case ES9356_JACK_IN:
+ func = FUNC_NUM_UAJ;
+ cs_entity = ES9356_SDCA_ENT_CS36;
+ pde_entity = ES9356_SDCA_ENT_PDE34;
+ break;
+ case ES9356_JACK_OUT:
+ func = FUNC_NUM_UAJ;
+ cs_entity = ES9356_SDCA_ENT_CS41;
+ pde_entity = ES9356_SDCA_ENT_PDE47;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* power state changes are not independent across functions */
+ mutex_lock(&es9356->pde_lock);
+ ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3);
+ if (ret) {
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0);
+ es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0);
+ } else
+ dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3);
+
+ mutex_unlock(&es9356->pde_lock);
+
+ if (rate)
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(func, cs_entity, ES9356_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), *rate);
+
+ return 0;
+}
+
+static int es9356_sdw_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 es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ unsigned char ps0 = 0x0;
+ unsigned int rate;
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ if (!es9356->slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+
+ port_config.num = dai->id;
+
+ ret = sdw_stream_add_slave(es9356->slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return -EINVAL;
+ }
+
+ switch (params_rate(params)) {
+ case 16000:
+ rate = ES9356_SDCA_RATE_16000HZ;
+ break;
+ case 44100:
+ rate = ES9356_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ rate = ES9356_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ rate = ES9356_SDCA_RATE_96000HZ;
+ break;
+ default:
+ dev_err(component->dev, "%s: Rate %d is not supported\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ ret = es9356_power_state(dai, ps0, &rate);
+ if (ret) {
+ dev_err(component->dev, "%s: Invalid dai id: %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int es9356_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ unsigned char ps3 = 0x3;
+ int ret;
+
+ if (!es9356->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(es9356->slave, sdw_stream);
+
+ ret = es9356_power_state(dai, ps3, NULL);
+ if (ret) {
+ dev_err(component->dev, "%s: Invalid dai id: %d\n",
+ __func__, dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es9356_sdw_ops = {
+ .hw_params = es9356_sdw_pcm_hw_params,
+ .hw_free = es9356_sdw_pcm_hw_free,
+ .set_stream = es9356_sdw_set_sdw_stream,
+ .shutdown = es9356_sdw_shutdown,
+};
+
+static struct snd_soc_dai_driver es9356_sdw_dai[] = {
+ {
+ .name = "es9356-sdp-aif4",
+ .id = ES9356_DMIC,
+ .capture = {
+ .stream_name = "DP1 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &es9356_sdw_ops,
+ },
+ {
+ .name = "es9356-sdp-aif2",
+ .id = ES9356_JACK_IN,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &es9356_sdw_ops,
+ },
+ {
+ .name = "es9356-sdp-aif3",
+ .id = ES9356_AMP,
+ .playback = {
+ .stream_name = "DP3 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &es9356_sdw_ops,
+ },
+ {
+ .name = "es9356-sdp-aif1",
+ .id = ES9356_JACK_OUT,
+ .playback = {
+ .stream_name = "DP4 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ },
+ .ops = &es9356_sdw_ops,
+ },
+};
+
+static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU33, ES9356_SDCA_CTL_FU_CH_GAIN, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU11, ES9356_SDCA_CTL_FU_CH_GAIN, 0):
+ return 2;
+ default:
+ return 1;
+ }
+}
+
+static struct regmap_sdw_mbq_cfg es9356_mbq_config = {
+ .mbq_size = es9356_sdca_mbq_size,
+};
+
+static bool es9356_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES9356_BUF_ADDR_HID:
+ case ES9356_HID_BYTE2:
+ case ES9356_HID_BYTE3:
+ case ES9356_HID_BYTE4:
+ case SDW_SDCA_CTL(FUNC_NUM_HID, ES9356_SDCA_ENT_HID01, ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_DETECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_SELECTED_MODE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_PDE23, ES9356_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_PDE23, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_PDE11, ES9356_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_PDE11, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE47, ES9356_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE47, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE34, ES9356_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE34, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0):
+ case ES9356_FLAGS_HP:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config es9356_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16,
+ .volatile_reg = es9356_sdca_volatile_register,
+ .max_register = 0x45ffffff,
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static void es9356_register_init(struct es9356_sdw_priv *es9356)
+{
+ regmap_write(es9356->regmap, ES9356_STATE, 0x02);
+ regmap_write(es9356->regmap, ES9356_ENDPOINT_MODE, 0x24);
+ regmap_write(es9356->regmap, ES9356_PRE_DIV_CTL, 0x00);
+ regmap_write(es9356->regmap, ES9356_ADC_OSR, 0x18);
+ regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x13);
+ regmap_write(es9356->regmap, ES9356_DAC_OSR, 0x16);
+ regmap_write(es9356->regmap, ES9356_CLK_CTL, 0x0f);
+ regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01);
+ regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30);
+
+ regmap_write(es9356->regmap, ES9356_DETCLK_CTL, 0x51);
+ regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x10);
+ regmap_write(es9356->regmap, ES9356_MICBIAS_CTL, 0x10);
+ regmap_write(es9356->regmap, ES9356_HPDETECT_CTL, 0x07);
+ regmap_write(es9356->regmap, ES9356_ADC_ANA, 0x30);
+ regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xa8);
+ regmap_write(es9356->regmap, ES9356_ADC_INT, 0xaa);
+ regmap_write(es9356->regmap, ES9356_ADC_LP, 0x19);
+ regmap_write(es9356->regmap, ES9356_VMID1SEL, 0xbc);
+ regmap_write(es9356->regmap, ES9356_VMID_TIME, 0x0b);
+ regmap_write(es9356->regmap, ES9356_STATE_TIME, 0xbb);
+ regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x77);
+ regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4);
+ regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15);
+ regmap_write(es9356->regmap, ES9356_KEY_PRESS_TIME, 0xff);
+ regmap_write(es9356->regmap, ES9356_KEY_RELEASE_TIME, 0xff);
+ regmap_write(es9356->regmap, ES9356_KEY_HOLD_TIME, 0x0f);
+ regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00);
+ regmap_write(es9356->regmap, ES9356_KEYD_DETECT, 0x18);
+ regmap_write(es9356->regmap, ES9356_MICBIAS_RES, 0x03);
+ regmap_write(es9356->regmap, ES9356_BUTTON_CHARGE, 0x00);
+ regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x13);
+ regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4);
+
+ regmap_write(es9356->regmap, ES9356_SPK_VOLUME, 0x33);
+ regmap_write(es9356->regmap, ES9356_DAC_VROI, 0x01);
+ regmap_write(es9356->regmap, ES9356_DAC_LP, 0x00);
+ regmap_write(es9356->regmap, ES9356_HP_IBIAS, 0x04);
+ regmap_write(es9356->regmap, ES9356_HP_LP, 0x03);
+ regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x65);
+ regmap_write(es9356->regmap, ES9356_SPKBIAS_COMP, 0x09);
+ regmap_write(es9356->regmap, ES9356_VMID1STL, 0x00);
+ regmap_write(es9356->regmap, ES9356_VMID2STL, 0x00);
+ regmap_write(es9356->regmap, ES9356_VSEL, 0xfc);
+
+ regmap_write(es9356->regmap, ES9356_IBIASGEN, 0x10);
+ regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x0d);
+ regmap_write(es9356->regmap, ES9356_STATE, 0x0e);
+ regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x00);
+ regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x08);
+
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME);
+
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME);
+
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME);
+
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME);
+}
+
+static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev);
+
+ if (es9356->hw_init)
+ return 0;
+
+ es9356->disable_irq = false;
+
+ regcache_cache_only(es9356->regmap, false);
+
+ if (es9356->first_hw_init) {
+ regcache_cache_bypass(es9356->regmap, true);
+ } else {
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(&slave->dev);
+
+ es9356_register_init(es9356);
+ }
+ pm_runtime_get_noresume(&slave->dev);
+
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_XU12, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_XU22, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_XU42, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_XU36, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01);
+ regmap_write(es9356->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40);
+
+ if (es9356->first_hw_init) {
+ regcache_cache_bypass(es9356->regmap, false);
+ regcache_mark_dirty(es9356->regmap);
+ } else
+ es9356->first_hw_init = true;
+
+ es9356->hw_init = true;
+
+ pm_runtime_mark_last_busy(&slave->dev);
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ return 0;
+}
+
+static int es9356_sdw_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED) {
+ es9356->hw_init = false;
+ cancel_delayed_work_sync(&es9356->interrupt_handle_work);
+ cancel_delayed_work_sync(&es9356->button_detect_work);
+ regcache_cache_only(es9356->regmap, true);
+ }
+
+ if (status == SDW_SLAVE_ATTACHED) {
+ if (es9356->hs_jack)
+ sdw_write_no_pm(es9356->slave, SDW_SCP_SDCA_INTMASK1,
+ (SDW_SCP_SDCA_INTMASK_SDCA_7 | SDW_SCP_SDCA_INTMASK_SDCA_5 | SDW_SCP_SDCA_INTMASK_SDCA_1));
+ }
+
+ if (es9356->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ return es9356_sdca_io_init(&slave->dev, slave);
+}
+
+static int es9356_sdw_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->paging_support = true;
+
+ /*
+ * first we need to allocate memory for set bits in port lists
+ * the port allocation is completely arbitrary:
+ * DP0 is not supported
+ * DP3 and DP4 is sink
+ * DP1 and DP2 is source
+ */
+ prop->source_ports = BIT(1) | BIT(2);
+ prop->sink_ports = BIT(3) | BIT(4);
+
+ 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;
+ i++;
+ }
+
+ /* do this again for sink now */
+ 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;
+ j++;
+ }
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static int es9356_sdw_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev);
+ unsigned int sdca_cascade, scp_sdca_stat1 = 0;
+ int count = 0, retry = 3;
+ int ret, stat, reg;
+
+ mutex_lock(&es9356->disable_irq_lock);
+
+ ret = sdw_read_no_pm(es9356->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ es9356->sdca_status = ret;
+
+ do {
+ reg = sdw_read_no_pm(es9356->slave, SDW_SCP_SDCA_INT1);
+ if (reg < 0)
+ goto io_error;
+ if (reg & SDW_SCP_SDCA_INTMASK_SDCA_1) {
+ ret = sdw_update_no_pm(es9356->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_1, SDW_SCP_SDCA_INT_SDCA_1);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ if (reg & SDW_SCP_SDCA_INTMASK_SDCA_5) {
+ ret = sdw_update_no_pm(es9356->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_5, SDW_SCP_SDCA_INT_SDCA_5);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ if (reg & SDW_SCP_SDCA_INTMASK_SDCA_7) {
+ ret = sdw_update_no_pm(es9356->slave, SDW_SCP_SDCA_INT1,
+ SDW_SCP_SDCA_INT_SDCA_7, SDW_SCP_SDCA_INT_SDCA_7);
+ if (ret < 0)
+ goto io_error;
+ }
+
+ ret = sdw_read_no_pm(es9356->slave, SDW_DP0_INT);
+ if (ret < 0)
+ goto io_error;
+ sdca_cascade = ret & SDW_DP0_SDCA_CASCADE;
+
+ ret = sdw_read_no_pm(es9356->slave, SDW_SCP_SDCA_INT1);
+ if (ret < 0)
+ goto io_error;
+ scp_sdca_stat1 = ret &
+ (SDW_SCP_SDCA_INTMASK_SDCA_1 | SDW_SCP_SDCA_INTMASK_SDCA_5 | SDW_SCP_SDCA_INTMASK_SDCA_7);
+
+ stat = scp_sdca_stat1 || sdca_cascade;
+
+ count++;
+ } while (stat != 0 && count < retry);
+
+ /* The 280 ms figure was determined through testing */
+ if (status->sdca_cascade && !es9356->disable_irq)
+ mod_delayed_work(system_power_efficient_wq,
+ &es9356->interrupt_handle_work, msecs_to_jiffies(280));
+
+ mutex_unlock(&es9356->disable_irq_lock);
+ return 0;
+
+io_error:
+ mutex_unlock(&es9356->disable_irq_lock);
+ pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret);
+ return ret;
+}
+
+static const struct sdw_slave_ops es9356_sdw_slave_ops = {
+ .read_prop = es9356_sdw_read_prop,
+ .interrupt_callback = es9356_sdw_interrupt_callback,
+ .update_status = es9356_sdw_update_status,
+};
+
+static int es9356_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave)
+{
+ struct es9356_sdw_priv *es9356;
+ int ret;
+
+ es9356 = devm_kzalloc(dev, sizeof(*es9356), GFP_KERNEL);
+ if (!es9356)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, es9356);
+
+ es9356->slave = slave;
+ es9356->regmap = regmap;
+ mutex_init(&es9356->disable_irq_lock);
+ mutex_init(&es9356->pde_lock);
+
+ regcache_cache_only(es9356->regmap, true);
+
+ es9356->hw_init = false;
+ es9356->first_hw_init = false;
+
+ INIT_DELAYED_WORK(&es9356->interrupt_handle_work,
+ es9356_interrupt_handler);
+ INIT_DELAYED_WORK(&es9356->button_detect_work,
+ es9356_button_detect_handler);
+
+ ret = devm_snd_soc_register_component(dev,
+ &snd_soc_es9356_sdw_component,
+ es9356_sdw_dai,
+ ARRAY_SIZE(es9356_sdw_dai));
+ if (ret) {
+ dev_err_probe(dev, ret, "Failed to register component\n");
+ return ret;
+ }
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_enable(dev);
+
+ return 0;
+}
+
+static int es9356_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, &es9356_sdca_regmap, &es9356_mbq_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return es9356_sdca_init(&slave->dev, regmap, slave);
+}
+
+static void es9356_sdw_remove(struct sdw_slave *slave)
+{
+ struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev);
+
+ if (es9356->hw_init) {
+ cancel_delayed_work_sync(&es9356->interrupt_handle_work);
+ cancel_delayed_work_sync(&es9356->button_detect_work);
+ }
+
+ if (es9356->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ mutex_destroy(&es9356->disable_irq_lock);
+ mutex_destroy(&es9356->pde_lock);
+}
+
+static const struct sdw_device_id es9356_sdw_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x04b3, 0x9356, 0x02, 0, 0),
+ SDW_SLAVE_ENTRY_EXT(0x04b3, 0x9356, 0x03, 0, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, es9356_sdw_id);
+
+static int es9356_sdca_dev_suspend(struct device *dev)
+{
+ struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev);
+
+ cancel_delayed_work_sync(&es9356->interrupt_handle_work);
+ cancel_delayed_work_sync(&es9356->button_detect_work);
+
+ regcache_cache_only(es9356->regmap, true);
+
+ return 0;
+}
+
+static int es9356_sdca_dev_system_suspend(struct device *dev)
+{
+ struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev);
+
+ mutex_lock(&es9356->disable_irq_lock);
+ es9356->disable_irq = true;
+ mutex_unlock(&es9356->disable_irq_lock);
+
+ return es9356_sdca_dev_suspend(dev);
+}
+
+#define es9356_PROBE_TIMEOUT 2000
+
+static int es9356_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev);
+ int ret;
+
+ if (!slave->unattach_request)
+ es9356->disable_irq = false;
+
+ ret = sdw_slave_wait_for_init(slave, es9356_PROBE_TIMEOUT);
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
+ return ret;
+ }
+
+ regcache_cache_only(es9356->regmap, false);
+ regcache_sync(es9356->regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops es9356_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(es9356_sdca_dev_system_suspend, es9356_sdca_dev_resume)
+ RUNTIME_PM_OPS(es9356_sdca_dev_suspend, es9356_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver es9356_sdw_driver = {
+ .driver = {
+ .name = "es9356",
+ .pm = pm_ptr(&es9356_sdca_pm),
+ },
+ .probe = es9356_sdw_probe,
+ .remove = es9356_sdw_remove,
+ .ops = &es9356_sdw_slave_ops,
+ .id_table = es9356_sdw_id,
+};
+module_sdw_driver(es9356_sdw_driver);
+
+MODULE_IMPORT_NS("SND_SOC_SDCA");
+MODULE_DESCRIPTION("ASoC ES9356 SDCA SDW codec driver");
+MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es9356.h b/sound/soc/codecs/es9356.h
new file mode 100644
index 000000000000..2a676f36590f
--- /dev/null
+++ b/sound/soc/codecs/es9356.h
@@ -0,0 +1,208 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#ifndef __ES9356_H__
+#define __ES9356_H__
+
+/*ES9356 Implementation-define*/
+#define ES9356_FLAGS_HP 0x2003
+#define ES9356_CSM_RESET 0x2020
+#define ES9356_FUC_RESET 0x2021
+#define ES9356_STATE 0x2022
+#define ES9356_VMID_TIME 0x2023
+#define ES9356_STATE_TIME 0x2024
+#define ES9356_HP_SPK_TIME 0x2025
+#define ES9356_WP_ENABLE 0x2026
+#define ES9356_DMIC_GPIO 0x2027
+#define ES9356_ENDPOINT_MODE 0x2028
+
+/*HP DETECT*/
+#define ES9356_HP_TYPE 0x2029
+#define ES9356_HP_DETECTTIME 0x202A
+#define ES9356_MICBIAS_SEL 0x202B
+#define ES9356_KEY_PRESS_TIME 0x202C
+#define ES9356_KEY_RELEASE_TIME 0x202D
+#define ES9356_KEY_HOLD_TIME 0x202E
+#define ES9356_BTSEL_REF 0x202F
+#define ES9356_BUTTON_CHARGE 0x2030
+
+#define ES9356_KEYD_DETECT 0x2031
+#define ES9356_DPEN_TIME 0x2032
+#define ES9356_TIMER_CHECK 0x2033
+#define ES9356_IBIASGEN 0x2041
+#define ES9356_VMID1SEL 0x2042
+#define ES9356_VMID1STL 0x2043
+#define ES9356_VMID2SEL 0x2044
+#define ES9356_VMID2STL 0x2045
+#define ES9356_VSEL 0x2046
+#define ES9356_MICBIAS_CTL 0x2047
+#define ES9356_HPDETECT_CTL 0x2048
+#define ES9356_MICBIAS_RES 0x2049
+
+/*CLK*/
+#define ES9356_CLK_SEL 0x2050
+#define ES9356_CLK_CTL 0x2051
+#define ES9356_DETCLK_CTL 0x2052
+#define ES9356_CPCLK_CTL 0x2053
+#define ES9356_SPKCLK_CTL 0x2054
+#define ES9356_PRE_DIV_CTL 0x2055
+#define ES9356_DLL_MODE 0x2056
+#define ES9356_ANACLK_SEL 0x2057
+#define ES9356_OSRCLK_SEL 0x2058
+#define ES9356_DSPCLK_SEL 0x2059
+#define ES9356_SPK9M_MODE 0x205a
+
+/*ADC DIG CTL*/
+#define ES9356_DMIC_POL 0x2061
+#define ES9356_ADC_SWAP 0x2062
+#define ES9356_ADC_OSR 0x2063
+#define ES9356_ADC_OSRGAIN 0x2064
+#define ES9356_ADC_CLEARRAM 0x2065
+#define ES9356_ADC_RAMP 0x2066
+#define ES9356_ADC_HPF1 0x2067
+#define ES9356_ADC_HPF2 0x2068
+#define ES9356_ADC_ALC 0x206C
+#define ES9356_ALC_LEVEL 0x206D
+#define ES9356_ALC_RAMP_WINSIZE 0x206E
+
+/*ADC ANA CTL*/
+#define ES9356_ADC_REF_EN 0x2080
+#define ES9356_ADC_AMIC_CTL 0x2081
+#define ES9356_ADC_ANA 0x2082
+#define ES9356_PGA_CTL 0x2083
+#define ES9356_ADC_INT 0x2084
+#define ES9356_ADC_VCM 0x2085
+#define ES9356_ADC_VRPBIAS 0x2086
+#define ES9356_ADC_LP 0x2087
+
+/*DAC DIG CTL*/
+#define ES9356_DAC_FSMODE 0x2090
+#define ES9356_DAC_OSR 0x2091
+#define ES9356_DAC_INV 0x2092
+#define ES9356_DAC_RAMP 0x2093
+#define ES9356_DAC_VPPSCALE 0x2094
+#define ES9356_DAC_SWAP 0x2097
+#define ES9356_SPKCMP_VPPSC 0x20A0
+#define ES9356_CALIBRATION_TIME 0x20A1
+#define ES9356_CALIBRATION_SETTING 0x20A2
+#define ES9356_DAC_OFFSET_LH 0x20A3
+#define ES9356_DAC_OFFSET_LL 0x20A4
+#define ES9356_DAC_OFFSET_RH 0x20A5
+#define ES9356_DAC_OFFSET_RL 0x20A6
+
+/*DAC ANA CTL*/
+#define ES9356_DAC_REF_EN 0x20B0
+#define ES9356_DAC_ENABLE 0x20B1
+#define ES9356_DAC_VROI 0x20B2
+#define ES9356_DAC_LP 0x20B3
+
+/*HP CTL*/
+#define ES9356_CHARGEPUMP_CTL 0x20C0
+#define ES9356_CPLDO_CTL 0x20C1
+#define ES9356_HP_REF_CTL 0x20C2
+#define ES9356_HP_IBIAS 0x20C3
+#define ES9356_HP_EN 0x20C4
+#define ES9356_HP_VOLUME 0x20C5
+#define ES9356_HP_LP 0x20C6
+
+/*SPK CTL*/
+#define ES9356_SPKLDO_CTL 0x20D0
+#define ES9356_CLASSD_CTL 0x20D1
+#define ES9356_SPK_HBDG 0x20D5
+#define ES9356_SPK_VOLUME 0x20D7
+#define ES9356_SPK_SCP 0x20D8
+#define ES9356_SPK_DT 0x20D9
+#define ES9356_SPK_OTP 0x20DA
+#define ES9356_SPKBIAS_COMP 0x20DB
+
+/* ES9356 SDCA Control - function number */
+#define FUNC_NUM_UAJ 0x01
+#define FUNC_NUM_MIC 0x02
+#define FUNC_NUM_AMP 0x03
+#define FUNC_NUM_HID 0x04
+
+/* ES9356 SDCA entity */
+#define ES9356_SDCA_ENT0 0x00
+#define ES9356_SDCA_ENT_PDE11 0x03
+#define ES9356_SDCA_ENT_FU11 0x04
+#define ES9356_SDCA_ENT_XU12 0x05
+#define ES9356_SDCA_ENT_FU113 0x07
+#define ES9356_SDCA_ENT_CS113 0x09
+#define ES9356_SDCA_ENT_PPU11 0x0C
+
+#define ES9356_SDCA_ENT_CS21 0x02
+#define ES9356_SDCA_ENT_PPU21 0x03
+#define ES9356_SDCA_ENT_FU21 0X04
+#define ES9356_SDCA_ENT_XU22 0x06
+#define ES9356_SDCA_ENT_SAPU29 0x03
+#define ES9356_SDCA_ENT_PDE23 0x0B
+#define ES9356_SDCA_ENT_HID01 0x01
+
+#define ES9356_SDCA_ENT_CS41 0x02
+#define ES9356_SDCA_ENT_FU35 0x04
+#define ES9356_SDCA_ENT_XU42 0x06
+#define ES9356_SDCA_ENT_FU41 0x07
+#define ES9356_SDCA_ENT_PDE47 0x0E
+#define ES9356_SDCA_ENT_IT33 0x0F
+#define ES9356_SDCA_ENT_PDE34 0x10
+#define ES9356_SDCA_ENT_FU33 0x11
+#define ES9356_SDCA_ENT_XU36 0x13
+#define ES9356_SDCA_ENT_FU36 0x15
+#define ES9356_SDCA_ENT_CS36 0x17
+#define ES9356_SDCA_ENT_GE35 0x18
+
+/* ES9356 SDCA control */
+#define ES9356_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10
+#define ES9356_SDCA_CTL_FU_MUTE 0x01
+#define ES9356_SDCA_CTL_FU_VOLUME 0x02
+#define ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define ES9356_SDCA_CTL_SELECTED_MODE 0x01
+#define ES9356_SDCA_CTL_DETECTED_MODE 0x02
+#define ES9356_SDCA_CTL_REQ_POWER_STATE 0x01
+#define ES9356_SDCA_CTL_FU_CH_GAIN 0x0b
+#define ES9356_SDCA_CTL_FUNC_STATUS 0x10
+#define ES9356_SDCA_CTL_ACTUAL_POWER_STATE 0x10
+#define ES9356_SDCA_CTL_POSTURE_NUMBER 0x00
+
+/* ES9356 SDCA channel */
+#define CH_L 0x01
+#define CH_R 0x02
+#define MBQ 0x2000
+
+/* ES9356 HID*/
+#define ES9356_BUF_ADDR_HID 0x44000000
+#define ES9356_HID_BYTE2 0x44000001
+#define ES9356_HID_BYTE3 0x44000002
+#define ES9356_HID_BYTE4 0x44000003
+
+/* ES9356 Volume Setting*/
+#define ES9356_VU_BASE 768
+#define ES9356_OFFSET_HIGH 0x07F8
+#define ES9356_OFFSET_LOW 0x0007
+#define ES9356_DEFAULT_VOLUME 0x00
+#define ES9356_VOLUME_STEP 32
+#define ES9356_VOLUME_MIN -768
+#define ES9356_VOLUME_MAX 285
+#define ES9356_AMIC_GAIN_STEP 768
+#define ES9356_DMIC_GAIN_STEP 1536
+#define ES9356_GAIN_MIN 0
+#define ES9356_AMIC_GAIN_MAX 10
+#define ES9356_DMIC_GAIN_MAX 3
+
+enum {
+ ES9356_DMIC = 1, /* For dmic */
+ ES9356_JACK_IN, /* For headset mic */
+ ES9356_AMP, /* For speaker */
+ ES9356_JACK_OUT, /* For headphone */
+};
+
+enum {
+ ES9356_SDCA_RATE_16000HZ,
+ ES9356_SDCA_RATE_24000HZ,
+ ES9356_SDCA_RATE_32000HZ,
+ ES9356_SDCA_RATE_44100HZ,
+ ES9356_SDCA_RATE_48000HZ,
+ ES9356_SDCA_RATE_88200HZ,
+ ES9356_SDCA_RATE_96000HZ,
+};
+
+#endif
diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c
index 6f57a3aeecc8..a87a78390172 100644
--- a/sound/soc/codecs/framer-codec.c
+++ b/sound/soc/codecs/framer-codec.c
@@ -238,15 +238,13 @@ static int framer_dai_startup(struct snd_pcm_substream *substream,
return 0;
}
-static const u64 framer_dai_formats[] = {
- SND_SOC_POSSIBLE_DAIFMT_DSP_B,
-};
+static const u64 framer_dai_formats = SND_SOC_POSSIBLE_DAIFMT_DSP_B;
static const struct snd_soc_dai_ops framer_dai_ops = {
.startup = framer_dai_startup,
.set_tdm_slot = framer_dai_set_tdm_slot,
- .auto_selectable_formats = framer_dai_formats,
- .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats),
+ .auto_selectable_formats = &framer_dai_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver framer_dai_driver = {
diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c
index eda716f817b5..5f381fe063e8 100644
--- a/sound/soc/codecs/fs210x.c
+++ b/sound/soc/codecs/fs210x.c
@@ -1557,9 +1557,9 @@ static void fs210x_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id fs210x_i2c_id[] = {
- { "fs2104" },
- { "fs2105s" },
- {}
+ { .name = "fs2104" },
+ { .name = "fs2105s" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id);
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 2652fcf2a3a3..3220f9226e0b 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -911,12 +911,14 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol,
struct hdac_device *hdev = dev_to_hdac_dev(dev);
struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev);
struct hdac_hdmi_pcm *pcm;
- const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
+ const char *cvt_name;
ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
if (ret < 0)
return ret;
+ cvt_name = e->texts[ucontrol->value.enumerated.item[0]];
+
if (port == NULL)
return -EINVAL;
diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c
index 39bafefa6a18..084090ccef77 100644
--- a/sound/soc/codecs/idt821034.c
+++ b/sound/soc/codecs/idt821034.c
@@ -860,18 +860,17 @@ static int idt821034_dai_startup(struct snd_pcm_substream *substream,
return 0;
}
-static const u64 idt821034_dai_formats[] = {
+static const u64 idt821034_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
- SND_SOC_POSSIBLE_DAIFMT_DSP_B,
-};
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
static const struct snd_soc_dai_ops idt821034_dai_ops = {
.startup = idt821034_dai_startup,
.hw_params = idt821034_dai_hw_params,
.set_tdm_slot = idt821034_dai_set_tdm_slot,
.set_fmt = idt821034_dai_set_fmt,
- .auto_selectable_formats = idt821034_dai_formats,
- .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats),
+ .auto_selectable_formats = &idt821034_dai_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver idt821034_dai_driver = {
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index b7a94631d77d..fa1c23dda4eb 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1133,7 +1133,7 @@ static int isabelle_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id isabelle_i2c_id[] = {
- { "isabelle" },
+ { .name = "isabelle" },
{ }
};
MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id);
diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c
index 26cdb750cbca..06add7fb6737 100644
--- a/sound/soc/codecs/lm4857.c
+++ b/sound/soc/codecs/lm4857.c
@@ -128,7 +128,7 @@ static int lm4857_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id lm4857_i2c_id[] = {
- { "lm4857" },
+ { .name = "lm4857" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id);
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index 043030509795..6e2e292d143b 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1443,7 +1443,7 @@ static int lm49453_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id lm49453_i2c_id[] = {
- { "lm49453" },
+ { .name = "lm49453" },
{ }
};
MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id);
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index 528d5b167ecf..58a5798823d7 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -244,6 +244,11 @@ static const struct va_macro_data sm8250_va_data = {
.version = LPASS_CODEC_VERSION_1_0,
};
+static const struct va_macro_data sc7280_va_data = {
+ .has_swr_master = false,
+ .has_npl_clk = false,
+};
+
static const struct va_macro_data sm8450_va_data = {
.has_swr_master = true,
.has_npl_clk = true,
@@ -1755,7 +1760,7 @@ static const struct dev_pm_ops va_macro_pm_ops = {
};
static const struct of_device_id va_macro_dt_match[] = {
- { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data },
+ { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sc7280_va_data },
{ .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data },
{ .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data },
{ .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data },
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index 7ad7a9fb7255..8621d9bdb9fd 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -213,7 +213,7 @@ static int max9768_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id max9768_i2c_id[] = {
- { "max9768" },
+ { .name = "max9768" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9768_i2c_id);
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 9f40ca4b60d5..df438baf05dc 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1722,8 +1722,8 @@ static const struct snd_soc_component_driver soc_component_dev_max98088 = {
};
static const struct i2c_device_id max98088_i2c_id[] = {
- { "max98088", MAX98088 },
- { "max98089", MAX98089 },
+ { .name = "max98088", .driver_data = MAX98088 },
+ { .name = "max98089", .driver_data = MAX98089 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c
index 13a15459040f..da416329b038 100644
--- a/sound/soc/codecs/max98090.c
+++ b/sound/soc/codecs/max98090.c
@@ -2337,7 +2337,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
}
/**
- * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ
+ * max98090_set_jack - Enable microphone detection via the MAX98090 IRQ
*
* @component: MAX98090 component
* @jack: jack to report detection events on
@@ -2349,12 +2349,12 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
*
* If no jack is supplied detection will be disabled.
*/
-int max98090_mic_detect(struct snd_soc_component *component,
- struct snd_soc_jack *jack)
+static int max98090_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *data)
{
struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component);
- dev_dbg(component->dev, "max98090_mic_detect\n");
+ dev_dbg(component->dev, "%s\n", __func__);
max98090->jack = jack;
if (jack) {
@@ -2377,7 +2377,6 @@ int max98090_mic_detect(struct snd_soc_component *component,
return 0;
}
-EXPORT_SYMBOL_GPL(max98090_mic_detect);
#define MAX98090_RATES SNDRV_PCM_RATE_8000_96000
#define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
@@ -2554,6 +2553,7 @@ static const struct snd_soc_component_driver soc_component_dev_max98090 = {
.remove = max98090_remove,
.seq_notifier = max98090_seq_notifier,
.set_bias_level = max98090_set_bias_level,
+ .set_jack = max98090_set_jack,
.idle_bias_on = 1,
.use_pmdown_time = 1,
.endianness = 1,
@@ -2572,8 +2572,8 @@ static const struct regmap_config max98090_regmap = {
};
static const struct i2c_device_id max98090_i2c_id[] = {
- { "max98090", MAX98090 },
- { "max98091", MAX98091 },
+ { .name = "max98090", .driver_data = MAX98090 },
+ { .name = "max98091", .driver_data = MAX98091 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h
index 6ce8dd176e48..048af4a1376f 100644
--- a/sound/soc/codecs/max98090.h
+++ b/sound/soc/codecs/max98090.h
@@ -1543,7 +1543,4 @@ struct max98090_priv {
bool shdn_pending;
};
-int max98090_mic_detect(struct snd_soc_component *component,
- struct snd_soc_jack *jack);
-
#endif
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index aae6423156e1..ced9bd4d94da 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -2109,7 +2109,7 @@ static const struct snd_soc_component_driver soc_component_dev_max98095 = {
};
static const struct i2c_device_id max98095_i2c_id[] = {
- { "max98095", MAX98095 },
+ { .name = "max98095", .driver_data = MAX98095 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c
index cc811f58c9d2..b0f8043fb9e2 100644
--- a/sound/soc/codecs/max98357a.c
+++ b/sound/soc/codecs/max98357a.c
@@ -10,7 +10,6 @@
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
index 25af78ab30d5..099dc5bf6195 100644
--- a/sound/soc/codecs/max98363.c
+++ b/sound/soc/codecs/max98363.c
@@ -90,24 +90,15 @@ static int max98363_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct max98363_priv *max98363 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!max98363->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(MAX98363_PROBE_TIMEOUT));
- if (!time) {
- dev_err(dev, "Initialization not complete, timed out\n");
- return -ETIMEDOUT;
- }
-
-regmap_sync:
+ ret = sdw_slave_wait_for_init(slave, MAX98363_PROBE_TIMEOUT);
+ if (ret)
+ return ret;
- slave->unattach_request = 0;
regcache_cache_only(max98363->regmap, false);
regcache_sync(max98363->regmap);
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
index 852db211ba1e..c8d4c68af562 100644
--- a/sound/soc/codecs/max98371.c
+++ b/sound/soc/codecs/max98371.c
@@ -400,7 +400,7 @@ static int max98371_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max98371_i2c_id[] = {
- { "max98371" },
+ { .name = "max98371" },
{ }
};
diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c
index f58b8c8625a7..8805bd01153c 100644
--- a/sound/soc/codecs/max98373-i2c.c
+++ b/sound/soc/codecs/max98373-i2c.c
@@ -5,7 +5,6 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/regmap.h>
@@ -576,8 +575,8 @@ static int max98373_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max98373_i2c_id[] = {
- { "max98373"},
- { },
+ { .name = "max98373" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max98373_i2c_id);
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index 16673440218c..8fe9c58e1a62 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -4,7 +4,6 @@
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -266,25 +265,17 @@ static int max98373_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct max98373_priv *max98373 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!max98373->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(MAX98373_PROBE_TIMEOUT));
- if (!time) {
- dev_err(dev, "Initialization not complete, timed out\n");
+ ret = sdw_slave_wait_for_init(slave, MAX98373_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(max98373->regmap, false);
regcache_sync(max98373->regmap);
diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c
index 076f15a9867e..a4c57152d25f 100644
--- a/sound/soc/codecs/max98388.c
+++ b/sound/soc/codecs/max98388.c
@@ -6,7 +6,6 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -977,8 +976,8 @@ static int max98388_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max98388_i2c_id[] = {
- { "max98388"},
- { },
+ { .name = "max98388" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max98388_i2c_id);
diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c
index 65f095c47191..2bbedf84ee5d 100644
--- a/sound/soc/codecs/max98390.c
+++ b/sound/soc/codecs/max98390.c
@@ -1096,8 +1096,8 @@ static int max98390_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max98390_i2c_id[] = {
- { "max98390"},
- {},
+ { .name = "max98390" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max98390_i2c_id);
diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c
index 18fd90227187..9c1d7213410c 100644
--- a/sound/soc/codecs/max98396.c
+++ b/sound/soc/codecs/max98396.c
@@ -1871,9 +1871,9 @@ static int max98396_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max98396_i2c_id[] = {
- { "max98396", CODEC_TYPE_MAX98396},
- { "max98397", CODEC_TYPE_MAX98397},
- { },
+ { .name = "max98396", .driver_data = CODEC_TYPE_MAX98396 },
+ { .name = "max98397", .driver_data = CODEC_TYPE_MAX98397 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max98396_i2c_id);
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index 1fcbc64a2771..9bd6a61212c1 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -321,7 +321,7 @@ static int max9850_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max9850_i2c_id[] = {
- { "max9850" },
+ { .name = "max9850" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9850_i2c_id);
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
index c94142768c81..8b2620eaf9b0 100644
--- a/sound/soc/codecs/max98504.c
+++ b/sound/soc/codecs/max98504.c
@@ -363,7 +363,7 @@ MODULE_DEVICE_TABLE(of, max98504_of_match);
#endif
static const struct i2c_device_id max98504_i2c_id[] = {
- { "max98504" },
+ { .name = "max98504" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98504_i2c_id);
diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c
index 5bc3d95ade5a..4fb98505db1a 100644
--- a/sound/soc/codecs/max98520.c
+++ b/sound/soc/codecs/max98520.c
@@ -734,8 +734,8 @@ static int max98520_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max98520_i2c_id[] = {
- { "max98520"},
- { },
+ { .name = "max98520" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max98520_i2c_id);
diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c
index 716d16daf7d7..0d7ac37850bb 100644
--- a/sound/soc/codecs/max9860.c
+++ b/sound/soc/codecs/max9860.c
@@ -709,7 +709,7 @@ static void max9860_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id max9860_i2c_id[] = {
- { "max9860", },
+ { .name = "max9860" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9860_i2c_id);
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 9cad9b698cf2..07a53ec2a18a 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -689,7 +689,7 @@ static int max9867_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max9867_i2c_id[] = {
- { "max9867" },
+ { .name = "max9867" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c
index 1bd0d4761ca6..7cd07b6f9dd6 100644
--- a/sound/soc/codecs/max9877.c
+++ b/sound/soc/codecs/max9877.c
@@ -151,7 +151,7 @@ static int max9877_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id max9877_i2c_id[] = {
- { "max9877" },
+ { .name = "max9877" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max9877_i2c_id);
diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c
index 124af6408d96..4302ab16a642 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -617,7 +617,7 @@ static int max98925_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id max98925_i2c_id[] = {
- { "max98925" },
+ { .name = "max98925" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98925_i2c_id);
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
index ae962bda163e..5305e1f9d97f 100644
--- a/sound/soc/codecs/max98926.c
+++ b/sound/soc/codecs/max98926.c
@@ -565,7 +565,7 @@ err_out:
}
static const struct i2c_device_id max98926_i2c_id[] = {
- { "max98926" },
+ { .name = "max98926" },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98926_i2c_id);
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index 0e9b8970997c..65e6fdb30eec 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -873,8 +873,8 @@ static void max98927_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id max98927_i2c_id[] = {
- { "max98927"},
- { },
+ { .name = "max98927" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max98927_i2c_id);
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index fad0cc902346..8a14626d43ff 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -573,7 +573,7 @@ static int ml26124_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id ml26124_i2c_id[] = {
- { "ml26124" },
+ { .name = "ml26124" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id);
diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c
index 2a5e963fb2b5..1768c249650d 100644
--- a/sound/soc/codecs/mt6351.c
+++ b/sound/soc/codecs/mt6351.c
@@ -8,7 +8,6 @@
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/delay.h>
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
index a787accb88e8..ed8cbc63fa77 100644
--- a/sound/soc/codecs/mt6358.c
+++ b/sound/soc/codecs/mt6358.c
@@ -6,7 +6,6 @@
// Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com>
#include <linux/platform_device.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kthread.h>
diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c
index ef63fd113cb7..21741afc80ef 100644
--- a/sound/soc/codecs/mt6660.c
+++ b/sound/soc/codecs/mt6660.c
@@ -557,8 +557,8 @@ static const struct of_device_id __maybe_unused mt6660_of_id[] = {
MODULE_DEVICE_TABLE(of, mt6660_of_id);
static const struct i2c_device_id mt6660_i2c_id[] = {
- {"mt6660" },
- {},
+ { .name = "mt6660" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id);
diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c
index 58ef5c493835..236d94c29c24 100644
--- a/sound/soc/codecs/nau8325.c
+++ b/sound/soc/codecs/nau8325.c
@@ -877,7 +877,7 @@ err:
}
static const struct i2c_device_id nau8325_i2c_ids[] = {
- { "nau8325" },
+ { .name = "nau8325" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8325_i2c_ids);
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
index caf2edb23088..fefbd5722c00 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -965,7 +965,7 @@ static int nau8540_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id nau8540_i2c_ids[] = {
- { "nau8540" },
+ { .name = "nau8540" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids);
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
index 9870e62d372e..a050387dd485 100644
--- a/sound/soc/codecs/nau8810.c
+++ b/sound/soc/codecs/nau8810.c
@@ -896,9 +896,9 @@ static int nau8810_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id nau8810_i2c_id[] = {
- { "nau8810" },
- { "nau8812" },
- { "nau8814" },
+ { .name = "nau8810" },
+ { .name = "nau8812" },
+ { .name = "nau8814" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id);
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index ffb526de0021..c45c5b864ea4 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -1968,7 +1968,7 @@ static int nau8821_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id nau8821_i2c_ids[] = {
- { "nau8821" },
+ { .name = "nau8821" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8821_i2c_ids);
diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c
index a11759f85eac..830164e991a7 100644
--- a/sound/soc/codecs/nau8822.c
+++ b/sound/soc/codecs/nau8822.c
@@ -19,6 +19,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -108,6 +109,10 @@ static const struct reg_default nau8822_reg_defaults[] = {
{ NAU8822_REG_OUTPUT_TIEOFF, 0x0000 },
};
+static const char * const nau8822_supply_names[NAU8822_NUM_SUPPLIES] = {
+ "vdda", "vddb", "vddc", "vddspk",
+};
+
static bool nau8822_readable_reg(struct device *dev, unsigned int reg)
{
switch (reg) {
@@ -1056,6 +1061,7 @@ static int nau8822_suspend(struct snd_soc_component *component)
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF);
+ regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
regcache_mark_dirty(nau8822->regmap);
@@ -1066,6 +1072,15 @@ 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);
+
+ if (ret) {
+ dev_err(component->dev,
+ "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ fsleep(100);
regcache_sync(nau8822->regmap);
@@ -1153,7 +1168,7 @@ static int nau8822_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
struct nau8822 *nau8822 = dev_get_platdata(dev);
- int ret;
+ int ret, i;
if (!nau8822) {
nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL);
@@ -1167,6 +1182,13 @@ static int nau8822_i2c_probe(struct i2c_client *i2c)
return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk),
"Error getting mclk\n");
+ for (i = 0; i < NAU8822_NUM_SUPPLIES; i++)
+ nau8822->supplies[i].supply = nau8822_supply_names[i];
+
+ ret = devm_regulator_bulk_get(dev, NAU8822_NUM_SUPPLIES, nau8822->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulators\n");
+
nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config);
if (IS_ERR(nau8822->regmap)) {
ret = PTR_ERR(nau8822->regmap);
@@ -1175,25 +1197,42 @@ static int nau8822_i2c_probe(struct i2c_client *i2c)
}
nau8822->dev = dev;
+ ret = regulator_bulk_enable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable regulators\n");
+
+ fsleep(100);
+
/* Reset the codec */
ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret);
- return ret;
+ goto err_reg;
}
ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822,
&nau8822_dai, 1);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret);
- return ret;
+ goto err_reg;
}
return 0;
+
+err_reg:
+ regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
+ return ret;
+}
+
+static void nau8822_i2c_remove(struct i2c_client *i2c)
+{
+ struct nau8822 *nau8822 = i2c_get_clientdata(i2c);
+
+ regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies);
}
static const struct i2c_device_id nau8822_i2c_id[] = {
- { "nau8822" },
+ { .name = "nau8822" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8822_i2c_id);
@@ -1212,6 +1251,7 @@ static struct i2c_driver nau8822_i2c_driver = {
.of_match_table = of_match_ptr(nau8822_of_match),
},
.probe = nau8822_i2c_probe,
+ .remove = nau8822_i2c_remove,
.id_table = nau8822_i2c_id,
};
module_i2c_driver(nau8822_i2c_driver);
diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h
index 13fe0a091e9e..24799c7b5931 100644
--- a/sound/soc/codecs/nau8822.h
+++ b/sound/soc/codecs/nau8822.h
@@ -211,6 +211,8 @@ struct nau8822_pll {
int freq_out;
};
+#define NAU8822_NUM_SUPPLIES 4
+
/* Codec Private Data */
struct nau8822 {
struct device *dev;
@@ -219,6 +221,7 @@ struct nau8822 {
struct nau8822_pll pll;
int sysclk;
int div_id;
+ struct regulator_bulk_data supplies[NAU8822_NUM_SUPPLIES];
};
#endif /* __NAU8822_H__ */
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
index 6ce763762443..426a488ff2a3 100644
--- a/sound/soc/codecs/nau8824.c
+++ b/sound/soc/codecs/nau8824.c
@@ -2020,7 +2020,7 @@ static int nau8824_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id nau8824_i2c_ids[] = {
- { "nau8824" },
+ { .name = "nau8824" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids);
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index dd3528537ae4..ae83b778bf93 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -712,8 +712,8 @@ static void nau8825_xtalk_measure(struct nau8825 *nau8825)
/* In left headphone IMM state, read out left headphone
* impedance measure result, and delay some time to wait
* detection sine wave output finish. Then, we can calculate
- * the cross talk suppresstion side tone according to the L/R
- * headphone imedance.
+ * the cross talk suppression side tone according to the L/R
+ * headphone impedance.
*/
regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L,
&nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]);
@@ -2237,7 +2237,7 @@ static void nau8825_component_remove(struct snd_soc_component *component)
{
struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component);
- /* Cancel and reset cross tak suppresstion detection funciton */
+ /* Cancel and reset cross talk suppression detection function */
nau8825_xtalk_cancel(nau8825);
}
@@ -2651,7 +2651,7 @@ static int nau8825_set_bias_level(struct snd_soc_component *component,
/* ground HPL/HPR, MICGRND1/2 */
regmap_update_bits(nau8825->regmap,
NAU8825_REG_HSD_CTRL, 0xf, 0xf);
- /* Cancel and reset cross talk detection funciton */
+ /* Cancel and reset cross talk detection function */
nau8825_xtalk_cancel(nau8825);
/* Turn off all interruptions before system shutdown. Keep the
* interruption quiet before resume setup completes.
@@ -2930,11 +2930,8 @@ static int nau8825_i2c_probe(struct i2c_client *i2c)
&nau8825_dai, 1);
}
-static void nau8825_i2c_remove(struct i2c_client *client)
-{}
-
static const struct i2c_device_id nau8825_i2c_ids[] = {
- { "nau8825" },
+ { .name = "nau8825" },
{ }
};
MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids);
@@ -2962,7 +2959,6 @@ static struct i2c_driver nau8825_driver = {
.acpi_match_table = ACPI_PTR(nau8825_acpi_match),
},
.probe = nau8825_i2c_probe,
- .remove = nau8825_i2c_remove,
.id_table = nau8825_i2c_ids,
};
module_i2c_driver(nau8825_driver);
diff --git a/sound/soc/codecs/ntp8835.c b/sound/soc/codecs/ntp8835.c
index 2b93bea11752..5837b9379fee 100644
--- a/sound/soc/codecs/ntp8835.c
+++ b/sound/soc/codecs/ntp8835.c
@@ -454,8 +454,8 @@ static int ntp8835_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id ntp8835_i2c_id[] = {
- { "ntp8835" },
- {}
+ { .name = "ntp8835" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id);
diff --git a/sound/soc/codecs/ntp8918.c b/sound/soc/codecs/ntp8918.c
index 5593d48ef696..a18d79aa80aa 100644
--- a/sound/soc/codecs/ntp8918.c
+++ b/sound/soc/codecs/ntp8918.c
@@ -370,8 +370,8 @@ static int ntp8918_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id ntp8918_i2c_id[] = {
- { "ntp8918" },
- {}
+ { .name = "ntp8918" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id);
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index f4e5f3133f2b..cb923cecb47f 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -290,8 +290,8 @@ static const struct snd_soc_component_driver soc_component_dev_pcm1681 = {
};
static const struct i2c_device_id pcm1681_i2c_id[] = {
- {"pcm1681"},
- {}
+ { .name = "pcm1681" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c
index abadf4f8ed5e..4f1bb13fd4c3 100644
--- a/sound/soc/codecs/pcm1789-i2c.c
+++ b/sound/soc/codecs/pcm1789-i2c.c
@@ -41,7 +41,7 @@ MODULE_DEVICE_TABLE(of, pcm1789_of_match);
#endif
static const struct i2c_device_id pcm1789_i2c_ids[] = {
- { "pcm1789" },
+ { .name = "pcm1789" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm1789_i2c_ids);
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
index effc1dd6df22..5337bcc7c62c 100644
--- a/sound/soc/codecs/pcm179x-i2c.c
+++ b/sound/soc/codecs/pcm179x-i2c.c
@@ -38,7 +38,7 @@ MODULE_DEVICE_TABLE(of, pcm179x_of_match);
#endif
static const struct i2c_device_id pcm179x_i2c_ids[] = {
- { "pcm179x" },
+ { .name = "pcm179x" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids);
diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c
index a50f9f6e39c1..3bd9d557e3c2 100644
--- a/sound/soc/codecs/pcm186x-i2c.c
+++ b/sound/soc/codecs/pcm186x-i2c.c
@@ -23,10 +23,10 @@ static const struct of_device_id pcm186x_of_match[] = {
MODULE_DEVICE_TABLE(of, pcm186x_of_match);
static const struct i2c_device_id pcm186x_i2c_id[] = {
- { "pcm1862", PCM1862 },
- { "pcm1863", PCM1863 },
- { "pcm1864", PCM1864 },
- { "pcm1865", PCM1865 },
+ { .name = "pcm1862", .driver_data = PCM1862 },
+ { .name = "pcm1863", .driver_data = PCM1863 },
+ { .name = "pcm1864", .driver_data = PCM1864 },
+ { .name = "pcm1865", .driver_data = PCM1865 },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id);
diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c
index ff18c74b616c..dd24027836b2 100644
--- a/sound/soc/codecs/pcm3168a-i2c.c
+++ b/sound/soc/codecs/pcm3168a-i2c.c
@@ -10,7 +10,6 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <sound/soc.h>
@@ -33,7 +32,7 @@ static void pcm3168a_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id pcm3168a_i2c_id[] = {
- { "pcm3168a", },
+ { .name = "pcm3168a" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id);
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index c8617a488b11..cb6a6f08f2f8 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -564,12 +564,6 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
static const u64 pcm3168a_dai_formats[] = {
/*
- * Select below from Sound Card, not here
- * SND_SOC_DAIFMT_CBC_CFC
- * SND_SOC_DAIFMT_CBP_CFP
- */
-
- /*
* First Priority
*/
SND_SOC_POSSIBLE_DAIFMT_I2S |
@@ -581,6 +575,8 @@ static const u64 pcm3168a_dai_formats[] = {
* see
* pcm3168a_hw_params()
*/
+ SND_SOC_POSSIBLE_DAIFMT_I2S |
+ SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_RIGHT_J |
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
SND_SOC_POSSIBLE_DAIFMT_DSP_B,
@@ -799,7 +795,6 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap)
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
- pm_runtime_idle(dev);
memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv));
ret = devm_snd_soc_register_component(dev, &pcm3168a_driver,
@@ -822,15 +817,6 @@ err_clk:
}
EXPORT_SYMBOL_GPL(pcm3168a_probe);
-static void pcm3168a_disable(struct device *dev)
-{
- struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
-
- regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies),
- pcm3168a->supplies);
- clk_disable_unprepare(pcm3168a->scki);
-}
-
void pcm3168a_remove(struct device *dev)
{
struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev);
@@ -842,10 +828,12 @@ void pcm3168a_remove(struct device *dev)
* The asserted level of GPIO_ACTIVE_LOW is LOW.
*/
gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1);
+
pm_runtime_disable(dev);
-#ifndef CONFIG_PM
- pcm3168a_disable(dev);
-#endif
+ if (!pm_runtime_status_suspended(dev)) {
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
+ clk_disable_unprepare(pcm3168a->scki);
+ }
}
EXPORT_SYMBOL_GPL(pcm3168a_remove);
@@ -900,13 +888,15 @@ static int pcm3168a_rt_suspend(struct device *dev)
regcache_cache_only(pcm3168a->regmap, true);
- pcm3168a_disable(dev);
+ regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies);
+ clk_disable_unprepare(pcm3168a->scki);
return 0;
}
EXPORT_GPL_DEV_PM_OPS(pcm3168a_pm_ops) = {
RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
};
MODULE_DESCRIPTION("PCM3168A codec driver");
diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c
index a1d849b0c50f..836865d11d22 100644
--- a/sound/soc/codecs/pcm512x-i2c.c
+++ b/sound/soc/codecs/pcm512x-i2c.c
@@ -35,13 +35,13 @@ static void pcm512x_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id pcm512x_i2c_id[] = {
- { "pcm5121", },
- { "pcm5122", },
- { "pcm5141", },
- { "pcm5142", },
- { "pcm5242", },
- { "tas5754", },
- { "tas5756", },
+ { .name = "pcm5121" },
+ { .name = "pcm5122" },
+ { .name = "pcm5141" },
+ { .name = "pcm5142" },
+ { .name = "pcm5242" },
+ { .name = "tas5754" },
+ { .name = "tas5756" },
{ }
};
MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id);
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index fdef98ce52f1..fe3b5011fa16 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -633,7 +633,7 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream,
struct regmap *regmap = pcm512x->regmap;
if (IS_ERR(pcm512x->sclk)) {
- dev_info(dev, "No SCLK, using BCLK: %ld\n",
+ dev_info_once(dev, "No SCLK, using BCLK: %ld\n",
PTR_ERR(pcm512x->sclk));
/* Disable reporting of missing SCLK as an error */
diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c
index 78b21fbfad50..4ac4448ac3c8 100644
--- a/sound/soc/codecs/pcm6240.c
+++ b/sound/soc/codecs/pcm6240.c
@@ -27,28 +27,28 @@
#include "pcm6240.h"
static const struct i2c_device_id pcmdevice_i2c_id[] = {
- { "adc3120", ADC3120 },
- { "adc5120", ADC5120 },
- { "adc6120", ADC6120 },
- { "dix4192", DIX4192 },
- { "pcm1690", PCM1690 },
- { "pcm3120", PCM3120 },
- { "pcm3140", PCM3140 },
- { "pcm5120", PCM5120 },
- { "pcm5140", PCM5140 },
- { "pcm6120", PCM6120 },
- { "pcm6140", PCM6140 },
- { "pcm6240", PCM6240 },
- { "pcm6260", PCM6260 },
- { "pcm9211", PCM9211 },
- { "pcmd3140", PCMD3140 },
- { "pcmd3180", PCMD3180 },
- { "pcmd512x", PCMD512X },
- { "taa5212", TAA5212 },
- { "taa5412", TAA5412 },
- { "tad5212", TAD5212 },
- { "tad5412", TAD5412 },
- {}
+ { .name = "adc3120", .driver_data = ADC3120 },
+ { .name = "adc5120", .driver_data = ADC5120 },
+ { .name = "adc6120", .driver_data = ADC6120 },
+ { .name = "dix4192", .driver_data = DIX4192 },
+ { .name = "pcm1690", .driver_data = PCM1690 },
+ { .name = "pcm3120", .driver_data = PCM3120 },
+ { .name = "pcm3140", .driver_data = PCM3140 },
+ { .name = "pcm5120", .driver_data = PCM5120 },
+ { .name = "pcm5140", .driver_data = PCM5140 },
+ { .name = "pcm6120", .driver_data = PCM6120 },
+ { .name = "pcm6140", .driver_data = PCM6140 },
+ { .name = "pcm6240", .driver_data = PCM6240 },
+ { .name = "pcm6260", .driver_data = PCM6260 },
+ { .name = "pcm9211", .driver_data = PCM9211 },
+ { .name = "pcmd3140", .driver_data = PCMD3140 },
+ { .name = "pcmd3180", .driver_data = PCMD3180 },
+ { .name = "pcmd512x", .driver_data = PCMD512X },
+ { .name = "taa5212", .driver_data = TAA5212 },
+ { .name = "taa5412", .driver_data = TAA5412 },
+ { .name = "tad5212", .driver_data = TAD5212 },
+ { .name = "tad5412", .driver_data = TAD5412 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id);
@@ -1230,15 +1230,11 @@ static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt,
int *status)
{
struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt;
- struct pcmdevice_config_info *cfg_info;
+ struct pcmdevice_config_info *cfg_info = NULL;
struct pcmdevice_block_data **bk_da;
+ char cfg_name[64] = {};
unsigned int config_offset = 0, i;
-
- cfg_info = kzalloc_obj(struct pcmdevice_config_info);
- if (!cfg_info) {
- *status = -ENOMEM;
- goto out;
- }
+ unsigned int nblocks;
if (pcm_dev->regbin.fw_hdr.binary_version_num >= 0x105) {
if (config_offset + 64 > (int)config_size) {
@@ -1247,7 +1243,7 @@ static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt,
"%s: cfg_name out of boundary\n", __func__);
goto out;
}
- memcpy(cfg_info->cfg_name, &config_data[config_offset], 64);
+ memcpy(cfg_name, &config_data[config_offset], 64);
config_offset += 64;
}
@@ -1257,16 +1253,17 @@ static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt,
__func__);
goto out;
}
- cfg_info->nblocks =
- get_unaligned_be32(&config_data[config_offset]);
+ nblocks = get_unaligned_be32(&config_data[config_offset]);
config_offset += 4;
- bk_da = cfg_info->blk_data = kzalloc_objs(struct pcmdevice_block_data *,
- cfg_info->nblocks);
- if (!bk_da) {
+ cfg_info = kzalloc_flex(*cfg_info, blk_data, nblocks);
+ if (!cfg_info) {
*status = -ENOMEM;
goto out;
}
+ cfg_info->nblocks = nblocks;
+ memcpy(cfg_info->cfg_name, cfg_name, sizeof(cfg_info->cfg_name));
+ bk_da = cfg_info->blk_data;
cfg_info->real_nblocks = 0;
for (i = 0; i < cfg_info->nblocks; i++) {
if (config_offset + 12 > config_size) {
@@ -1449,14 +1446,11 @@ static void pcmdevice_config_info_remove(void *ctxt)
for (i = 0; i < regbin->ncfgs; i++) {
if (!cfg_info[i])
continue;
- if (cfg_info[i]->blk_data) {
- for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) {
- if (!cfg_info[i]->blk_data[j])
- continue;
- kfree(cfg_info[i]->blk_data[j]->regdata);
- kfree(cfg_info[i]->blk_data[j]);
- }
- kfree(cfg_info[i]->blk_data);
+ for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) {
+ if (!cfg_info[i]->blk_data[j])
+ continue;
+ kfree(cfg_info[i]->blk_data[j]->regdata);
+ kfree(cfg_info[i]->blk_data[j]);
}
kfree(cfg_info[i]);
}
diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h
index 2d8f9e798139..e27afd33910c 100644
--- a/sound/soc/codecs/pcm6240.h
+++ b/sound/soc/codecs/pcm6240.h
@@ -199,7 +199,7 @@ struct pcmdevice_config_info {
unsigned int nblocks;
unsigned int real_nblocks;
unsigned char active_dev;
- struct pcmdevice_block_data **blk_data;
+ struct pcmdevice_block_data *blk_data[] __counted_by(nblocks);
};
struct pcmdevice_regbin {
diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c
index 2d5163c15d0d..2d71d204d8fa 100644
--- a/sound/soc/codecs/peb2466.c
+++ b/sound/soc/codecs/peb2466.c
@@ -817,18 +817,17 @@ static int peb2466_dai_startup(struct snd_pcm_substream *substream,
&peb2466_sample_bits_constr);
}
-static const u64 peb2466_dai_formats[] = {
+static const u64 peb2466_dai_formats =
SND_SOC_POSSIBLE_DAIFMT_DSP_A |
- SND_SOC_POSSIBLE_DAIFMT_DSP_B,
-};
+ SND_SOC_POSSIBLE_DAIFMT_DSP_B;
static const struct snd_soc_dai_ops peb2466_dai_ops = {
.startup = peb2466_dai_startup,
.hw_params = peb2466_dai_hw_params,
.set_tdm_slot = peb2466_dai_set_tdm_slot,
.set_fmt = peb2466_dai_set_fmt,
- .auto_selectable_formats = peb2466_dai_formats,
- .num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats),
+ .auto_selectable_formats = &peb2466_dai_formats,
+ .num_auto_selectable_formats = 1,
};
static struct snd_soc_dai_driver peb2466_dai_driver = {
diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c
index 1f0a3f5389f1..29655175ea28 100644
--- a/sound/soc/codecs/pm4125.c
+++ b/sound/soc/codecs/pm4125.c
@@ -1309,17 +1309,12 @@ static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev)
static int pm4125_soc_codec_probe(struct snd_soc_component *component)
{
struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component);
- struct sdw_slave *tx_sdw_dev = pm4125->tx_sdw_dev;
struct device *dev = component->dev;
- unsigned long time_left;
int i, ret;
- time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
- msecs_to_jiffies(5000));
- if (!time_left) {
- dev_err(dev, "soundwire device init timeout\n");
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(pm4125->tx_sdw_dev, 5000);
+ if (ret)
+ return ret;
snd_soc_component_init_regmap(component, pm4125->regmap);
ret = pm_runtime_resume_and_get(dev);
diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c
index 9697aefc6e03..5871b5a81975 100644
--- a/sound/soc/codecs/rk3328_codec.c
+++ b/sound/soc/codecs/rk3328_codec.c
@@ -425,7 +425,6 @@ static int rk3328_platform_probe(struct platform_device *pdev)
struct rk3328_codec_priv *rk3328;
struct regmap *grf;
void __iomem *base;
- int ret = 0;
rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL);
if (!rk3328)
@@ -441,14 +440,13 @@ static int rk3328_platform_probe(struct platform_device *pdev)
regmap_write(grf, RK3328_GRF_SOC_CON2,
(BIT(14) << 16 | BIT(14)));
- ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms",
- &rk3328->spk_depop_time);
- if (ret < 0) {
+ if (of_property_read_u32(rk3328_np, "spk-depop-time-ms",
+ &rk3328->spk_depop_time)) {
dev_info(&pdev->dev, "spk_depop_time use default value.\n");
rk3328->spk_depop_time = 200;
}
- rk3328->mute = gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH);
+ rk3328->mute = devm_gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH);
if (IS_ERR(rk3328->mute))
return PTR_ERR(rk3328->mute);
/*
@@ -461,57 +459,31 @@ static int rk3328_platform_probe(struct platform_device *pdev)
regmap_write(grf, RK3328_GRF_SOC_CON10, BIT(17) | BIT(1));
}
- rk3328->mclk = devm_clk_get(&pdev->dev, "mclk");
+ rk3328->mclk = devm_clk_get_enabled(&pdev->dev, "mclk");
if (IS_ERR(rk3328->mclk))
return PTR_ERR(rk3328->mclk);
- ret = clk_prepare_enable(rk3328->mclk);
- if (ret)
- return ret;
clk_set_rate(rk3328->mclk, INITIAL_FREQ);
- rk3328->pclk = devm_clk_get(&pdev->dev, "pclk");
- if (IS_ERR(rk3328->pclk)) {
- dev_err(&pdev->dev, "can't get acodec pclk\n");
- ret = PTR_ERR(rk3328->pclk);
- goto err_unprepare_mclk;
- }
-
- ret = clk_prepare_enable(rk3328->pclk);
- if (ret < 0) {
- dev_err(&pdev->dev, "failed to enable acodec pclk\n");
- goto err_unprepare_mclk;
- }
+ rk3328->pclk = devm_clk_get_enabled(&pdev->dev, "pclk");
+ if (IS_ERR(rk3328->pclk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(rk3328->pclk),
+ "failed to get or enable acodec pclk\n");
base = devm_platform_ioremap_resource(pdev, 0);
- if (IS_ERR(base)) {
- ret = PTR_ERR(base);
- goto err_unprepare_pclk;
- }
+ if (IS_ERR(base))
+ return PTR_ERR(base);
rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base,
&rk3328_codec_regmap_config);
- if (IS_ERR(rk3328->regmap)) {
- ret = PTR_ERR(rk3328->regmap);
- goto err_unprepare_pclk;
- }
+ if (IS_ERR(rk3328->regmap))
+ return PTR_ERR(rk3328->regmap);
platform_set_drvdata(pdev, rk3328);
- ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
+ return devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328,
rk3328_dai,
ARRAY_SIZE(rk3328_dai));
- if (ret)
- goto err_unprepare_pclk;
-
- return 0;
-
-err_unprepare_pclk:
- clk_disable_unprepare(rk3328->pclk);
-
-err_unprepare_mclk:
- clk_disable_unprepare(rk3328->mclk);
- return ret;
}
static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = {
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
index 03f31d9d916e..15488a059283 100644
--- a/sound/soc/codecs/rt1011.c
+++ b/sound/soc/codecs/rt1011.c
@@ -2201,7 +2201,7 @@ MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match);
#endif
static const struct i2c_device_id rt1011_i2c_id[] = {
- { "rt1011" },
+ { .name = "rt1011" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt1011_i2c_id);
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
index ca1ed9d5a24e..ff0c08ff610e 100644
--- a/sound/soc/codecs/rt1015.c
+++ b/sound/soc/codecs/rt1015.c
@@ -1094,7 +1094,7 @@ static const struct regmap_config rt1015_regmap = {
};
static const struct i2c_device_id rt1015_i2c_id[] = {
- { "rt1015" },
+ { .name = "rt1015" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id);
diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c
index 9f86f071fca8..11c8c45574a1 100644
--- a/sound/soc/codecs/rt1016.c
+++ b/sound/soc/codecs/rt1016.c
@@ -608,7 +608,7 @@ static const struct regmap_config rt1016_regmap = {
};
static const struct i2c_device_id rt1016_i2c_id[] = {
- { "rt1016" },
+ { .name = "rt1016" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt1016_i2c_id);
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c
index 148b36173a25..95405cea8143 100644
--- a/sound/soc/codecs/rt1017-sdca-sdw.c
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -773,25 +772,17 @@ static int rt1017_sdca_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt1017->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT1017_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ ret = sdw_slave_wait_for_init(slave, RT1017_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt1017->regmap, false);
regcache_sync(rt1017->regmap);
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
index 86539c6f6cc1..370521149ac8 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -540,7 +540,7 @@ static const struct regmap_config rt1019_regmap = {
};
static const struct i2c_device_id rt1019_i2c_id[] = {
- { "rt1019" },
+ { .name = "rt1019" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id);
diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c
index 26b7382f97ef..2d5fad76cf52 100644
--- a/sound/soc/codecs/rt1305.c
+++ b/sound/soc/codecs/rt1305.c
@@ -981,8 +981,8 @@ MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match);
#endif
static const struct i2c_device_id rt1305_i2c_id[] = {
- { "rt1305" },
- { "rt1306" },
+ { .name = "rt1305" },
+ { .name = "rt1306" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt1305_i2c_id);
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index e077d096bc23..2f30497498c7 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
-#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
@@ -768,25 +767,17 @@ static int rt1308_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt1308->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT1308_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ ret = sdw_slave_wait_for_init(slave, RT1308_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt1308->regmap, false);
regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff);
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
index df50b38c24b9..630946cd7cdf 100644
--- a/sound/soc/codecs/rt1308.c
+++ b/sound/soc/codecs/rt1308.c
@@ -795,7 +795,7 @@ MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match);
#endif
static const struct i2c_device_id rt1308_i2c_id[] = {
- { "rt1308" },
+ { .name = "rt1308" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt1308_i2c_id);
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index 20fc1579eb9c..ca318dbd946e 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <sound/core.h>
@@ -745,25 +744,17 @@ static int rt1316_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt1316->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT1316_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ ret = sdw_slave_wait_for_init(slave, RT1316_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt1316->regmap, false);
regcache_sync(rt1316->regmap);
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
index d28f1afe68f1..c038ac0e3b76 100644
--- a/sound/soc/codecs/rt1318-sdw.c
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/dmi.h>
@@ -821,23 +820,15 @@ static int rt1318_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt1318->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT1318_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(slave, RT1318_PROBE_TIMEOUT);
+ if (ret)
+ return ret;
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt1318->regmap, false);
regcache_sync(rt1318->regmap);
diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c
index a80643099644..d13fd0e14125 100644
--- a/sound/soc/codecs/rt1318.c
+++ b/sound/soc/codecs/rt1318.c
@@ -1139,7 +1139,7 @@ static const struct regmap_config rt1318_regmap = {
};
static const struct i2c_device_id rt1318_i2c_id[] = {
- { "rt1318" },
+ { .name = "rt1318" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id);
diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c
index 192faa431b5e..1e930b27c67a 100644
--- a/sound/soc/codecs/rt1320-sdw.c
+++ b/sound/soc/codecs/rt1320-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/pm_runtime.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/dmi.h>
@@ -3053,23 +3052,15 @@ static int rt1320_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt1320->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT1320_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(slave, RT1320_PROBE_TIMEOUT);
+ if (ret)
+ return ret;
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt1320->regmap, false);
regcache_sync(rt1320->regmap);
regcache_cache_only(rt1320->mbq_regmap, false);
diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c
index bba714020c70..63b5fc439773 100644
--- a/sound/soc/codecs/rt274.c
+++ b/sound/soc/codecs/rt274.c
@@ -1098,8 +1098,8 @@ MODULE_DEVICE_TABLE(of, rt274_of_match);
#endif
static const struct i2c_device_id rt274_i2c_id[] = {
- {"rt274"},
- {}
+ { .name = "rt274" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt274_i2c_id);
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 195658f626cc..ded0ea332480 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1077,9 +1077,9 @@ static const struct regmap_config rt286_regmap = {
};
static const struct i2c_device_id rt286_i2c_id[] = {
- {"rt286"},
- {"rt288"},
- {}
+ { .name = "rt286" },
+ { .name = "rt288" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 7d532a5a5f73..5414a1712b57 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1138,8 +1138,8 @@ static const struct regmap_config rt298_regmap = {
};
static const struct i2c_device_id rt298_i2c_id[] = {
- {"rt298"},
- {}
+ { .name = "rt298" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt298_i2c_id);
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index 649b44b790b0..00a4a208d2fa 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -1200,7 +1200,7 @@ static const struct regmap_config rt5514_regmap = {
};
static const struct i2c_device_id rt5514_i2c_id[] = {
- { "rt5514" },
+ { .name = "rt5514" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id);
diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c
index 9a349965435b..d5b3a57c8866 100644
--- a/sound/soc/codecs/rt5575-spi.c
+++ b/sound/soc/codecs/rt5575-spi.c
@@ -17,7 +17,7 @@
struct rt5575_spi_burst_write {
u8 cmd;
- u32 addr;
+ __le32 addr;
u8 data[RT5575_SPI_BUF_LEN];
u8 dummy;
} __packed;
diff --git a/sound/soc/codecs/rt5575.c b/sound/soc/codecs/rt5575.c
index 24e41af29689..5c3e60eaa6a4 100644
--- a/sound/soc/codecs/rt5575.c
+++ b/sound/soc/codecs/rt5575.c
@@ -325,7 +325,7 @@ static int rt5575_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id rt5575_i2c_id[] = {
- { "rt5575" },
+ { .name = "rt5575" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5575_i2c_id);
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index fb9cf127e3ff..46ee412dc81d 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -1321,7 +1321,7 @@ static const struct regmap_config rt5616_regmap = {
};
static const struct i2c_device_id rt5616_i2c_id[] = {
- { "rt5616" },
+ { .name = "rt5616" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5616_i2c_id);
@@ -1386,9 +1386,6 @@ static int rt5616_i2c_probe(struct i2c_client *i2c)
rt5616_dai, ARRAY_SIZE(rt5616_dai));
}
-static void rt5616_i2c_remove(struct i2c_client *i2c)
-{}
-
static void rt5616_i2c_shutdown(struct i2c_client *client)
{
struct rt5616_priv *rt5616 = i2c_get_clientdata(client);
@@ -1403,7 +1400,6 @@ static struct i2c_driver rt5616_i2c_driver = {
.of_match_table = of_match_ptr(rt5616_of_match),
},
.probe = rt5616_i2c_probe,
- .remove = rt5616_i2c_remove,
.shutdown = rt5616_i2c_shutdown,
.id_table = rt5616_i2c_id,
};
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 19c6d8f760d9..ed1233973eb8 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1671,8 +1671,8 @@ static const struct snd_soc_component_driver soc_component_dev_rt5631 = {
};
static const struct i2c_device_id rt5631_i2c_id[] = {
- { "rt5631" },
- { "alc5631" },
+ { .name = "rt5631" },
+ { .name = "alc5631" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
@@ -1722,16 +1722,12 @@ static int rt5631_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void rt5631_i2c_remove(struct i2c_client *client)
-{}
-
static struct i2c_driver rt5631_i2c_driver = {
.driver = {
.name = "rt5631",
.of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
},
- .probe = rt5631_i2c_probe,
- .remove = rt5631_i2c_remove,
+ .probe = rt5631_i2c_probe,
.id_table = rt5631_i2c_id,
};
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index f6c6294e1588..74fd05176eff 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -2397,7 +2397,7 @@ static void rt5640_jack_work(struct work_struct *work)
* disabled the OVCD IRQ, the IRQ pin will stay high and as
* we react to edges, we miss the unplug event -> recheck.
*/
- queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0);
}
}
@@ -2410,7 +2410,8 @@ static irqreturn_t rt5640_irq(int irq, void *data)
delay = 100;
if (rt5640->jack)
- mod_delayed_work(system_long_wq, &rt5640->jack_work, delay);
+ mod_delayed_work(system_dfl_long_wq, &rt5640->jack_work,
+ delay);
return IRQ_HANDLED;
}
@@ -2419,7 +2420,7 @@ static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data)
{
struct rt5640_priv *rt5640 = data;
- queue_delayed_work(system_long_wq, &rt5640->jack_work,
+ queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work,
msecs_to_jiffies(JACK_SETTLE_TIME));
return IRQ_HANDLED;
@@ -2553,10 +2554,12 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
rt5640->jd_gpio = jack_data->jd_gpio;
rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio);
- ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "rt5640-jd-gpio", rt5640);
- if (ret) {
+ ret = request_any_context_irq(rt5640->jd_gpio_irq,
+ rt5640_jd_gpio_irq,
+ IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ "rt5640-jd-gpio", rt5640);
+ if (ret < 0) {
dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n",
rt5640->jd_gpio_irq, ret);
rt5640_disable_jack_detect(component);
@@ -2568,10 +2571,10 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
if (jack_data && jack_data->use_platform_clock)
rt5640->use_platform_clock = jack_data->use_platform_clock;
- ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
- "rt5640", rt5640);
- if (ret) {
+ ret = request_any_context_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "rt5640", rt5640);
+ if (ret < 0) {
dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
rt5640_disable_jack_detect(component);
return;
@@ -2579,7 +2582,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
rt5640->irq_requested = true;
/* sync initial jack state */
- queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0);
}
static const struct snd_soc_dapm_route rt5640_hda_jack_dapm_routes[] = {
@@ -2622,9 +2625,9 @@ static void rt5640_enable_hda_jack_detect(
rt5640->jack = jack;
- ret = request_irq(rt5640->irq, rt5640_irq,
- IRQF_TRIGGER_RISING, "rt5640", rt5640);
- if (ret) {
+ ret = request_any_context_irq(rt5640->irq, rt5640_irq,
+ IRQF_TRIGGER_RISING, "rt5640", rt5640);
+ if (ret < 0) {
dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
rt5640->jack = NULL;
return;
@@ -2632,7 +2635,7 @@ static void rt5640_enable_hda_jack_detect(
rt5640->irq_requested = true;
/* sync initial jack state */
- queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0);
snd_soc_dapm_add_routes(dapm, rt5640_hda_jack_dapm_routes,
ARRAY_SIZE(rt5640_hda_jack_dapm_routes));
@@ -2860,7 +2863,7 @@ static int rt5640_resume(struct snd_soc_component *component)
}
enable_irq(rt5640->irq);
- queue_delayed_work(system_long_wq, &rt5640->jack_work, 0);
+ queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0);
}
return 0;
@@ -2958,9 +2961,9 @@ static const struct regmap_config rt5640_regmap = {
};
static const struct i2c_device_id rt5640_i2c_id[] = {
- { "rt5640" },
- { "rt5639" },
- { "rt5642" },
+ { .name = "rt5640" },
+ { .name = "rt5639" },
+ { .name = "rt5642" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id);
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index f7701b8d0d3c..e9819653b30d 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -83,6 +83,8 @@ static const struct reg_sequence rt5650_init_list[] = {
{RT5645_PWR_ANLG1, 0x02},
{RT5645_IL_CMD3, 0x6728},
{RT5645_PR_BASE + 0x3a, 0x0000},
+ {RT5645_CLSD_OUT_CTRL1, 0x4059},
+ {RT5645_GEN_CTRL3, 0x0200},
};
static const struct reg_default rt5645_reg[] = {
@@ -1855,13 +1857,9 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
RT5645_PWR_CLS_D_L,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
RT5645_PWR_CLS_D_L);
- snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
- RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1);
break;
case SND_SOC_DAPM_PRE_PMD:
- snd_soc_component_update_bits(component, RT5645_GEN_CTRL3,
- RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS);
snd_soc_component_write(component, RT5645_EQ_CTRL2, 0);
snd_soc_component_update_bits(component, RT5645_PWR_DIG1,
RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
@@ -3646,8 +3644,8 @@ static const struct regmap_config temp_regmap = {
};
static const struct i2c_device_id rt5645_i2c_id[] = {
- { "rt5645" },
- { "rt5650" },
+ { .name = "rt5645" },
+ { .name = "rt5650" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index bef74b29fd54..a5bfe9861b8b 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -118,6 +118,7 @@
#define RT5645_A_JD_CTRL1 0x94
#define RT5645_VAD_CTRL4 0x9d
#define RT5645_CLSD_OUT_CTRL 0xa0
+#define RT5645_CLSD_OUT_CTRL1 0xa1
/* Function - Digital */
#define RT5645_ADC_EQ_CTRL1 0xae
#define RT5645_ADC_EQ_CTRL2 0xaf
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 23c4bf3da298..5c729135f71b 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -2202,7 +2202,7 @@ MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match);
#endif
static const struct i2c_device_id rt5651_i2c_id[] = {
- { "rt5651" },
+ { .name = "rt5651" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id);
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 3590ebd41c27..3097ee6d4e89 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -3814,8 +3814,8 @@ static const struct regmap_config rt5659_regmap = {
};
static const struct i2c_device_id rt5659_i2c_id[] = {
- { "rt5658" },
- { "rt5659" },
+ { .name = "rt5658" },
+ { .name = "rt5659" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5659_i2c_id);
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index 84cdfb810c66..edf2e3e346e4 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -1225,7 +1225,7 @@ static const struct regmap_config rt5660_regmap = {
};
static const struct i2c_device_id rt5660_i2c_id[] = {
- { "rt5660" },
+ { .name = "rt5660" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id);
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index eee1c98cc4aa..262d3bba1f3d 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -3307,8 +3307,8 @@ static const struct regmap_config temp_regmap = {
};
static const struct i2c_device_id rt5663_i2c_id[] = {
- { "rt5663" },
- {}
+ { .name = "rt5663" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id);
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 38fb3a277e26..48f57cd0920d 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -4534,8 +4534,8 @@ static const struct regmap_config rt5665_regmap = {
};
static const struct i2c_device_id rt5665_i2c_id[] = {
- {"rt5665"},
- {}
+ { .name = "rt5665" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt5665_i2c_id);
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index c551696ae11a..fed6de40b8c8 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -2334,8 +2334,8 @@ static const struct regmap_config rt5668_regmap = {
};
static const struct i2c_device_id rt5668_i2c_id[] = {
- {"rt5668b"},
- {}
+ { .name = "rt5668b" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt5668_i2c_id);
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index cb5d03bf4c7f..1d120b5dc2be 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2876,9 +2876,9 @@ static const struct regmap_config rt5670_regmap = {
};
static const struct i2c_device_id rt5670_i2c_id[] = {
- { "rt5670" },
- { "rt5671" },
- { "rt5672" },
+ { .name = "rt5670" },
+ { .name = "rt5671" },
+ { .name = "rt5672" },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index 60a93c3fe2e7..ac084ca008f3 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5210,7 +5210,7 @@ static const struct acpi_device_id rt5677_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match);
static const struct i2c_device_id rt5677_i2c_id[] = {
- { "rt5677", RT5677 },
+ { .name = "rt5677", .driver_data = RT5677 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index e556a365adc8..286cf0d17968 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -324,8 +324,8 @@ static const struct acpi_device_id rt5682_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match);
static const struct i2c_device_id rt5682_i2c_id[] = {
- {"rt5682"},
- {}
+ { .name = "rt5682" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id);
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index fc464538ceff..dec8c2147d68 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -754,7 +754,7 @@ static int rt5682_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt5682_priv *rt5682 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt5682->first_hw_init)
return 0;
@@ -766,20 +766,14 @@ static int rt5682_dev_resume(struct device *dev)
rt5682->disable_irq = false;
}
mutex_unlock(&rt5682->disable_irq_lock);
- goto regmap_sync;
}
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT5682_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt5682->sdw_regmap, false);
regcache_cache_only(rt5682->regmap, false);
regcache_sync(rt5682->regmap);
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index d39f8e4f3474..4b82e07d3b2c 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -2929,20 +2929,14 @@ static int rt5682_probe(struct snd_soc_component *component)
{
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
- struct sdw_slave *slave;
- unsigned long time;
+ int ret;
rt5682->component = component;
if (rt5682->is_sdw) {
- slave = rt5682->slave;
- time = wait_for_completion_timeout(
- &slave->initialization_complete,
- msecs_to_jiffies(RT5682_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "Initialization not complete, timed out\n");
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(rt5682->slave, RT5682_PROBE_TIMEOUT);
+ if (ret)
+ return ret;
}
snd_soc_dapm_disable_pin(dapm, "MICBIAS");
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index 98de94a79260..3624067950c0 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -3325,8 +3325,8 @@ static const struct acpi_device_id rt5682s_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5682s_acpi_match);
static const struct i2c_device_id rt5682s_i2c_id[] = {
- {"rt5682s"},
- {}
+ { .name = "rt5682s" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, rt5682s_i2c_id);
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
index 9ce36a66fae1..a451d5d1f8ab 100644
--- a/sound/soc/codecs/rt700-sdw.c
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
@@ -522,25 +521,17 @@ static int rt700_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt700_priv *rt700 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt700->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT700_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ ret = sdw_slave_wait_for_init(slave, RT700_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt700->regmap, false);
regcache_sync_region(rt700->regmap, 0x3000, 0x8fff);
regcache_sync_region(rt700->regmap, 0x752010, 0x75206b);
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
index 49dacceddf81..e028a1c3a9ac 100644
--- a/sound/soc/codecs/rt711-sdca-sdw.c
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
@@ -438,7 +437,7 @@ static int rt711_sdca_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt711->first_hw_init)
return 0;
@@ -451,20 +450,14 @@ static int rt711_sdca_dev_resume(struct device *dev)
rt711->disable_irq = false;
}
mutex_unlock(&rt711->disable_irq_lock);
- goto regmap_sync;
}
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT711_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt711->regmap, false);
regcache_sync(rt711->regmap);
regcache_cache_only(rt711->mbq_regmap, false);
diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c
index 72ddf4cebdf3..a0c6a9efa840 100644
--- a/sound/soc/codecs/rt711-sdw.c
+++ b/sound/soc/codecs/rt711-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
@@ -530,7 +529,7 @@ static int rt711_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt711_priv *rt711 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt711->first_hw_init)
return 0;
@@ -542,18 +541,12 @@ static int rt711_dev_resume(struct device *dev)
rt711->disable_irq = false;
}
mutex_unlock(&rt711->disable_irq_lock);
- goto regmap_sync;
}
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT711_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT);
+ if (ret)
+ return ret;
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt711->regmap, false);
regcache_sync_region(rt711->regmap, 0x3000, 0x8fff);
regcache_sync_region(rt711->regmap, 0x752009, 0x752091);
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index 4d83544ef204..85779547653e 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -7,7 +7,6 @@
//
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
#include <linux/slab.h>
@@ -905,26 +904,17 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt712->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT712_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n",
- __func__);
+ ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt712->regmap, false);
regcache_sync(rt712->regmap);
regcache_cache_only(rt712->mbq_regmap, false);
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
index 8c82887174db..70d661ce2ef2 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.c
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
@@ -450,7 +449,7 @@ static int rt712_sdca_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt712->first_hw_init)
return 0;
@@ -464,20 +463,14 @@ static int rt712_sdca_dev_resume(struct device *dev)
rt712->disable_irq = false;
}
mutex_unlock(&rt712->disable_irq_lock);
- goto regmap_sync;
}
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT712_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt712->regmap, false);
regcache_sync(rt712->regmap);
regcache_cache_only(rt712->mbq_regmap, false);
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
index 4796fce084ff..d6353af07380 100644
--- a/sound/soc/codecs/rt712-sdca.c
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -1843,6 +1843,15 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712)
}
}
+static void rt712_sdca_reset(struct rt712_sdca_priv *rt712)
+{
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG,
+ RT712_PARA_VERB_CTL, RT712_HIDDEN_REG_SW_RESET,
+ RT712_HIDDEN_REG_SW_RESET);
+ rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_RESET_CTL, 0x1, 0x1);
+}
+
int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
{
struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev);
@@ -1870,6 +1879,8 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
pm_runtime_get_noresume(&slave->dev);
+ rt712_sdca_reset(rt712);
+
rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val);
rt712->hw_id = (val & 0xf000) >> 12;
rt712->version_id = (val & 0x0f00) >> 8;
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
index 968bc183b8d8..1b183b21a4b8 100644
--- a/sound/soc/codecs/rt715-sdca-sdw.c
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
@@ -224,25 +223,17 @@ static int rt715_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt715->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT715_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt715->regmap, false);
regcache_sync_region(rt715->regmap,
SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL,
diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c
index 49c91d015be4..f5ec348a9628 100644
--- a/sound/soc/codecs/rt715-sdw.c
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -9,7 +9,6 @@
*/
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/mod_devicetable.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
#include <linux/soundwire/sdw_registers.h>
@@ -501,25 +500,17 @@ static int rt715_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt715_priv *rt715 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt715->first_hw_init)
return 0;
- if (!slave->unattach_request)
- goto regmap_sync;
-
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT715_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__);
+ ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt715->regmap, false);
regcache_sync_region(rt715->regmap, 0x3000, 0x8fff);
regcache_sync_region(rt715->regmap, 0x752039, 0x752039);
diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c
index 6eb8512975b8..041b381e582b 100644
--- a/sound/soc/codecs/rt721-sdca-sdw.c
+++ b/sound/soc/codecs/rt721-sdca-sdw.c
@@ -9,7 +9,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
@@ -489,7 +488,7 @@ static int rt721_sdca_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt721->first_hw_init)
return 0;
@@ -502,20 +501,14 @@ static int rt721_sdca_dev_resume(struct device *dev)
rt721->disable_irq = false;
}
mutex_unlock(&rt721->disable_irq_lock);
- goto regmap_sync;
}
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT721_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt721->regmap, false);
regcache_sync(rt721->regmap);
regcache_cache_only(rt721->mbq_regmap, false);
diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c
index 0a5b3ffa90da..e68aa0350a5b 100644
--- a/sound/soc/codecs/rt722-sdca-sdw.c
+++ b/sound/soc/codecs/rt722-sdca-sdw.c
@@ -9,7 +9,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
-#include <linux/mod_devicetable.h>
#include <linux/pm_runtime.h>
#include <linux/soundwire/sdw_registers.h>
@@ -19,7 +18,9 @@
static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
{
switch (reg) {
- case 0x2f01 ... 0x2f0a:
+ case 0x22f0 ... 0x22f1:
+ case 0x2f01 ... 0x2f0c:
+ case 0x2f21 ... 0x2f24:
case 0x2f35 ... 0x2f36:
case 0x2f50 ... 0x2f52:
case 0x2f54:
@@ -84,6 +85,21 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31,
RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0):
case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
+ case 0x44011000 ... 0x440115ff:
+ case 0x44012000:
+ case 0x44012021:
+ case 0x44012022:
+ case 0x44012025:
+ case 0x44021000 ... 0x440211ff:
+ case 0x44022000:
+ case 0x44022019:
+ case 0x4402201a:
+ case 0x4402201d:
+ case 0x44041000 ... 0x440415ff:
+ case 0x44042000:
+ case 0x44042019:
+ case 0x4404201a:
+ case 0x4404201d:
return 1;
case 0x2000000 ... 0x2000024:
case 0x2000029 ... 0x200004a:
@@ -95,8 +111,10 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg)
case 0x200007f:
case 0x2000082 ... 0x200008e:
case 0x2000090 ... 0x2000094:
+ case 0x20000b1:
+ case 0x20000b4:
case 0x3110000:
- case 0x5300000 ... 0x5300002:
+ case 0x5300000 ... 0x5300300:
case 0x5400002:
case 0x5600000 ... 0x5600007:
case 0x5700000 ... 0x5700004:
@@ -175,6 +193,7 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0):
case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2:
case 0x2000000:
+ case 0x2000007:
case 0x200000d:
case 0x2000019:
case 0x2000020:
@@ -186,6 +205,21 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg)
case 0x3110000:
case 0x5800003:
case 0x5810000:
+ case 0x44011000 ... 0x440115ff:
+ case 0x44012000:
+ case 0x44012021:
+ case 0x44012022:
+ case 0x44012025:
+ case 0x44021000 ... 0x440211ff:
+ case 0x44022000:
+ case 0x44022019:
+ case 0x4402201a:
+ case 0x4402201d:
+ case 0x44041000 ... 0x440415ff:
+ case 0x44042000:
+ case 0x44042019:
+ case 0x4404201a:
+ case 0x4404201d:
return true;
default:
return false;
@@ -501,7 +535,7 @@ static int rt722_sdca_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
if (!rt722->first_hw_init)
return 0;
@@ -514,20 +548,14 @@ static int rt722_sdca_dev_resume(struct device *dev)
rt722->disable_irq = false;
}
mutex_unlock(&rt722->disable_irq_lock);
- goto regmap_sync;
}
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(RT722_PROBE_TIMEOUT));
- if (!time) {
- dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
-
- return -ETIMEDOUT;
+ return ret;
}
-regmap_sync:
- slave->unattach_request = 0;
regcache_cache_only(rt722->regmap, false);
regcache_sync(rt722->regmap);
return 0;
diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c
index 79b8b7e70a33..1b6729f363fc 100644
--- a/sound/soc/codecs/rt722-sdca.c
+++ b/sound/soc/codecs/rt722-sdca.c
@@ -7,19 +7,21 @@
//
#include <linux/bitops.h>
-#include <sound/core.h>
#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/firmware.h>
#include <linux/init.h>
-#include <sound/initval.h>
-#include <sound/jack.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <sound/pcm.h>
#include <linux/pm_runtime.h>
-#include <sound/pcm_params.h>
-#include <linux/soundwire/sdw_registers.h>
#include <linux/slab.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
#include <sound/soc-dapm.h>
#include <sound/tlv.h>
@@ -344,6 +346,280 @@ static int rt722_sdca_set_jack_detect(struct snd_soc_component *component,
return 0;
}
+static int rt722_cae_load(struct rt722_sdca_priv *rt722)
+{
+ struct device *dev = &rt722->slave->dev;
+ static const char func_tag[] = "FUNC";
+ static const char xu_tag[] = "XU";
+ const char *dmi_vendor, *dmi_product, *dmi_sku;
+ char *cae_filename;
+ const struct firmware *cae_fw = NULL;
+ unsigned int cae_st_spk, cae_st_hp, cae_st_mic;
+ unsigned int func, value;
+ unsigned int combined_val;
+ unsigned int addr, size;
+ unsigned int fw_offset;
+ unsigned char mbq_high_val = 0;
+ unsigned char *param_data;
+ unsigned char *fw_data;
+ char tag[5];
+ char *space;
+ int v_len, p_len, s_len;
+ int ret = 0, i;
+ int retry = 50;
+
+ dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR);
+ dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME);
+ dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU);
+
+ if (!dmi_vendor || !dmi_product || !dmi_sku) {
+ dev_warn(dev, "%s: Incomplete DMI info\n", __func__);
+ return -EINVAL;
+ }
+ space = strchr(dmi_vendor, ' ');
+ v_len = space ? space - dmi_vendor : strlen(dmi_vendor);
+
+ space = strchr(dmi_product, ' ');
+ p_len = space ? space - dmi_product : strlen(dmi_product);
+
+ space = strchr(dmi_sku, ' ');
+ s_len = space ? space - dmi_sku : strlen(dmi_sku);
+
+ cae_filename = kasprintf(GFP_KERNEL,
+ "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat",
+ v_len, dmi_vendor,
+ p_len, dmi_product,
+ s_len, dmi_sku);
+ if (!cae_filename)
+ return -ENOMEM;
+ dev_dbg(dev, "%s: try to load CAE file %s\n", __func__, cae_filename);
+
+ regmap_write(rt722->regmap, RT722_SPK_CAE_PARAM1, 0x5f);
+ regmap_write(rt722->regmap, RT722_HP_CAE_PARAM39, 0x5f);
+ regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f);
+ usleep_range(50000, 60000);
+
+ request_firmware(&cae_fw, cae_filename, dev);
+ kfree(cae_filename);
+ if (!cae_fw) {
+ dev_err(dev, "%s: Failed to load CAE firmware\n", __func__);
+ return -ENOENT;
+ }
+
+ regmap_read(rt722->regmap, RT722_SPK_CAE_PARAM38, &cae_st_spk);
+ regmap_read(rt722->regmap, RT722_HP_CAE_PARAM68, &cae_st_hp);
+ regmap_read(rt722->regmap, RT722_MIC_CAE_PARAM99, &cae_st_mic);
+ cae_st_spk &= 0x80;
+ cae_st_hp &= 0x80;
+ cae_st_mic &= 0x80;
+
+ dev_dbg(dev, "%s(%d) spk_crc:%x, hp_crc:%x, mic_crc:%x\n",
+ __func__, __LINE__, cae_st_spk, cae_st_hp, cae_st_mic);
+
+ if (cae_st_spk)
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE,
+ RT722_EQ_CTRL_SPK, 0x0008, 0x0008);
+ else if (cae_st_hp)
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE,
+ RT722_EQ_CTRL_HP, 0x0008, 0x0008);
+ else if (cae_st_mic)
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE,
+ RT722_EQ_CTRL_DMIC, 0x0008, 0x0008);
+
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_MISC_CTRL1, 0x8000, 0x8000);
+
+ regmap_update_bits(rt722->regmap, RT722_SPK_CAE_PARAM34, 0x1, 0x0);
+ regmap_update_bits(rt722->regmap, RT722_HP_CAE_PARAM64, 0x1, 0x0);
+ regmap_update_bits(rt722->regmap, RT722_MIC_CAE_PARAM95, 0x1, 0x0);
+
+ while (--retry) {
+ regmap_read(rt722->regmap, RT722_SPK_CAE_PARAM35, &cae_st_spk);
+ regmap_read(rt722->regmap, RT722_HP_CAE_PARAM65, &cae_st_hp);
+ regmap_read(rt722->regmap, RT722_MIC_CAE_PARAM96, &cae_st_mic);
+ dev_dbg(dev, "%s(%d) cae_st_spk:%x, cae_st_hp:%x, cae_st_mic:%x\n",
+ __func__, __LINE__, cae_st_spk, cae_st_hp, cae_st_mic);
+ if ((cae_st_spk & 0x40) && (cae_st_hp & 0x40) && (cae_st_mic & 0x40))
+ break;
+ usleep_range(1000, 1100);
+ }
+
+ if (!retry && !((cae_st_spk & 0x40) && (cae_st_hp & 0x40)
+ && (cae_st_mic & 0x40))) {
+ dev_err(dev, "%s: CAE is not ready to be loaded.\n", __func__);
+ ret = -ETIMEDOUT;
+ goto out_release;
+ }
+
+ dev_dbg(dev, "%s, cae_fw size=0x%zx, start\n", __func__, cae_fw->size);
+
+ rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE,
+ RT722_EQ_CTRL_AMIC, 0x8000);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE,
+ RT722_EQ_CTRL_DMIC, 0x8004);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE,
+ RT722_EQ_CTRL_HP, 0x8074);
+ rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE,
+ RT722_EQ_CTRL_SPK, 0xa074);
+
+ regcache_cache_bypass(rt722->regmap, true);
+ for (fw_offset = 0; fw_offset < cae_fw->size;) {
+
+ if (fw_offset + 12 > cae_fw->size) {
+ dev_err(dev, "%s: Unexpected end of firmware\n", __func__);
+ ret = -EINVAL;
+ goto verify_abort;
+ }
+
+ fw_data = (unsigned char *)&cae_fw->data[fw_offset];
+ memcpy(tag, fw_data, 4);
+ tag[4] = '\0';
+
+ if (strcmp(tag, xu_tag) == 0) {
+ dev_dbg(dev, "%s: This is a XU tag", __func__);
+ memcpy(&addr, (fw_data + 4), 4);
+ memcpy(&size, (fw_data + 8), 4);
+
+ if (size == 0 || size > cae_fw->size - fw_offset - 12) {
+ dev_err(dev, "%s: Invalid payload size: %u\n", __func__, size);
+ ret = -EINVAL;
+ goto verify_abort;
+ }
+
+ param_data = (unsigned char *)(fw_data + 12);
+
+ dev_dbg(dev, "%s: addr=0x%x, size=0x%x\n", __func__, addr, size);
+
+ if ((addr <= 0x05302300 && addr >= 0x05300000) ||
+ (addr <= 0x020020b4 && addr >= 0x020000b1)) {
+ if (addr & BIT(13)) {
+ mbq_high_val = param_data[0];
+ dev_dbg(dev, "MBQ: High Byte 0x%02x\n", mbq_high_val);
+ fw_offset += (size + 12);
+
+ continue;
+ } else {
+ regcache_cache_bypass(rt722->regmap, false);
+ combined_val = (mbq_high_val << 8) | param_data[0];
+ if (addr == 0x20000b1 || addr == 0x20000b4)
+ combined_val |= (0x2 << 8);
+ ret = regmap_write(rt722->regmap, addr, combined_val);
+ if (ret) {
+ dev_err(dev,
+ "MBQ fail: addr=0x%x ret=%d\n", addr, ret);
+ regcache_cache_bypass(rt722->regmap, true);
+ goto verify_abort;
+ }
+
+ dev_dbg(dev, "MBQ-reg=0x%x, value=0x%x\n",
+ addr, combined_val);
+
+ fw_offset += (size + 12);
+ regcache_cache_bypass(rt722->regmap, true);
+ continue;
+ }
+ }
+
+ for (i = 0; i < size; i++) {
+ ret = regmap_write(rt722->regmap, addr + i, param_data[i]);
+ if (ret) {
+ dev_err(dev,
+ "CAE write fail: addr=0x%x ret=%d\n",
+ addr + i, ret);
+ goto verify_abort;
+ }
+ }
+ fw_offset += (size + 12);
+ } else if (strcmp(tag, func_tag) == 0) {
+ dev_dbg(dev, "%s: This is a FUNC tag", __func__);
+
+ memcpy(&func, (fw_data + 4), 4);
+ memcpy(&value, (fw_data + 8), 4);
+ dev_dbg(dev, "%s: func=0x%x, value=0x%x\n",
+ __func__, func, value);
+
+ if (func == 1)
+ msleep(value);
+
+ fw_offset += 12;
+ } else {
+ dev_err(dev, "%s: No XU/FUNC tag at fw_offset=0x%x\n",
+ __func__, fw_offset);
+ ret = -EINVAL;
+ goto verify_abort;
+ }
+ }
+
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_MISC_CTRL1, 0x8000, 0x0000);
+ regcache_cache_bypass(rt722->regmap, false);
+ rt722->cae_update_done = 1;
+ dev_dbg(dev, "%s: CAE FW update done.\n", __func__);
+ release_firmware(cae_fw);
+ return 0;
+
+verify_abort:
+ regcache_cache_bypass(rt722->regmap, false);
+ if (!ret)
+ ret = -EIO;
+out_release:
+ rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG,
+ RT722_MISC_CTRL1, 0x8000, 0x0000);
+ release_firmware(cae_fw);
+ dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret);
+ return ret;
+}
+
+static int rt722_cae_update_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ int ret, changed = 0;
+
+ if (!rt722->hw_init)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ if (ucontrol->value.integer.value[0]) {
+ if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) {
+ ret = rt722_cae_load(rt722);
+ if (ret) {
+ dev_err(component->dev, "CAE load failed: %d\n", ret);
+ goto out;
+ } else
+ changed = 1;
+ }
+ } else {
+ if (rt722->cae_update_done) {
+ rt722->cae_update_done = 0;
+ changed = 1;
+ }
+ }
+
+out:
+ pm_runtime_mark_last_busy(component->dev);
+ pm_runtime_put_autosuspend(component->dev);
+
+ return ret < 0 ? ret : changed;
+}
+
+static int rt722_cae_update_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = rt722->cae_update_done;
+
+ return 0;
+}
+
+
/* For SDCA control DAC/ADC Gain */
static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -517,6 +793,61 @@ static int rt722_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol,
return changed;
}
+static int rt722_sdca_set_fu06_playback_ctl(struct rt722_sdca_priv *rt722)
+{
+ int err;
+ unsigned int ch_l, ch_r;
+
+ ch_l = (rt722->fu06_dapm_mute || rt722->fu06_mixer_l_mute) ? 0x01 : 0x00;
+ ch_r = (rt722->fu06_dapm_mute || rt722->fu06_mixer_r_mute) ? 0x01 : 0x00;
+
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l);
+ if (err < 0)
+ return err;
+
+ err = regmap_write(rt722->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
+ RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int rt722_sdca_fu06_playback_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+
+ ucontrol->value.integer.value[0] = !rt722->fu06_mixer_l_mute;
+ ucontrol->value.integer.value[1] = !rt722->fu06_mixer_r_mute;
+ return 0;
+}
+
+static int rt722_sdca_fu06_playback_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
+ int err, changed = 0;
+
+ if (rt722->fu06_mixer_l_mute != !ucontrol->value.integer.value[0] ||
+ rt722->fu06_mixer_r_mute != !ucontrol->value.integer.value[1])
+ changed = 1;
+
+ rt722->fu06_mixer_l_mute = !ucontrol->value.integer.value[0];
+ rt722->fu06_mixer_r_mute = !ucontrol->value.integer.value[1];
+
+ err = rt722_sdca_set_fu06_playback_ctl(rt722);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722)
{
int err;
@@ -718,6 +1049,8 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = {
RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0,
rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv),
/* AMP playback settings */
+ SOC_DOUBLE_EXT("FU06 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0,
+ rt722_sdca_fu06_playback_get, rt722_sdca_fu06_playback_put),
SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume",
SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
RT722_SDCA_CTL_FU_VOLUME, CH_L),
@@ -738,6 +1071,9 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = {
RT722_SDCA_CTL_FU_CH_GAIN, CH_01),
rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put,
4, 3, boost_vol_tlv),
+ /* CAE firmware update */
+ SOC_SINGLE_EXT("CAE Update", SND_SOC_NOPM, 0, 1, 0,
+ rt722_cae_update_get, rt722_cae_update_put),
};
static const char * const adc22_mux_text[] = {
@@ -807,27 +1143,17 @@ static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w,
struct snd_soc_component *component =
snd_soc_dapm_to_component(w->dapm);
struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component);
- unsigned char unmute = 0x0, mute = 0x1;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- regmap_write(rt722->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
- RT722_SDCA_CTL_FU_MUTE, CH_L), unmute);
- regmap_write(rt722->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
- RT722_SDCA_CTL_FU_MUTE, CH_R), unmute);
+ rt722->fu06_dapm_mute = false;
break;
case SND_SOC_DAPM_PRE_PMD:
- regmap_write(rt722->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
- RT722_SDCA_CTL_FU_MUTE, CH_L), mute);
- regmap_write(rt722->regmap,
- SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06,
- RT722_SDCA_CTL_FU_MUTE, CH_R), mute);
+ rt722->fu06_dapm_mute = true;
break;
}
- return 0;
+
+ return rt722_sdca_set_fu06_playback_ctl(rt722);
}
static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w,
@@ -1324,9 +1650,12 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave
rt722->first_hw_init = false;
rt722->fu1e_dapm_mute = true;
rt722->fu0f_dapm_mute = true;
+ rt722->fu06_dapm_mute = true;
+ rt722->fu06_mixer_l_mute = rt722->fu06_mixer_r_mute = false;
rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true;
rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] =
rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true;
+ rt722->cae_update_done = 0;
return devm_snd_soc_register_component(dev,
&soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai));
diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h
index 823abee9ab76..b654b14334d4 100644
--- a/sound/soc/codecs/rt722-sdca.h
+++ b/sound/soc/codecs/rt722-sdca.h
@@ -36,10 +36,15 @@ struct rt722_sdca_priv {
bool fu0f_dapm_mute;
bool fu0f_mixer_l_mute;
bool fu0f_mixer_r_mute;
+ /* For AMP */
+ bool fu06_dapm_mute;
+ bool fu06_mixer_l_mute;
+ bool fu06_mixer_r_mute;
/* For DMIC */
bool fu1e_dapm_mute;
bool fu1e_mixer_mute[4];
int hw_vid;
+ int cae_update_done;
};
struct rt722_sdca_dmic_kctrl_priv {
@@ -51,6 +56,7 @@ struct rt722_sdca_dmic_kctrl_priv {
/* NID */
#define RT722_VENDOR_REG 0x20
+#define RT722_VENDOR_EQ_CAE 0x53
#define RT722_VENDOR_CALI 0x58
#define RT722_VENDOR_SPK_EFUSE 0x5c
#define RT722_VENDOR_IMS_DRE 0x5b
@@ -60,6 +66,7 @@ struct rt722_sdca_dmic_kctrl_priv {
/* Index (NID:20h) */
#define RT722_JD_PRODUCT_NUM 0x00
#define RT722_ANALOG_BIAS_CTL3 0x04
+#define RT722_MISC_CTRL1 0x07
#define RT722_JD_CTRL1 0x09
#define RT722_LDO2_3_CTL1 0x0e
#define RT722_LDO1_CTL 0x1a
@@ -75,6 +82,12 @@ struct rt722_sdca_dmic_kctrl_priv {
#define RT722_SW_CONFIG1 0x8a
#define RT722_SW_CONFIG2 0x8b
+/* Index (NID:53h) */
+#define RT722_EQ_CTRL_SPK 0x00
+#define RT722_EQ_CTRL_HP 0x100
+#define RT722_EQ_CTRL_DMIC 0x200
+#define RT722_EQ_CTRL_AMIC 0x300
+
/* Index (NID:58h) */
#define RT722_DAC_DC_CALI_CTL0 0x00
#define RT722_DAC_DC_CALI_CTL1 0x01
@@ -152,6 +165,20 @@ struct rt722_sdca_dmic_kctrl_priv {
#define RT722_BUF_ADDR_HID1 0x44030000
#define RT722_BUF_ADDR_HID2 0x44030020
+/* RT722 CAE parameter settings */
+#define RT722_SPK_CAE_PARAM1 0x44012000
+#define RT722_SPK_CAE_PARAM34 0x44012021
+#define RT722_SPK_CAE_PARAM35 0x44012022
+#define RT722_SPK_CAE_PARAM38 0x44012025
+#define RT722_HP_CAE_PARAM39 0x44022000
+#define RT722_HP_CAE_PARAM64 0x44022019
+#define RT722_HP_CAE_PARAM65 0x4402201a
+#define RT722_HP_CAE_PARAM68 0x4402201d
+#define RT722_MIC_CAE_PARAM39 0x44042000
+#define RT722_MIC_CAE_PARAM95 0x44042019
+#define RT722_MIC_CAE_PARAM96 0x4404201a
+#define RT722_MIC_CAE_PARAM99 0x4404201d
+
/* RT722 SDCA Control - function number */
#define FUNC_NUM_JACK_CODEC 0x01
#define FUNC_NUM_MIC_ARRAY 0x02
diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c
index 84fd3d6861de..07eaf275c9e9 100644
--- a/sound/soc/codecs/rt9123.c
+++ b/sound/soc/codecs/rt9123.c
@@ -13,7 +13,6 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
diff --git a/sound/soc/codecs/rt9123p.c b/sound/soc/codecs/rt9123p.c
index d509659e735b..584fcb78cd3f 100644
--- a/sound/soc/codecs/rt9123p.c
+++ b/sound/soc/codecs/rt9123p.c
@@ -9,7 +9,6 @@
#include <linux/err.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c
index 186904b31434..2a041894bc0c 100644
--- a/sound/soc/codecs/rtq9124.c
+++ b/sound/soc/codecs/rtq9124.c
@@ -12,7 +12,6 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/pm_runtime.h>
diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c
index 14a2c0723d33..573200e5062f 100644
--- a/sound/soc/codecs/rtq9128.c
+++ b/sound/soc/codecs/rtq9128.c
@@ -11,7 +11,6 @@
#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/kernel.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
index b7e6546f1b5a..93f3fd1882a5 100644
--- a/sound/soc/codecs/sdw-mockup.c
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -8,7 +8,6 @@
//
#include <linux/device.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/soundwire/sdw.h>
#include <linux/soundwire/sdw_type.h>
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 320312f8db92..59642673b4cb 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -1809,8 +1809,8 @@ static void sgtl5000_i2c_shutdown(struct i2c_client *client)
}
static const struct i2c_device_id sgtl5000_id[] = {
- {"sgtl5000"},
- {},
+ { .name = "sgtl5000" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sgtl5000_id);
diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c
index 3343dbb63d2b..2e08fde3989c 100644
--- a/sound/soc/codecs/sigmadsp.c
+++ b/sound/soc/codecs/sigmadsp.c
@@ -35,7 +35,7 @@ struct sigmadsp_control {
struct snd_kcontrol *kcontrol;
bool is_readback;
bool cached;
- uint8_t cache[];
+ u8 cache[] __counted_by(num_bytes);
};
struct sigmadsp_data {
@@ -223,7 +223,7 @@ static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
return -EINVAL;
num_bytes = le16_to_cpu(ctrl_chunk->num_bytes);
- ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL);
+ ctrl = kzalloc_flex(*ctrl, cache, num_bytes);
if (!ctrl)
return -ENOMEM;
diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c
index d306c585b52b..ca53b08c0b33 100644
--- a/sound/soc/codecs/simple-amplifier.c
+++ b/sound/soc/codecs/simple-amplifier.c
@@ -1,25 +1,100 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (c) 2017 BayLibre, SAS.
- * Author: Jerome Brunet <jbrunet@baylibre.com>
+ * Support for gpio amplifier
+ * Copyright 2026 CS GROUP France
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ *
+ * Basic simple amplifier driver
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
*/
+#include <linux/bitmap.h>
+#include <linux/bits.h>
#include <linux/gpio/consumer.h>
+#include <linux/math.h>
+#include <linux/minmax.h>
#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
#include <sound/soc.h>
+#include <linux/sort.h>
+#include <sound/tlv.h>
-#define DRV_NAME "simple-amplifier"
+struct simple_amp_single {
+ struct gpio_desc *gpio;
+ bool is_inverted;
+ int kctrl_val;
+ const char *control_name;
+};
+
+struct simple_amp_point {
+ u32 gpio_val;
+ int gain_db;
+};
+
+struct simple_amp_range {
+ unsigned int nb_points;
+ struct simple_amp_point min;
+ struct simple_amp_point max;
+};
+
+struct simple_amp_ranges {
+ unsigned int nb_ranges;
+ struct simple_amp_range *tab_ranges;
+};
+
+struct simple_amp_labels {
+ unsigned int nb_labels;
+ const char **tab_labels;
+};
+
+enum simple_amp_mode {
+ SIMPLE_AMP_MODE_NONE,
+ SIMPLE_AMP_MODE_RANGES,
+ SIMPLE_AMP_MODE_LABELS,
+};
+
+struct simple_amp_multi {
+ struct gpio_descs *gpios;
+ u32 kctrl_val;
+ u32 kctrl_max;
+ const char *control_name;
+ unsigned int *tlv_array;
+ enum simple_amp_mode mode;
+ union {
+ struct simple_amp_ranges ranges;
+ struct simple_amp_labels labels;
+ };
+};
+
+struct simple_amp_data {
+ unsigned int supports;
+#define SIMPLE_AUDIO_SUPPORT_PGA BIT(0)
+#define SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES BIT(1)
+#define SIMPLE_AUDIO_SUPPORT_MUTE BIT(2)
+#define SIMPLE_AUDIO_SUPPORT_BYPASS BIT(3)
+
+ const struct snd_soc_dapm_widget *dapm_widgets;
+ unsigned int num_dapm_widgets;
+ const struct snd_soc_dapm_route *dapm_routes;
+ unsigned int num_dapm_routes;
+};
struct simple_amp {
+ const struct simple_amp_data *data;
struct gpio_desc *gpiod_enable;
+ struct simple_amp_single mute;
+ struct simple_amp_single bypass;
+ struct simple_amp_multi gain;
};
-static int drv_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *control, int event)
+static int simple_amp_power_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
{
struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
- struct simple_amp *priv = snd_soc_component_get_drvdata(c);
+ struct simple_amp *simple_amp = snd_soc_component_get_drvdata(c);
int val;
switch (event) {
@@ -34,7 +109,7 @@ static int drv_event(struct snd_soc_dapm_widget *w,
return -EINVAL;
}
- gpiod_set_value_cansleep(priv->gpiod_enable, val);
+ gpiod_set_value_cansleep(simple_amp->gpiod_enable, val);
return 0;
}
@@ -42,7 +117,7 @@ static int drv_event(struct snd_soc_dapm_widget *w,
static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("INL"),
SND_SOC_DAPM_INPUT("INR"),
- SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event,
+ SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, simple_amp_power_event,
(SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
SND_SOC_DAPM_OUTPUT("OUTL"),
SND_SOC_DAPM_OUTPUT("OUTR"),
@@ -58,47 +133,836 @@ static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = {
{ "OUTR", NULL, "DRV" },
};
+static const struct snd_soc_dapm_widget simple_amp_mono_pga_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("IN"),
+ SND_SOC_DAPM_OUTPUT("OUT"),
+ SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, simple_amp_power_event,
+ (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd", 0, 0),
+};
+
+static const struct snd_soc_dapm_route simple_amp_mono_pga_dapm_routes[] = {
+ { "PGA", NULL, "IN" },
+ { "PGA", NULL, "vdd" },
+ { "OUT", NULL, "PGA" },
+};
+
+static const struct snd_soc_dapm_widget simple_amp_stereo_pga_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+ SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, simple_amp_power_event,
+ (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd", 0, 0),
+};
+
+static const struct snd_soc_dapm_route simple_amp_stereo_pga_dapm_routes[] = {
+ { "PGA", NULL, "INL" },
+ { "PGA", NULL, "INR" },
+ { "PGA", NULL, "vdd" },
+ { "OUTL", NULL, "PGA" },
+ { "OUTR", NULL, "PGA" },
+};
+
+static int simple_amp_single_kctrl_write_gpio(struct simple_amp_single *single,
+ int kctrl_val)
+{
+ int gpio_val;
+
+ gpio_val = single->is_inverted ? !kctrl_val : kctrl_val;
+
+ return gpiod_set_value_cansleep(single->gpio, gpio_val);
+}
+
+static int simple_amp_single_kctrl_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+ return 0;
+}
+
+static int simple_amp_single_kctrl_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct simple_amp_single *single = (struct simple_amp_single *)kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = single->kctrl_val;
+
+ return 0;
+}
+
+static int simple_amp_single_kctrl_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct simple_amp_single *single = (struct simple_amp_single *)kcontrol->private_value;
+ int kctrl_val;
+ int err;
+
+ kctrl_val = ucontrol->value.integer.value[0] ? 1 : 0;
+
+ if (kctrl_val == single->kctrl_val)
+ return 0;
+
+ err = simple_amp_single_kctrl_write_gpio(single, kctrl_val);
+ if (err)
+ return err;
+
+ single->kctrl_val = kctrl_val;
+
+ return 1; /* The value changed */
+}
+
+static int simple_amp_single_add_kcontrol(struct snd_soc_component *component,
+ struct simple_amp_single *single)
+{
+ struct snd_kcontrol_new control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = single->control_name,
+ .info = simple_amp_single_kctrl_info,
+ .get = simple_amp_single_kctrl_get,
+ .put = simple_amp_single_kctrl_put,
+ .private_value = (unsigned long)single,
+ };
+ int ret;
+
+ /* Be consistent between single->kctrl_val value and the GPIO value */
+ ret = simple_amp_single_kctrl_write_gpio(single, single->kctrl_val);
+ if (ret)
+ return ret;
+
+ return snd_soc_add_component_controls(component, &control, 1);
+}
+
+static u32 simple_amp_multi_ranges_kctrl_to_gpio(u32 kctrl_val,
+ struct simple_amp_ranges *ranges)
+{
+ struct simple_amp_range *range;
+ u32 index = kctrl_val;
+ unsigned int i;
+
+ for (i = 0; i < ranges->nb_ranges; i++) {
+ range = &ranges->tab_ranges[i];
+
+ if (index < range->nb_points)
+ return (range->max.gpio_val >= range->min.gpio_val) ?
+ range->min.gpio_val + index :
+ range->min.gpio_val - index;
+
+ index -= range->nb_points;
+ }
+
+ /*
+ * Given index out of possible ranges. This is shouldn't happen.
+ * Signal the issue and return the maximum value
+ */
+ WARN(1, "kctrl_val %u out of ranges\n", kctrl_val);
+ return ranges->tab_ranges[ranges->nb_ranges - 1].max.gpio_val;
+}
+
+static int simple_amp_multi_kctrl_write_gpios(struct simple_amp_multi *multi,
+ u32 kctrl_val)
+{
+ DECLARE_BITMAP(bm, 32);
+ u32 gpio_val;
+
+ if (kctrl_val > multi->kctrl_max)
+ return -EINVAL;
+
+ if (multi->mode == SIMPLE_AMP_MODE_RANGES)
+ gpio_val = simple_amp_multi_ranges_kctrl_to_gpio(kctrl_val,
+ &multi->ranges);
+ else
+ gpio_val = kctrl_val;
+
+ bitmap_from_arr32(bm, &gpio_val, multi->gpios->ndescs);
+
+ return gpiod_multi_set_value_cansleep(multi->gpios, bm);
+}
+
+static int simple_amp_multi_kctrl_int_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value;
+
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = multi->kctrl_max;
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ return 0;
+}
+
+static int simple_amp_multi_kctrl_int_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value;
+
+ ucontrol->value.integer.value[0] = multi->kctrl_val;
+ return 0;
+}
+
+static int simple_amp_multi_kctrl_int_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value;
+ u32 kctrl_val;
+ int ret;
+
+ kctrl_val = ucontrol->value.integer.value[0];
+
+ if (kctrl_val == multi->kctrl_val)
+ return 0;
+
+ ret = simple_amp_multi_kctrl_write_gpios(multi, kctrl_val);
+ if (ret)
+ return ret;
+
+ multi->kctrl_val = kctrl_val;
+
+ return 1; /* The value changed */
+}
+
+static int simple_amp_multi_kctrl_enum_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value;
+
+ return snd_ctl_enum_info(uinfo, 1, multi->labels.nb_labels,
+ multi->labels.tab_labels);
+}
+
+static int simple_amp_multi_kctrl_enum_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value;
+
+ ucontrol->value.enumerated.item[0] = multi->kctrl_val;
+ return 0;
+}
+
+static int simple_amp_multi_kctrl_enum_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value;
+ u32 kctrl_val;
+ int ret;
+
+ kctrl_val = ucontrol->value.enumerated.item[0];
+
+ if (kctrl_val == multi->kctrl_val)
+ return 0;
+
+ ret = simple_amp_multi_kctrl_write_gpios(multi, kctrl_val);
+ if (ret)
+ return ret;
+
+ multi->kctrl_val = kctrl_val;
+
+ return 1; /* The value changed */
+}
+
+static unsigned int *simple_amp_alloc_tlv_ranges(const struct simple_amp_ranges *ranges)
+{
+ unsigned int index;
+ unsigned int *tlv;
+ unsigned int *t;
+ unsigned int i;
+
+ tlv = kzalloc_objs(*tlv, 2 + ranges->nb_ranges * 6, GFP_KERNEL);
+ if (!tlv)
+ return NULL;
+
+ t = tlv;
+
+ /* Fill first TLV */
+ *t++ = SNDRV_CTL_TLVT_DB_RANGE; /* Tag */
+ *t++ = ranges->nb_ranges * 6 * sizeof(*tlv); /* Len */
+ /* Ranges are sorted from lower to higher value */
+ index = 0;
+ for (i = 0; i < ranges->nb_ranges; i++) {
+ /* Fill range item i */
+ *t++ = index; /* min */
+ index += ranges->tab_ranges[i].nb_points;
+ *t++ = index - 1; /* max */
+ *t++ = SNDRV_CTL_TLVT_DB_MINMAX; /* Tag */
+ *t++ = 2 * sizeof(*tlv); /* Len */
+ *t++ = ranges->tab_ranges[i].min.gain_db; /* min_dB */
+ *t++ = ranges->tab_ranges[i].max.gain_db; /* max_dB */
+ }
+
+ return tlv;
+}
+
+static int simple_amp_multi_add_kcontrol(struct snd_soc_component *component,
+ struct simple_amp_multi *multi)
+{
+ struct snd_kcontrol_new control = {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = multi->control_name,
+ .info = simple_amp_multi_kctrl_int_info,
+ .get = simple_amp_multi_kctrl_int_get,
+ .put = simple_amp_multi_kctrl_int_put,
+ .private_value = (unsigned long)multi,
+ };
+ int ret;
+
+ switch (multi->mode) {
+ case SIMPLE_AMP_MODE_RANGES:
+ multi->tlv_array = simple_amp_alloc_tlv_ranges(&multi->ranges);
+ if (!multi->tlv_array)
+ return -ENOMEM;
+
+ control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |
+ SNDRV_CTL_ELEM_ACCESS_READWRITE;
+ control.tlv.p = multi->tlv_array;
+ break;
+
+ case SIMPLE_AMP_MODE_LABELS:
+ /* Use enumerated values */
+ control.info = simple_amp_multi_kctrl_enum_info;
+ control.get = simple_amp_multi_kctrl_enum_get;
+ control.put = simple_amp_multi_kctrl_enum_put;
+ break;
+
+ case SIMPLE_AMP_MODE_NONE:
+ /* Already set control configuration is enough */
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ /* Be consistent between multi->kctrl_val value and the GPIOs value */
+ ret = simple_amp_multi_kctrl_write_gpios(multi, multi->kctrl_val);
+ if (ret)
+ goto err_free_tlv_array;
+
+ ret = snd_soc_add_component_controls(component, &control, 1);
+ if (ret)
+ goto err_free_tlv_array;
+
+ return 0;
+
+err_free_tlv_array:
+ kfree(multi->tlv_array);
+ return ret;
+}
+
+static int simple_amp_add_basic_dapm(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component);
+ struct device *dev = component->dev;
+ int ret;
+
+ /* Add basic dapm widgets and routes */
+ ret = snd_soc_dapm_new_controls(dapm, simple_amp->data->dapm_widgets,
+ simple_amp->data->num_dapm_widgets);
+ if (ret) {
+ dev_err(dev, "Failed to add basic dapm widgets (%d)\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, simple_amp->data->dapm_routes,
+ simple_amp->data->num_dapm_routes);
+ if (ret) {
+ dev_err(dev, "Failed to add basic dapm routes (%d)\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+struct simple_amp_supply {
+ const char *prop_name;
+ const struct snd_soc_dapm_widget dapm_widget;
+ const struct snd_soc_dapm_route dapm_route;
+};
+
+static const struct simple_amp_supply simple_amp_supplies[] = {
+ {
+ .prop_name = "vddio-supply",
+ .dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vddio", 0, 0),
+ .dapm_route = { "PGA", NULL, "vddio" },
+ }, {
+ .prop_name = "vdda1-supply",
+ .dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vdda1", 0, 0),
+ .dapm_route = { "PGA", NULL, "vdda1" },
+ }, {
+ .prop_name = "vdda2-supply",
+ .dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vdda2", 0, 0),
+ .dapm_route = { "PGA", NULL, "vdda2" },
+ },
+ { /* End of list */}
+};
+
+static int simple_amp_add_power_supplies(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
+ struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component);
+ const struct simple_amp_supply *supply;
+ struct device *dev = component->dev;
+ int ret;
+
+ /*
+ * Those additional power supplies are attached to the PGA.
+ * If PGA is not supported, simply skipped them.
+ */
+ if (!(simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_PGA)) {
+ dev_err(dev, "Extra power supplied need PGA\n");
+ return -EINVAL;
+ }
+
+ supply = simple_amp_supplies;
+ do {
+ if (!of_property_present(dev->of_node, supply->prop_name))
+ continue;
+
+ ret = snd_soc_dapm_new_controls(dapm, &supply->dapm_widget, 1);
+ if (ret) {
+ dev_err(dev, "Failed to add control for '%s' (%d)\n",
+ supply->prop_name, ret);
+ return ret;
+ }
+ ret = snd_soc_dapm_add_routes(dapm, &supply->dapm_route, 1);
+ if (ret) {
+ dev_err(dev, "Failed to add route for '%s' (%d)\n",
+ supply->prop_name, ret);
+ return ret;
+ }
+ } while ((++supply)->prop_name);
+
+ return 0;
+}
+
+static int simple_amp_component_probe(struct snd_soc_component *component)
+{
+ struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ /* Add basic dapm widgets and routes */
+ ret = simple_amp_add_basic_dapm(component);
+ if (ret)
+ return ret;
+
+ /* Add additional power supplies */
+ if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES) {
+ ret = simple_amp_add_power_supplies(component);
+ if (ret)
+ return ret;
+ }
+
+ if (simple_amp->mute.gpio) {
+ /*
+ * The name of the GPIO used is mute. According to this name, 1
+ * means muted and 0 means un-muted.
+ *
+ * An inversion is expected by ALSA. Indeed from ALSA point of
+ * view, 1 means 'on' (un-muted) and 0 means 'off' (muted).
+ */
+ simple_amp->mute.is_inverted = true;
+ simple_amp->mute.kctrl_val = 1; /* Un-muted */
+ ret = simple_amp_single_add_kcontrol(component, &simple_amp->mute);
+ if (ret)
+ return ret;
+ }
+
+ if (simple_amp->bypass.gpio) {
+ ret = simple_amp_single_add_kcontrol(component, &simple_amp->bypass);
+ if (ret)
+ return ret;
+ }
+
+ if (simple_amp->gain.gpios) {
+ ret = simple_amp_multi_add_kcontrol(component, &simple_amp->gain);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void simple_amp_component_remove(struct snd_soc_component *component)
+{
+ struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component);
+
+ kfree(simple_amp->gain.tlv_array);
+ simple_amp->gain.tlv_array = NULL;
+}
+
static const struct snd_soc_component_driver simple_amp_component_driver = {
- .dapm_widgets = simple_amp_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets),
- .dapm_routes = simple_amp_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes),
+ .probe = simple_amp_component_probe,
+ .remove = simple_amp_component_remove,
};
+static int simple_amp_parse_single_gpio(struct device *dev,
+ struct simple_amp_single *single,
+ const char *gpio_property)
+{
+ /* Start with the inactive value */
+ single->is_inverted = false;
+ single->kctrl_val = 0;
+ single->gpio = devm_gpiod_get_optional(dev, gpio_property, GPIOD_OUT_LOW);
+ if (IS_ERR(single->gpio))
+ return dev_err_probe(dev, PTR_ERR(single->gpio),
+ "Failed to get '%s' gpio\n",
+ gpio_property);
+ return 0;
+}
+
+static int simple_amp_cmp_ranges(const void *a, const void *b)
+{
+ const struct simple_amp_range *a_range = a;
+ const struct simple_amp_range *b_range = b;
+
+ /* Ranges a and b don't overlap. This has been already checked */
+
+ return a_range->min.gain_db - b_range->max.gain_db;
+}
+
+static int simple_amp_check_new_range(const struct simple_amp_range *new_range,
+ const struct simple_amp_range *tab_ranges,
+ unsigned int nb_ranges)
+{
+ unsigned int i;
+
+ for (i = 0; i < nb_ranges; i++) {
+ /* Check for range overlaps */
+ if (new_range->min.gain_db >= tab_ranges[i].min.gain_db &&
+ new_range->min.gain_db <= tab_ranges[i].max.gain_db)
+ return -EINVAL;
+
+ if (new_range->max.gain_db >= tab_ranges[i].min.gain_db &&
+ new_range->max.gain_db <= tab_ranges[i].max.gain_db)
+ return -EINVAL;
+
+ if (new_range->min.gain_db <= tab_ranges[i].min.gain_db &&
+ new_range->max.gain_db >= tab_ranges[i].max.gain_db)
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int simple_amp_parse_ranges(struct device *dev,
+ struct simple_amp_multi *multi,
+ const char *ranges_property)
+{
+ struct simple_amp_ranges *ranges = &multi->ranges;
+ struct simple_amp_range *range;
+ struct device_node *np = dev->of_node;
+ struct simple_amp_point first_point;
+ unsigned int max_gpio_val;
+ unsigned int i;
+ int ret;
+ u32 u;
+ s32 s;
+
+ max_gpio_val = (1 << multi->gpios->ndescs) - 1;
+
+ ret = of_property_count_u32_elems(np, ranges_property);
+ if (ret < 0)
+ return ret;
+
+ /* The ranges array cannot be empty */
+ if (ret == 0)
+ return -EINVAL;
+ /*
+ * One range item is composed of 2 points and each point is composed of
+ * 2 values.
+ */
+ if (ret % 4)
+ return -EINVAL;
+
+ ranges->nb_ranges = ret / 4;
+
+ /* The worst case is one range per possible gpio value */
+ if (ranges->nb_ranges > max_gpio_val + 1)
+ return -EINVAL;
+
+ ranges->tab_ranges = devm_kcalloc(dev, ranges->nb_ranges,
+ sizeof(*ranges->tab_ranges),
+ GFP_KERNEL);
+ if (!ranges->tab_ranges)
+ return -ENOMEM;
+
+ multi->kctrl_max = 0;
+ for (i = 0; i < ranges->nb_ranges; i++) {
+ range = &ranges->tab_ranges[i];
+
+ /* First gpios value */
+ ret = of_property_read_u32_index(np, ranges_property, i * 4, &u);
+ if (ret)
+ return ret;
+ if (u > max_gpio_val)
+ return -EINVAL;
+
+ range->min.gpio_val = u;
+
+ /* First Gain value */
+ ret = of_property_read_s32_index(np, ranges_property, i * 4 + 1, &s);
+ if (ret)
+ return ret;
+
+ range->min.gain_db = s;
+
+ /* Second gpios value */
+ ret = of_property_read_u32_index(np, ranges_property, i * 4 + 2, &u);
+ if (ret)
+ return ret;
+ if (u > max_gpio_val)
+ return -EINVAL;
+
+ range->max.gpio_val = u;
+
+ /* Second Gain value */
+ ret = of_property_read_s32_index(np, ranges_property, i * 4 + 3, &s);
+ if (ret)
+ return ret;
+
+ range->max.gain_db = s;
+
+ /* Save the first point for later usage */
+ if (i == 0)
+ first_point = range->min;
+
+ /* Fix min and max if needed */
+ if (range->min.gain_db > range->max.gain_db)
+ swap(range->min, range->max);
+
+ ret = simple_amp_check_new_range(range, ranges->tab_ranges, i);
+ if (ret)
+ return ret;
+
+ range->nb_points = abs_diff(range->min.gpio_val,
+ range->max.gpio_val) + 1;
+
+ multi->kctrl_max += range->nb_points;
+ }
+
+ multi->kctrl_max -= 1;
+
+ /* Sort the tab_range array by gain_db value */
+ sort(ranges->tab_ranges, ranges->nb_ranges, sizeof(*ranges->tab_ranges),
+ simple_amp_cmp_ranges, NULL);
+
+ /*
+ * multi->kctrl_val is the index in tab_ranges.
+ *
+ * Choose to have the initial amplification value set to the first point
+ * available in the first range available in the tab_ranges array before
+ * sorting.
+ *
+ * This first point has been identified before sorting. Search for it in
+ * the sorted array in order to set the multi->kctrl_val initial value.
+ */
+ multi->kctrl_val = 0;
+ for (i = 0; i < ranges->nb_ranges; i++) {
+ range = &ranges->tab_ranges[i];
+
+ if (range->min.gpio_val == first_point.gpio_val &&
+ range->min.gain_db == first_point.gain_db)
+ break;
+
+ multi->kctrl_val += range->nb_points;
+
+ if (range->max.gpio_val == first_point.gpio_val &&
+ range->max.gain_db == first_point.gain_db) {
+ multi->kctrl_val--;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int simple_amp_parse_labels(struct device *dev,
+ struct simple_amp_multi *multi,
+ const char *labels_property)
+{
+ struct simple_amp_labels *labels = &multi->labels;
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ ret = of_property_count_strings(np, labels_property);
+ if (ret < 0)
+ return ret;
+
+ /* The labels array cannot be empty */
+ if (ret == 0)
+ return -EINVAL;
+
+ labels->nb_labels = ret;
+ if (labels->nb_labels > (1 << multi->gpios->ndescs))
+ return -EINVAL;
+
+ labels->tab_labels = devm_kcalloc(dev, labels->nb_labels,
+ sizeof(*labels->tab_labels),
+ GFP_KERNEL);
+ if (!labels->tab_labels)
+ return -ENOMEM;
+
+ multi->kctrl_max = labels->nb_labels - 1;
+ multi->kctrl_val = 0;
+
+ return of_property_read_string_array(np, labels_property, labels->tab_labels,
+ labels->nb_labels);
+}
+
+static int simple_amp_parse_multi_gpio(struct device *dev,
+ struct simple_amp_multi *multi,
+ const char *gpios_property,
+ const char *ranges_property,
+ const char *labels_property)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ /* Start with the value 0 (GPIO inactive). Can be changed later */
+ multi->kctrl_val = 0;
+ multi->gpios = devm_gpiod_get_array_optional(dev, gpios_property, GPIOD_OUT_LOW);
+ if (IS_ERR(multi->gpios))
+ return dev_err_probe(dev, PTR_ERR(multi->gpios),
+ "Failed to get '%s' gpios\n",
+ gpios_property);
+ if (!multi->gpios)
+ return 0;
+
+ if (multi->gpios->ndescs > 16)
+ return dev_err_probe(dev, -EINVAL,
+ "Number of '%s' gpios limited to 16\n",
+ gpios_property);
+
+ /* Set default value for the kctrl_max. Can be changed later */
+ multi->kctrl_max = (1 << multi->gpios->ndescs) - 1;
+
+ multi->mode = SIMPLE_AMP_MODE_NONE;
+ if (of_property_present(np, ranges_property)) {
+ ret = simple_amp_parse_ranges(dev, multi, ranges_property);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to parse '%s'\n",
+ ranges_property);
+ multi->mode = SIMPLE_AMP_MODE_RANGES;
+ } else if (of_property_present(np, labels_property)) {
+ ret = simple_amp_parse_labels(dev, multi, labels_property);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Failed to parse '%s'\n",
+ labels_property);
+
+ multi->mode = SIMPLE_AMP_MODE_LABELS;
+ }
+
+ return 0;
+}
+
static int simple_amp_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct simple_amp *priv;
+ struct simple_amp *simple_amp;
+ int ret;
- priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (priv == NULL)
+ simple_amp = devm_kzalloc(dev, sizeof(*simple_amp), GFP_KERNEL);
+ if (!simple_amp)
return -ENOMEM;
- platform_set_drvdata(pdev, priv);
+ platform_set_drvdata(pdev, simple_amp);
- priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
- GPIOD_OUT_LOW);
- if (IS_ERR(priv->gpiod_enable))
- return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable),
+ simple_amp->data = of_device_get_match_data(dev);
+ if (!simple_amp->data)
+ return -EINVAL;
+
+ simple_amp->gpiod_enable = devm_gpiod_get_optional(dev, "enable",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(simple_amp->gpiod_enable))
+ return dev_err_probe(dev, PTR_ERR(simple_amp->gpiod_enable),
"Failed to get 'enable' gpio");
+ if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_MUTE) {
+ ret = simple_amp_parse_single_gpio(dev, &simple_amp->mute, "mute");
+ if (ret)
+ return ret;
+ }
+
+ if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_BYPASS) {
+ ret = simple_amp_parse_single_gpio(dev, &simple_amp->bypass, "bypass");
+ if (ret)
+ return ret;
+ }
+
+ if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_PGA) {
+ ret = simple_amp_parse_multi_gpio(dev, &simple_amp->gain, "gain",
+ "gain-ranges", "gain-labels");
+ if (ret)
+ return ret;
+ }
+
+ /* Set controls name */
+ simple_amp->gain.control_name = "Volume";
+ simple_amp->mute.control_name = "Switch";
+ simple_amp->bypass.control_name = "Bypass Switch";
+
+ if (simple_amp->gain.mode == SIMPLE_AMP_MODE_LABELS) {
+ /*
+ * The gain widget control will use enumerated values.
+ *
+ * Having just "Voltage" and "Switch" widget names with
+ * enumerated values and boolean value can confuse ALSA in terms
+ * of possible values (strings).
+ *
+ * Make things clear and avoid the just "Switch" name in that
+ * case.
+ */
+ simple_amp->mute.control_name = "Out Switch";
+ }
+
return devm_snd_soc_register_component(dev,
&simple_amp_component_driver,
NULL, 0);
}
-#ifdef CONFIG_OF
+static const struct simple_amp_data simple_audio_amplifier_data = {
+ .dapm_widgets = simple_amp_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets),
+ .dapm_routes = simple_amp_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes),
+};
+
+static const struct simple_amp_data simple_audio_mono_pga_data = {
+ .supports = SIMPLE_AUDIO_SUPPORT_PGA |
+ SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES |
+ SIMPLE_AUDIO_SUPPORT_MUTE |
+ SIMPLE_AUDIO_SUPPORT_BYPASS,
+ .dapm_widgets = simple_amp_mono_pga_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(simple_amp_mono_pga_dapm_widgets),
+ .dapm_routes = simple_amp_mono_pga_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(simple_amp_mono_pga_dapm_routes),
+};
+
+static const struct simple_amp_data simple_audio_stereo_pga_data = {
+ .supports = SIMPLE_AUDIO_SUPPORT_PGA |
+ SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES |
+ SIMPLE_AUDIO_SUPPORT_MUTE |
+ SIMPLE_AUDIO_SUPPORT_BYPASS,
+ .dapm_widgets = simple_amp_stereo_pga_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(simple_amp_stereo_pga_dapm_widgets),
+ .dapm_routes = simple_amp_stereo_pga_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(simple_amp_stereo_pga_dapm_routes),
+};
+
static const struct of_device_id simple_amp_ids[] = {
- { .compatible = "dioo,dio2125", },
- { .compatible = "simple-audio-amplifier", },
+ { .compatible = "dioo,dio2125", .data = &simple_audio_amplifier_data},
+ { .compatible = "simple-audio-amplifier", .data = &simple_audio_amplifier_data},
+ { .compatible = "gpio-audio-amp-mono", .data = &simple_audio_mono_pga_data},
+ { .compatible = "gpio-audio-amp-stereo", .data = &simple_audio_stereo_pga_data},
{ }
};
MODULE_DEVICE_TABLE(of, simple_amp_ids);
-#endif
static struct platform_driver simple_amp_driver = {
.driver = {
- .name = DRV_NAME,
- .of_match_table = of_match_ptr(simple_amp_ids),
+ .name = "simple-amplifier",
+ .of_match_table = simple_amp_ids,
},
.probe = simple_amp_probe,
};
@@ -107,4 +971,5 @@ module_platform_driver(simple_amp_driver);
MODULE_DESCRIPTION("ASoC Simple Audio Amplifier driver");
MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
index 06de2b4fce5e..7fce60de5e5f 100644
--- a/sound/soc/codecs/sma1303.c
+++ b/sound/soc/codecs/sma1303.c
@@ -7,7 +7,6 @@
// Auther: Gyuhwa Park <gyuhwa.park@irondevice.com>
// Kiseok Jo <kiseok.jo@irondevice.com>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -1782,8 +1781,8 @@ static void sma1303_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id sma1303_i2c_id[] = {
- {"sma1303"},
- {}
+ { .name = "sma1303" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id);
diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c
index 5850bf6e71ca..c52fe95b30c6 100644
--- a/sound/soc/codecs/sma1307.c
+++ b/sound/soc/codecs/sma1307.c
@@ -1592,6 +1592,7 @@ static void sma1307_check_fault_worker(struct work_struct *work)
struct sma1307_priv *sma1307 =
container_of(work, struct sma1307_priv, check_fault_work.work);
unsigned int status1_val, status2_val;
+ char volume[sizeof("VOLUME=0x12345678")];
char *envp[3] = { NULL, NULL, NULL };
if (sma1307->tsdw_cnt)
@@ -1607,7 +1608,7 @@ static void sma1307_check_fault_worker(struct work_struct *work)
if (~status1_val & SMA1307_OT1_OK_STATUS) {
dev_crit(sma1307->dev,
"%s: OT1(Over Temperature Level 1)\n", __func__);
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1");
+ envp[0] = "STATUS=OT1";
if (sma1307->sw_ot1_prot) {
/* Volume control (Current Volume -3dB) */
if ((sma1307->cur_vol + 6) <= 0xFA) {
@@ -1615,8 +1616,9 @@ static void sma1307_check_fault_worker(struct work_struct *work)
regmap_write(sma1307->regmap,
SMA1307_0A_SPK_VOL,
sma1307->cur_vol);
- envp[1] = kasprintf(GFP_KERNEL,
- "VOLUME=0x%02X", sma1307->cur_vol);
+ snprintf(volume, sizeof(volume),
+ "VOLUME=0x%02X", sma1307->cur_vol);
+ envp[1] = volume;
}
}
sma1307->tsdw_cnt++;
@@ -1625,48 +1627,53 @@ static void sma1307_check_fault_worker(struct work_struct *work)
SMA1307_0A_SPK_VOL, sma1307->init_vol);
sma1307->tsdw_cnt = 0;
sma1307->cur_vol = sma1307->init_vol;
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR");
- envp[1] = kasprintf(GFP_KERNEL,
- "VOLUME=0x%02X", sma1307->cur_vol);
+ envp[0] = "STATUS=OT1_CLEAR";
+ snprintf(volume, sizeof(volume), "VOLUME=0x%02X",
+ sma1307->cur_vol);
+ envp[1] = volume;
}
if (~status1_val & SMA1307_OT2_OK_STATUS) {
dev_crit(sma1307->dev,
"%s: OT2(Over Temperature Level 2)\n", __func__);
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2");
+ envp[0] = "STATUS=OT2";
+ envp[1] = NULL;
}
if (status1_val & SMA1307_UVLO_STATUS) {
dev_crit(sma1307->dev,
"%s: UVLO(Under Voltage Lock Out)\n", __func__);
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO");
+ envp[0] = "STATUS=UVLO";
+ envp[1] = NULL;
}
if (status1_val & SMA1307_OVP_BST_STATUS) {
dev_crit(sma1307->dev,
"%s: OVP_BST(Over Voltage Protection)\n", __func__);
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST");
+ envp[0] = "STATUS=OVP_BST";
+ envp[1] = NULL;
}
if (status2_val & SMA1307_OCP_SPK_STATUS) {
dev_crit(sma1307->dev,
"%s: OCP_SPK(Over Current Protect SPK)\n", __func__);
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK");
+ envp[0] = "STATUS=OCP_SPK";
+ envp[1] = NULL;
}
if (status2_val & SMA1307_OCP_BST_STATUS) {
dev_crit(sma1307->dev,
"%s: OCP_BST(Over Current Protect Boost)\n", __func__);
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST");
+ envp[0] = "STATUS=OCP_BST";
+ envp[1] = NULL;
}
if (status2_val & SMA1307_CLK_MON_STATUS) {
dev_crit(sma1307->dev,
"%s: CLK_FAULT(No clock input)\n", __func__);
- envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT");
+ envp[0] = "STATUS=CLK_FAULT";
+ envp[1] = NULL;
}
if (envp[0] != NULL) {
if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp))
dev_err(sma1307->dev,
"%s: Error sending uevent\n", __func__);
- kfree(envp[0]);
- kfree(envp[1]);
}
if (sma1307->check_fault_status) {
@@ -2008,8 +2015,8 @@ static void sma1307_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id sma1307_i2c_id[] = {
- { "sma1307a" },
- { "sma1307aq" },
+ { .name = "sma1307a" },
+ { .name = "sma1307aq" },
{ }
};
diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c
index 55f00ce7c718..4157f7787801 100644
--- a/sound/soc/codecs/src4xxx-i2c.c
+++ b/sound/soc/codecs/src4xxx-i2c.c
@@ -6,7 +6,6 @@
// Author: Matt Flax <flatmax@flatmax.com>
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -19,7 +18,7 @@ static int src4xxx_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id src4xxx_i2c_ids[] = {
- { "src4392" },
+ { .name = "src4392" },
{ }
};
MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids);
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index 9008e5416004..5192569ba6a8 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -794,7 +794,7 @@ MODULE_DEVICE_TABLE(of, ssm2518_dt_ids);
#endif
static const struct i2c_device_id ssm2518_i2c_ids[] = {
- { "ssm2518" },
+ { .name = "ssm2518" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids);
diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c
index 49c74cba17c7..23570d0a2f53 100644
--- a/sound/soc/codecs/ssm2602-i2c.c
+++ b/sound/soc/codecs/ssm2602-i2c.c
@@ -26,9 +26,9 @@ static int ssm2602_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id ssm2602_i2c_id[] = {
- { "ssm2602", SSM2602 },
- { "ssm2603", SSM2602 },
- { "ssm2604", SSM2604 },
+ { .name = "ssm2602", .driver_data = SSM2602 },
+ { .name = "ssm2603", .driver_data = SSM2602 },
+ { .name = "ssm2604", .driver_data = SSM2604 },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id);
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 15f88624faeb..8415dd163edd 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -472,7 +472,7 @@ static int ssm4567_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id ssm4567_i2c_ids[] = {
- { "ssm4567" },
+ { .name = "ssm4567" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index b9f9784f5164..652c6e3a9e63 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -1154,9 +1154,9 @@ static int sta32x_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id sta32x_i2c_id[] = {
- { "sta326" },
- { "sta328" },
- { "sta329" },
+ { .name = "sta326" },
+ { .name = "sta328" },
+ { .name = "sta329" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id);
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 71af82b099c0..99c7f7ac807b 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -1236,11 +1236,8 @@ static int sta350_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void sta350_i2c_remove(struct i2c_client *client)
-{}
-
static const struct i2c_device_id sta350_i2c_id[] = {
- { "sta350" },
+ { .name = "sta350" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta350_i2c_id);
@@ -1250,8 +1247,7 @@ static struct i2c_driver sta350_i2c_driver = {
.name = "sta350",
.of_match_table = of_match_ptr(st350_dt_ids),
},
- .probe = sta350_i2c_probe,
- .remove = sta350_i2c_remove,
+ .probe = sta350_i2c_probe,
.id_table = sta350_i2c_id,
};
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 946aa6a4e57c..7d57999f9cb1 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -361,7 +361,7 @@ static int sta529_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id sta529_i2c_id[] = {
- { "sta529" },
+ { .name = "sta529" },
{ }
};
MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c
new file mode 100644
index 000000000000..ace06f5ab58c
--- /dev/null
+++ b/sound/soc/codecs/tac5xx2-sdw.c
@@ -0,0 +1,2039 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// ALSA SoC Texas Instruments TAC5XX2 Audio Smart Amplifier
+//
+// Copyright (C) 2025 Texas Instruments Incorporated
+// https://www.ti.com
+//
+// Author: Niranjan H Y <niranjan.hy@ti.com>
+
+#include <linux/err.h>
+#include <linux/firmware.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/pci.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 <linux/time.h>
+#include <linux/unaligned.h>
+#include <sound/pcm_params.h>
+#include <sound/sdw.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/sdca_asoc.h>
+#include <sound/sdca_function.h>
+#include <sound/sdca_regmap.h>
+#include <sound/jack.h>
+
+#include "tac5xx2.h"
+
+#define TAC5XX2_PROBE_TIMEOUT_MS 3000
+#define TAC5XX2_FW_CACHE_TIMEOUT_MS 300
+
+#define TAC5XX2_DEVICE_RATES (SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200)
+#define TAC5XX2_DEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+/* Define channel constants */
+#define TAC_CHANNEL_LEFT 1
+#define TAC_CHANNEL_RIGHT 2
+#define TAC_JACK_MONO_CS 2
+
+#define TAC_MUTE_REG(func, fu, ch) \
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_##func, TAC_SDCA_ENT_##fu, \
+ TAC_SDCA_CHANNEL_MUTE, TAC_CHANNEL_##ch)
+#define TAC_USAGE_REG(func, ent) \
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_##func, TAC_SDCA_ENT_##ent, \
+ TAC_SDCA_CTL_USAGE, 0)
+#define TAC_XU_BYPASS_REG(func, xu) \
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_##func, TAC_SDCA_ENT_##xu, \
+ TAC_SDCA_CTL_XU_BYPASS, 0)
+
+/* mute registers */
+#define FU21_L_MUTE_REG TAC_MUTE_REG(SA, FU21, LEFT)
+#define FU21_R_MUTE_REG TAC_MUTE_REG(SA, FU21, RIGHT)
+#define FU23_L_MUTE_REG TAC_MUTE_REG(SA, FU23, LEFT)
+#define FU23_R_MUTE_REG TAC_MUTE_REG(SA, FU23, RIGHT)
+#define FU26_MUTE_REG TAC_MUTE_REG(SA, FU26, LEFT)
+#define FU11_L_MUTE_REG TAC_MUTE_REG(SM, FU11, LEFT)
+#define FU11_R_MUTE_REG TAC_MUTE_REG(SM, FU11, RIGHT)
+#define FU113_L_MUTE_REG TAC_MUTE_REG(SM, FU113, LEFT)
+#define FU113_R_MUTE_REG TAC_MUTE_REG(SM, FU113, RIGHT)
+#define FU41_L_MUTE_REG TAC_MUTE_REG(UAJ, FU41, LEFT)
+#define FU41_R_MUTE_REG TAC_MUTE_REG(UAJ, FU41, RIGHT)
+#define FU36_MUTE_REG TAC_MUTE_REG(UAJ, FU36, RIGHT)
+
+/* it/ot usage */
+#define IT11_USAGE_REG TAC_USAGE_REG(SM, IT11)
+#define IT41_USAGE_REG TAC_USAGE_REG(UAJ, IT41)
+#define IT33_USAGE_REG TAC_USAGE_REG(UAJ, IT33)
+#define OT113_USAGE_REG TAC_USAGE_REG(SM, OT113)
+#define OT45_USAGE_REG TAC_USAGE_REG(UAJ, OT45)
+#define OT36_USAGE_REG TAC_USAGE_REG(UAJ, OT36)
+
+/* xu bypass */
+#define XU12_BYPASS_REG TAC_XU_BYPASS_REG(SM, XU12)
+#define XU42_BYPASS_REG TAC_XU_BYPASS_REG(UAJ, XU42)
+
+#define TAC_DSP_ALGO_STATUS TAC_REG_SDW(0, 3, 12)
+#define TAC_DSP_ALGO_STATUS_RUNNING 0x20
+#define TAC_FW_HDR_SIZE 88
+#define TAC_FW_FILE_HDR 20
+#define TAC_MAX_FW_CHUNKS 512
+
+struct tac_fw_hdr {
+ u32 size;
+ u32 version_offset;
+ u32 plt_id;
+ u32 ppc3_ver;
+ u64 timestamp;
+ u8 ddc_name[64];
+};
+
+/* Firmware file/chunk structure */
+struct tac_fw_file {
+ u32 vendor_id;
+ u32 file_id;
+ u32 version;
+ u32 length;
+ u32 dest_addr;
+ u8 *fw_data;
+};
+
+/* TLV for volume control */
+static const DECLARE_TLV_DB_SCALE(tac5xx2_amp_tlv, 0, 50, 0);
+static const DECLARE_TLV_DB_SCALE(tac5xx2_dvc_tlv, -7200, 50, 0);
+
+/* Q7.8 volume control parameters: range -72dB to +6dB, step 0.5dB */
+#define TAC_DVC_STEP 128 /* 0.5 dB in Q7.8 format */
+#define TAC_DVC_MIN (-144) /* -72 dB / 0.5 dB step */
+#define TAC_DVC_MAX 12 /* +6 dB / 0.5 dB step */
+
+/* TAC-specific stereo volume control macro using SDW_SDCA_CTL (single control for L/R) */
+#define TAC_DOUBLE_Q78_TLV(name, func_id, ent_id) \
+ SDCA_DOUBLE_Q78_TLV(name, \
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_##func_id, TAC_SDCA_ENT_##ent_id, \
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), \
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_##func_id, TAC_SDCA_ENT_##ent_id, \
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), \
+ TAC_DVC_MIN, TAC_DVC_MAX, TAC_DVC_STEP, tac5xx2_dvc_tlv)
+
+struct tac5xx2_prv {
+ struct snd_soc_component *component;
+ struct sdw_slave *sdw_peripheral;
+ struct sdca_function_data *sa_func_data;
+ struct sdca_function_data *sm_func_data;
+ struct sdca_function_data *uaj_func_data;
+ struct sdca_function_data *hid_func_data;
+ enum sdw_slave_status status;
+ struct regmap *regmap;
+ struct device *dev;
+ bool hw_init;
+ bool first_hw_init_done;
+ u32 part_id;
+ struct snd_soc_jack *hs_jack;
+ int jack_type;
+ /* Custom fw binary. UMP File Download is not used. */
+ unsigned int fw_file_cnt;
+ struct tac_fw_file *fw_files;
+ struct completion fw_caching_complete;
+ bool fw_dl_success;
+ u8 fw_binaryname[64];
+};
+
+static const struct reg_default tac_reg_default[] = {
+ {TAC_SW_RESET, 0x0},
+ {TAC_SLEEP_MODEZ, 0x0},
+ {TAC_FEATURE_PDZ, 0x0},
+ {TAC_TX_CH_EN, 0xf0},
+ {TAC_REG_SDW(0, 0, 0x5), 0xcf},
+ {TAC_REG_SDW(0, 0, 0x6), 0xa},
+ {TAC_REG_SDW(0, 0, 0x7), 0x0},
+ {TAC_REG_SDW(0, 0, 0x8), 0xfe},
+ {TAC_REG_SDW(0, 0, 0x9), 0x9},
+ {TAC_REG_SDW(0, 0, 0xa), 0x28},
+ {TAC_REG_SDW(0, 0, 0xb), 0x1},
+ {TAC_REG_SDW(0, 0, 0xc), 0x11},
+ {TAC_REG_SDW(0, 0, 0xd), 0x11},
+ {TAC_REG_SDW(0, 0, 0xe), 0x61},
+ {TAC_REG_SDW(0, 0, 0xf), 0x0},
+ {TAC_REG_SDW(0, 0, 0x10), 0x50},
+ {TAC_REG_SDW(0, 0, 0x11), 0x70},
+ {TAC_REG_SDW(0, 0, 0x12), 0x60},
+ {TAC_REG_SDW(0, 0, 0x13), 0x28},
+ {TAC_REG_SDW(0, 0, 0x14), 0x0},
+ {TAC_REG_SDW(0, 0, 0x15), 0x18},
+ {TAC_REG_SDW(0, 0, 0x16), 0x20},
+ {TAC_REG_SDW(0, 0, 0x17), 0x0},
+ {TAC_REG_SDW(0, 0, 0x18), 0x18},
+ {TAC_REG_SDW(0, 0, 0x19), 0x54},
+ {TAC_REG_SDW(0, 0, 0x1a), 0x8},
+ {TAC_REG_SDW(0, 0, 0x1b), 0x0},
+ {TAC_REG_SDW(0, 0, 0x1c), 0x30},
+ {TAC_REG_SDW(0, 0, 0x1d), 0x0},
+ {TAC_REG_SDW(0, 0, 0x1e), 0x0},
+ {TAC_REG_SDW(0, 0, 0x1f), 0x0},
+ {TAC_REG_SDW(0, 0, 0x20), 0x0},
+ {TAC_REG_SDW(0, 0, 0x21), 0x20},
+ {TAC_REG_SDW(0, 0, 0x22), 0x21},
+ {TAC_REG_SDW(0, 0, 0x23), 0x22},
+ {TAC_REG_SDW(0, 0, 0x24), 0x23},
+ {TAC_REG_SDW(0, 0, 0x25), 0x4},
+ {TAC_REG_SDW(0, 0, 0x26), 0x5},
+ {TAC_REG_SDW(0, 0, 0x27), 0x6},
+ {TAC_REG_SDW(0, 0, 0x28), 0x7},
+ {TAC_REG_SDW(0, 0, 0x29), 0x0},
+ {TAC_REG_SDW(0, 0, 0x2a), 0x0},
+ {TAC_REG_SDW(0, 0, 0x2b), 0x0},
+ {TAC_REG_SDW(0, 0, 0x2c), 0x20},
+ {TAC_REG_SDW(0, 0, 0x2d), 0x21},
+ {TAC_REG_SDW(0, 0, 0x2e), 0x2},
+ {TAC_REG_SDW(0, 0, 0x2f), 0x3},
+ {TAC_REG_SDW(0, 0, 0x30), 0x4},
+ {TAC_REG_SDW(0, 0, 0x31), 0x5},
+ {TAC_REG_SDW(0, 0, 0x32), 0x6},
+ {TAC_REG_SDW(0, 0, 0x33), 0x7},
+ {TAC_REG_SDW(0, 0, 0x34), 0x0},
+ {TAC_REG_SDW(0, 0, 0x35), 0x90},
+ {TAC_REG_SDW(0, 0, 0x36), 0x80},
+ {TAC_REG_SDW(0, 0, 0x37), 0x0},
+ {TAC_REG_SDW(0, 0, 0x39), 0x0},
+ {TAC_REG_SDW(0, 0, 0x3a), 0x90},
+ {TAC_REG_SDW(0, 0, 0x3b), 0x80},
+ {TAC_REG_SDW(0, 0, 0x3c), 0x0},
+ {TAC_REG_SDW(0, 0, 0x3e), 0x0},
+ {TAC_REG_SDW(0, 0, 0x3f), 0x90},
+ {TAC_REG_SDW(0, 0, 0x40), 0x80},
+ {TAC_REG_SDW(0, 0, 0x41), 0x0},
+ {TAC_REG_SDW(0, 0, 0x43), 0x90},
+ {TAC_REG_SDW(0, 0, 0x44), 0x80},
+ {TAC_REG_SDW(0, 0, 0x45), 0x0},
+ {TAC_REG_SDW(0, 0, 0x47), 0x90},
+ {TAC_REG_SDW(0, 0, 0x48), 0x80},
+ {TAC_REG_SDW(0, 0, 0x49), 0x0},
+ {TAC_REG_SDW(0, 0, 0x4b), 0x90},
+ {TAC_REG_SDW(0, 0, 0x4c), 0x80},
+ {TAC_REG_SDW(0, 0, 0x4d), 0x0},
+ {TAC_REG_SDW(0, 0, 0x4f), 0x31},
+ {TAC_REG_SDW(0, 0, 0x50), 0x0},
+ {TAC_REG_SDW(0, 0, 0x51), 0x0},
+ {TAC_REG_SDW(0, 0, 0x52), 0x90},
+ {TAC_REG_SDW(0, 0, 0x53), 0x80},
+ {TAC_REG_SDW(0, 0, 0x55), 0x90},
+ {TAC_REG_SDW(0, 0, 0x56), 0x80},
+ {TAC_REG_SDW(0, 0, 0x58), 0x90},
+ {TAC_REG_SDW(0, 0, 0x59), 0x80},
+ {TAC_REG_SDW(0, 0, 0x5b), 0x90},
+ {TAC_REG_SDW(0, 0, 0x5c), 0x80},
+ {TAC_REG_SDW(0, 0, 0x5e), 0x8},
+ {TAC_REG_SDW(0, 0, 0x5f), 0x8},
+ {TAC_REG_SDW(0, 0, 0x60), 0x0},
+ {TAC_REG_SDW(0, 0, 0x61), 0x0},
+ {TAC_REG_SDW(0, 0, 0x62), 0xff},
+ {TAC_REG_SDW(0, 0, 0x63), 0xc0},
+ {TAC_REG_SDW(0, 0, 0x64), 0x5},
+ {TAC_REG_SDW(0, 0, 0x65), 0x3},
+ {TAC_REG_SDW(0, 0, 0x66), 0x0},
+ {TAC_REG_SDW(0, 0, 0x67), 0x0},
+ {TAC_REG_SDW(0, 0, 0x68), 0x0},
+ {TAC_REG_SDW(0, 0, 0x69), 0x8},
+ {TAC_REG_SDW(0, 0, 0x6a), 0x0},
+ {TAC_REG_SDW(0, 0, 0x6b), 0xa0},
+ {TAC_REG_SDW(0, 0, 0x6c), 0x18},
+ {TAC_REG_SDW(0, 0, 0x6d), 0x18},
+ {TAC_REG_SDW(0, 0, 0x6e), 0x18},
+ {TAC_REG_SDW(0, 0, 0x6f), 0x18},
+ {TAC_REG_SDW(0, 0, 0x70), 0x88},
+ {TAC_REG_SDW(0, 0, 0x71), 0xff},
+ {TAC_REG_SDW(0, 0, 0x72), 0x0},
+ {TAC_REG_SDW(0, 0, 0x73), 0x31},
+ {TAC_REG_SDW(0, 0, 0x74), 0xc0},
+ {TAC_REG_SDW(0, 0, 0x75), 0x0},
+ {TAC_REG_SDW(0, 0, 0x76), 0x0},
+ {TAC_REG_SDW(0, 0, 0x77), 0x0},
+ {TAC_REG_SDW(0, 0, 0x78), 0x0},
+ {TAC_REG_SDW(0, 0, 0x7b), 0x0},
+ {TAC_REG_SDW(0, 0, 0x7c), 0xd0},
+ {TAC_REG_SDW(0, 0, 0x7d), 0x0},
+ {TAC_REG_SDW(0, 0, 0x7e), 0x0},
+ {TAC_REG_SDW(0, 1, 0x1), 0x0},
+ {TAC_REG_SDW(0, 1, 0x2), 0x0},
+ {TAC_REG_SDW(0, 1, 0x3), 0x0},
+ {TAC_REG_SDW(0, 1, 0x4), 0x4},
+ {TAC_REG_SDW(0, 1, 0x5), 0x0},
+ {TAC_REG_SDW(0, 1, 0x6), 0x0},
+ {TAC_REG_SDW(0, 1, 0x7), 0x0},
+ {TAC_REG_SDW(0, 1, 0x8), 0x0},
+ {TAC_REG_SDW(0, 1, 0x9), 0x0},
+ {TAC_REG_SDW(0, 1, 0xa), 0x0},
+ {TAC_REG_SDW(0, 1, 0xb), 0x1},
+ {TAC_REG_SDW(0, 1, 0xc), 0x0},
+ {TAC_REG_SDW(0, 1, 0xd), 0x0},
+ {TAC_REG_SDW(0, 1, 0xe), 0x0},
+ {TAC_REG_SDW(0, 1, 0xf), 0x8},
+ {TAC_REG_SDW(0, 1, 0x10), 0x0},
+ {TAC_REG_SDW(0, 1, 0x11), 0x0},
+ {TAC_REG_SDW(0, 1, 0x12), 0x1},
+ {TAC_REG_SDW(0, 1, 0x13), 0x0},
+ {TAC_REG_SDW(0, 1, 0x14), 0x0},
+ {TAC_REG_SDW(0, 1, 0x15), 0x0},
+ {TAC_REG_SDW(0, 1, 0x16), 0x0},
+ {TAC_REG_SDW(0, 1, 0x17), 0x0},
+ {TAC_REG_SDW(0, 1, 0x18), 0x0},
+ {TAC_REG_SDW(0, 1, 0x19), 0x0},
+ {TAC_REG_SDW(0, 1, 0x1a), 0x0},
+ {TAC_REG_SDW(0, 1, 0x1b), 0x0},
+ {TAC_REG_SDW(0, 1, 0x1c), 0x0},
+ {TAC_REG_SDW(0, 1, 0x1d), 0x0},
+ {TAC_REG_SDW(0, 1, 0x1e), 0x2},
+ {TAC_REG_SDW(0, 1, 0x1f), 0x8},
+ {TAC_REG_SDW(0, 1, 0x20), 0x9},
+ {TAC_REG_SDW(0, 1, 0x21), 0xa},
+ {TAC_REG_SDW(0, 1, 0x22), 0xb},
+ {TAC_REG_SDW(0, 1, 0x23), 0xc},
+ {TAC_REG_SDW(0, 1, 0x24), 0xd},
+ {TAC_REG_SDW(0, 1, 0x25), 0xe},
+ {TAC_REG_SDW(0, 1, 0x26), 0xf},
+ {TAC_REG_SDW(0, 1, 0x27), 0x8},
+ {TAC_REG_SDW(0, 1, 0x28), 0x9},
+ {TAC_REG_SDW(0, 1, 0x29), 0xa},
+ {TAC_REG_SDW(0, 1, 0x2a), 0xb},
+ {TAC_REG_SDW(0, 1, 0x2b), 0xc},
+ {TAC_REG_SDW(0, 1, 0x2c), 0xd},
+ {TAC_REG_SDW(0, 1, 0x2d), 0xe},
+ {TAC_REG_SDW(0, 1, 0x2e), 0xf},
+ {TAC_REG_SDW(0, 1, 0x2f), 0x0},
+ {TAC_REG_SDW(0, 1, 0x30), 0x0},
+ {TAC_REG_SDW(0, 1, 0x31), 0x0},
+ {TAC_REG_SDW(0, 1, 0x32), 0x0},
+ {TAC_REG_SDW(0, 1, 0x33), 0x0},
+ {TAC_REG_SDW(0, 1, 0x34), 0x0},
+ {TAC_REG_SDW(0, 1, 0x35), 0x0},
+ {TAC_REG_SDW(0, 1, 0x36), 0x0},
+ {TAC_REG_SDW(0, 1, 0x37), 0x0},
+ {TAC_REG_SDW(0, 1, 0x38), 0x98},
+ {TAC_REG_SDW(0, 1, 0x39), 0x0},
+ {TAC_REG_SDW(0, 1, 0x3a), 0x0},
+ {TAC_REG_SDW(0, 1, 0x3b), 0x0},
+ {TAC_REG_SDW(0, 1, 0x3c), 0x1},
+ {TAC_REG_SDW(0, 1, 0x3d), 0x2},
+ {TAC_REG_SDW(0, 1, 0x3e), 0x3},
+ {TAC_REG_SDW(0, 1, 0x3f), 0x4},
+ {TAC_REG_SDW(0, 1, 0x40), 0x5},
+ {TAC_REG_SDW(0, 1, 0x41), 0x6},
+ {TAC_REG_SDW(0, 1, 0x42), 0x7},
+ {TAC_REG_SDW(0, 1, 0x43), 0x0},
+ {TAC_REG_SDW(0, 1, 0x44), 0x0},
+ {TAC_REG_SDW(0, 1, 0x45), 0x1},
+ {TAC_REG_SDW(0, 1, 0x46), 0x2},
+ {TAC_REG_SDW(0, 1, 0x47), 0x3},
+ {TAC_REG_SDW(0, 1, 0x48), 0x4},
+ {TAC_REG_SDW(0, 1, 0x49), 0x5},
+ {TAC_REG_SDW(0, 1, 0x4a), 0x6},
+ {TAC_REG_SDW(0, 1, 0x4b), 0x7},
+ {TAC_REG_SDW(0, 1, 0x4c), 0x98},
+ {TAC_REG_SDW(0, 1, 0x4d), 0x0},
+ {TAC_REG_SDW(0, 1, 0x4e), 0x0},
+ {TAC_REG_SDW(0, 1, 0x4f), 0x0},
+ {TAC_REG_SDW(0, 1, 0x50), 0x1},
+ {TAC_REG_SDW(0, 1, 0x51), 0x2},
+ {TAC_REG_SDW(0, 1, 0x52), 0x3},
+ {TAC_REG_SDW(0, 1, 0x53), 0x4},
+ {TAC_REG_SDW(0, 1, 0x54), 0x5},
+ {TAC_REG_SDW(0, 1, 0x55), 0x6},
+ {TAC_REG_SDW(0, 1, 0x56), 0x7},
+ {TAC_REG_SDW(0, 1, 0x57), 0x0},
+ {TAC_REG_SDW(0, 1, 0x58), 0x0},
+ {TAC_REG_SDW(0, 1, 0x59), 0x1},
+ {TAC_REG_SDW(0, 1, 0x5a), 0x2},
+ {TAC_REG_SDW(0, 1, 0x5b), 0x3},
+ {TAC_REG_SDW(0, 1, 0x5c), 0x4},
+ {TAC_REG_SDW(0, 1, 0x5d), 0x5},
+ {TAC_REG_SDW(0, 1, 0x5e), 0x6},
+ {TAC_REG_SDW(0, 1, 0x5f), 0x7},
+ {TAC_REG_SDW(0, 1, 0x60), 0x98},
+ {TAC_REG_SDW(0, 1, 0x61), 0x0},
+ {TAC_REG_SDW(0, 1, 0x62), 0x0},
+ {TAC_REG_SDW(0, 1, 0x63), 0x0},
+ {TAC_REG_SDW(0, 1, 0x64), 0x1},
+ {TAC_REG_SDW(0, 1, 0x65), 0x2},
+ {TAC_REG_SDW(0, 1, 0x66), 0x3},
+ {TAC_REG_SDW(0, 1, 0x67), 0x4},
+ {TAC_REG_SDW(0, 1, 0x68), 0x5},
+ {TAC_REG_SDW(0, 1, 0x69), 0x6},
+ {TAC_REG_SDW(0, 1, 0x6a), 0x7},
+ {TAC_REG_SDW(0, 1, 0x6b), 0x0},
+ {TAC_REG_SDW(0, 1, 0x6c), 0x0},
+ {TAC_REG_SDW(0, 1, 0x6d), 0x1},
+ {TAC_REG_SDW(0, 1, 0x6e), 0x2},
+ {TAC_REG_SDW(0, 1, 0x6f), 0x3},
+ {TAC_REG_SDW(0, 1, 0x70), 0x4},
+ {TAC_REG_SDW(0, 1, 0x71), 0x5},
+ {TAC_REG_SDW(0, 1, 0x72), 0x6},
+ {TAC_REG_SDW(0, 1, 0x73), 0x7},
+};
+
+static const struct reg_sequence tac_spk_seq[] = {
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0),
+};
+
+static const struct reg_sequence tac_sm_seq[] = {
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0),
+};
+
+static const struct reg_sequence tac_uaj_seq[] = {
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0),
+ REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU36,
+ TAC_SDCA_CHANNEL_GAIN, TAC_JACK_MONO_CS), 0),
+};
+
+static bool tac_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAC_REG_SDW(0, 0, 1) ... TAC_REG_SDW(0, 0, 5):
+ case TAC_REG_SDW(0, 2, 1) ... TAC_REG_SDW(0, 2, 6):
+ case TAC_REG_SDW(0, 2, 24) ... TAC_REG_SDW(0, 2, 55):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1,
+ TAC_SDCA_CTL_HIDTX_CURRENT_OWNER, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1,
+ TAC_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35,
+ TAC_SDCA_CTL_DET_MODE, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_PDE23,
+ TAC_SDCA_REQUESTED_PS, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_PDE11,
+ TAC_SDCA_REQUESTED_PS, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE47,
+ TAC_SDCA_REQUESTED_PS, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE34,
+ TAC_SDCA_REQUESTED_PS, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_PDE23,
+ TAC_SDCA_ACTUAL_PS, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_PDE11,
+ TAC_SDCA_ACTUAL_PS, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE47,
+ TAC_SDCA_ACTUAL_PS, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE34,
+ TAC_SDCA_ACTUAL_PS, 0):
+ case SDW_SCP_SDCA_INT1:
+ case SDW_SCP_SDCA_INT2:
+ case SDW_SCP_SDCA_INT3:
+ case SDW_SCP_SDCA_INT4:
+ case SDW_SDCA_CTL(1, 0, 0x10, 0):
+ case SDW_SDCA_CTL(2, 0, 0x10, 0):
+ case SDW_SDCA_CTL(3, 0, 0x10, 0):
+ case SDW_SDCA_CTL(4, 0, 0x1, 0):
+ case 0x44007F80 ... 0x44007F87:
+ case TAC_DSP_ALGO_STATUS: /* DSP algo status - always read from HW */
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+static int tac_sdca_mbq_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21,
+ TAC_SDCA_CHANNEL_VOLUME, TAC_CHANNEL_LEFT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21,
+ TAC_SDCA_CHANNEL_VOLUME, TAC_CHANNEL_RIGHT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23,
+ TAC_SDCA_MASTER_GAIN, 0):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41,
+ TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT):
+ case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU36,
+ TAC_SDCA_CHANNEL_GAIN, TAC_JACK_MONO_CS):
+ return 2;
+
+ default:
+ return 1;
+ }
+}
+
+static const struct regmap_sdw_mbq_cfg tac_mbq_cfg = {
+ .mbq_size = tac_sdca_mbq_size,
+};
+
+static const struct regmap_config tac_regmap = {
+ .reg_bits = 32,
+ .val_bits = 16, /* mbq support */
+ .reg_defaults = tac_reg_default,
+ .num_reg_defaults = ARRAY_SIZE(tac_reg_default),
+ .max_register = 0x47FFFFFF,
+ .cache_type = REGCACHE_MAPLE,
+ .volatile_reg = tac_volatile_reg,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+/* Check if device has DSP algo that needs status monitoring */
+static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev)
+{
+ switch (tac_dev->part_id) {
+ case 0x5682:
+ case 0x2883:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* Check if device has UAJ (Universal Audio Jack) support */
+static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev)
+{
+ return tac_dev->uaj_func_data;
+}
+
+/* Forward declaration for headset detection */
+static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev);
+
+/* Volume controls for mic, hp and mic cap */
+static const struct snd_kcontrol_new tac5xx2_snd_controls[] = {
+ SOC_DOUBLE_R_RANGE_TLV("Amp Volume", TAC_AMP_LVL_CFG0, TAC_AMP_LVL_CFG1,
+ 2, 0, 44, 1, tac5xx2_amp_tlv),
+ TAC_DOUBLE_Q78_TLV("DMIC Capture Volume", SM, FU113),
+ TAC_DOUBLE_Q78_TLV("Speaker Volume", SA, FU21),
+};
+
+static const struct snd_kcontrol_new tac_uaj_controls[] = {
+ TAC_DOUBLE_Q78_TLV("UAJ Playback Volume", UAJ, FU41),
+ SDCA_SINGLE_Q78_TLV("UAJ Capture Volume",
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU36,
+ TAC_SDCA_CHANNEL_GAIN, TAC_JACK_MONO_CS),
+ TAC_DVC_MIN, TAC_DVC_MAX, TAC_DVC_STEP, tac5xx2_dvc_tlv),
+};
+
+static const struct snd_soc_dapm_widget tac5xx2_common_widgets[] = {
+ /* Port 1: Speaker Playback Path */
+ SND_SOC_DAPM_AIF_IN("AIF1 Playback", "DP1 Speaker Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("FU21_L", FU21_L_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("FU21_R", FU21_R_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("FU23_L", FU23_L_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("FU23_R", FU23_R_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPK_L"),
+ SND_SOC_DAPM_OUTPUT("SPK_R"),
+
+ /* Port 3: Smart Mic (DMIC) Capture Path */
+ SND_SOC_DAPM_AIF_OUT("AIF3 Capture", "DP3 Mic Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("DMIC_L"),
+ SND_SOC_DAPM_INPUT("DMIC_R"),
+ SND_SOC_DAPM_PGA("IT11", IT11_USAGE_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CS113", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("FU11_L", FU11_L_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("FU11_R", FU11_R_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("PPU11", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("XU12", XU12_BYPASS_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("FU113_L", FU113_L_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("FU113_R", FU113_R_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("OT113", OT113_USAGE_REG, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_widget tac_uaj_widgets[] = {
+ /* Port 4: UAJ (Headphone) Playback Path */
+ SND_SOC_DAPM_AIF_IN("AIF4 Playback", "DP4 UAJ Speaker Playback", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_PGA("IT41", IT41_USAGE_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("FU41_L", FU41_L_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("FU41_R", FU41_R_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("XU42", XU42_BYPASS_REG, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CS41", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("OT45", "DP4 UAJ Speaker Playback", OT45_USAGE_REG, 0, 0),
+ SND_SOC_DAPM_OUTPUT("HP_L"),
+ SND_SOC_DAPM_OUTPUT("HP_R"),
+
+ /* Port 7: UAJ (Headset Mic) Capture Path */
+ SND_SOC_DAPM_AIF_OUT("AIF7 Capture", "DP7 UAJ Mic Capture", 0,
+ SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("UAJ_MIC"),
+ SND_SOC_DAPM_ADC("IT33", "DP7 UAJ Mic Capture", IT33_USAGE_REG, 0, 0),
+ SND_SOC_DAPM_PGA("FU36", FU36_MUTE_REG, 0, 1, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("CS36", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("OT36", OT36_USAGE_REG, 0, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route tac5xx2_common_routes[] = {
+ /* Speaker Playback Path */
+ {"FU21_L", NULL, "AIF1 Playback"},
+ {"FU21_R", NULL, "AIF1 Playback"},
+
+ {"FU23_L", NULL, "FU21_L"},
+ {"FU23_R", NULL, "FU21_R"},
+
+ {"SPK_L", NULL, "FU23_L"},
+ {"SPK_R", NULL, "FU23_R"},
+
+ /* Smart Mic DAPM Routes */
+ {"IT11", NULL, "DMIC_L"},
+ {"IT11", NULL, "DMIC_R"},
+ {"FU11_L", NULL, "IT11"},
+ {"FU11_R", NULL, "IT11"},
+ {"PPU11", NULL, "FU11_L"},
+ {"PPU11", NULL, "FU11_R"},
+ {"XU12", NULL, "PPU11"},
+ {"FU113_L", NULL, "XU12"},
+ {"FU113_R", NULL, "XU12"},
+ {"FU113_L", NULL, "CS113"},
+ {"FU113_R", NULL, "CS113"},
+ {"OT113", NULL, "FU113_L"},
+ {"OT113", NULL, "FU113_R"},
+ {"OT113", NULL, "CS113"},
+ {"AIF3 Capture", NULL, "OT113"},
+};
+
+static const struct snd_soc_dapm_route tac_uaj_routes[] = {
+ /* UAJ Playback routes */
+ {"IT41", NULL, "AIF4 Playback"},
+ {"IT41", NULL, "CS41"},
+ {"FU41_L", NULL, "IT41"},
+ {"FU41_R", NULL, "IT41"},
+ {"XU42", NULL, "FU41_L"},
+ {"XU42", NULL, "FU41_R"},
+ {"OT45", NULL, "XU42"},
+ {"OT45", NULL, "CS41"},
+ {"HP_L", NULL, "OT45"},
+ {"HP_R", NULL, "OT45"},
+
+ /* UAJ Capture routes */
+ {"IT33", NULL, "UAJ_MIC"},
+ {"IT33", NULL, "CS36"},
+ {"FU36", NULL, "IT33"},
+ {"OT36", NULL, "FU36"},
+ {"OT36", NULL, "CS36"},
+ {"AIF7 Capture", NULL, "OT36"},
+};
+
+static s32 tac_set_sdw_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, s32 direction)
+{
+ if (sdw_stream)
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void tac_sdw_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int tac_clear_latch(struct tac5xx2_prv *priv)
+{
+ /* CLR_REG is a self-clearing bit */
+ return regmap_update_bits(priv->regmap, TAC_INT_CFG,
+ TAC_INT_CFG_CLR_REG, TAC_INT_CFG_CLR_REG);
+}
+
+static int tac_sdw_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 tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component);
+ struct sdw_slave *sdw_peripheral = tac_dev->sdw_peripheral;
+ struct sdw_stream_runtime *sdw_stream;
+ struct sdw_stream_config stream_config = {0};
+ struct sdw_port_config port_config = {0};
+ u8 sample_rate_idx = 0;
+ int function_id;
+ int pde_entity;
+ int port_num;
+ int ret;
+
+ if (!tac_dev->hw_init) {
+ dev_err(tac_dev->dev,
+ "error: operation without hw initialization");
+ return -EINVAL;
+ }
+
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!sdw_stream) {
+ dev_err(tac_dev->dev, "failed to get dma data");
+ return -EINVAL;
+ }
+
+ ret = tac_clear_latch(tac_dev);
+ if (ret)
+ dev_warn(tac_dev->dev, "clear latch failed, err=%d", ret);
+
+ switch (dai->id) {
+ case TAC5XX2_DMIC:
+ function_id = TAC_FUNCTION_ID_SM;
+ pde_entity = TAC_SDCA_ENT_PDE11;
+ port_num = TAC_SDW_PORT_NUM_DMIC;
+ break;
+ case TAC5XX2_UAJ:
+ function_id = TAC_FUNCTION_ID_UAJ;
+ pde_entity = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ TAC_SDCA_ENT_PDE47 : TAC_SDCA_ENT_PDE34;
+ port_num = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ TAC_SDW_PORT_NUM_UAJ_PLAYBACK :
+ TAC_SDW_PORT_NUM_UAJ_CAPTURE;
+ break;
+ case TAC5XX2_SPK:
+ function_id = TAC_FUNCTION_ID_SA;
+ pde_entity = TAC_SDCA_ENT_PDE23;
+ port_num = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ TAC_SDW_PORT_NUM_SPK_PLAYBACK :
+ TAC_SDW_PORT_NUM_SPK_CAPTURE;
+ break;
+ default:
+ dev_err(tac_dev->dev, "Invalid dai id: %d for power up\n", dai->id);
+ return -EINVAL;
+ }
+
+ snd_sdw_params_to_config(substream, params, &stream_config, &port_config);
+ port_config.num = port_num;
+ ret = sdw_stream_add_slave(sdw_peripheral, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev,
+ "Unable to configure port %d: %d\n", port_num, ret);
+ return ret;
+ }
+
+ switch (params_rate(params)) {
+ case 48000:
+ sample_rate_idx = 0x01;
+ break;
+ case 44100:
+ sample_rate_idx = 0x02;
+ break;
+ case 96000:
+ sample_rate_idx = 0x03;
+ break;
+ case 88200:
+ sample_rate_idx = 0x04;
+ break;
+ default:
+ dev_dbg(tac_dev->dev, "Unsupported sample rate: %d Hz",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ switch (function_id) {
+ case TAC_FUNCTION_ID_SM:
+ ret = regmap_write(tac_dev->regmap,
+ SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS113,
+ TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0),
+ sample_rate_idx);
+ if (ret) {
+ dev_err(tac_dev->dev, "Failed to set CS113 sample rate: %d", ret);
+ return ret;
+ }
+
+ break;
+ case TAC_FUNCTION_ID_UAJ:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ ret = regmap_write(tac_dev->regmap,
+ SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS41,
+ TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0),
+ sample_rate_idx);
+ if (ret) {
+ dev_err(tac_dev->dev, "Failed to set CS41 sample rate: %d", ret);
+ return ret;
+ }
+ } else {
+ ret = regmap_write(tac_dev->regmap,
+ SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS36,
+ TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0),
+ sample_rate_idx);
+ if (ret) {
+ dev_err(tac_dev->dev, "Failed to set CS36 sample rate: %d", ret);
+ return ret;
+ }
+ }
+ break;
+ case TAC_FUNCTION_ID_SA:
+ /* SmartAmp: no additional sample rate configuration needed */
+ break;
+ }
+
+ ret = regmap_write(tac_dev->regmap, SDW_SDCA_CTL(function_id, pde_entity,
+ TAC_SDCA_REQUESTED_PS, 0), 0);
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "failed to set func %d, entity %d's requested PS to 0: %d\n",
+ function_id, pde_entity, ret);
+ return ret;
+ }
+
+ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity,
+ SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0);
+ if (ret)
+ dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n",
+ function_id, pde_entity, ret);
+ return ret;
+}
+
+static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(dai->component);
+ int pde_entity, function_id;
+ int ret;
+
+ sdw_stream_remove_slave(tac_dev->sdw_peripheral, sdw_stream);
+
+ switch (dai->id) {
+ case TAC5XX2_DMIC:
+ pde_entity = TAC_SDCA_ENT_PDE11;
+ function_id = TAC_FUNCTION_ID_SM;
+ break;
+ case TAC5XX2_UAJ:
+ function_id = TAC_FUNCTION_ID_UAJ;
+ pde_entity = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+ TAC_SDCA_ENT_PDE47 : TAC_SDCA_ENT_PDE34;
+ break;
+ case TAC5XX2_SPK:
+ function_id = TAC_FUNCTION_ID_SA;
+ pde_entity = TAC_SDCA_ENT_PDE23;
+ break;
+ default:
+ dev_err(tac_dev->dev, "unhandled dai %d for power down\n", dai->id);
+ return -EINVAL;
+ }
+
+ ret = regmap_write(tac_dev->regmap, SDW_SDCA_CTL(function_id, pde_entity,
+ TAC_SDCA_REQUESTED_PS, 0),
+ SDCA_PDE_PS3);
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "failed to set func %d, entity %d's requested PS to 3: %d\n",
+ function_id, pde_entity, ret);
+ return ret;
+ }
+
+ ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id,
+ pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3,
+ NULL, 0);
+ if (ret)
+ dev_err(tac_dev->dev,
+ "failed to transition func %d, pde %d from PS0 -> PS3, err=%d\n",
+ function_id, pde_entity, ret);
+
+ return ret;
+}
+
+static const struct snd_soc_dai_ops tac_dai_ops = {
+ .hw_params = tac_sdw_hw_params,
+ .hw_free = tac_sdw_pcm_hw_free,
+ .set_stream = tac_set_sdw_stream,
+ .shutdown = tac_sdw_shutdown,
+};
+
+static int tac5xx2_sdca_btn_type(unsigned char *buffer, struct tac5xx2_prv *tac_dev)
+{
+ switch (*buffer) {
+ case 1: /* play pause */
+ return SND_JACK_BTN_0;
+ case 10: /* vol down */
+ return SND_JACK_BTN_3;
+ case 8: /* vol up */
+ return SND_JACK_BTN_2;
+ case 4: /* long press */
+ return SND_JACK_BTN_1;
+ case 2: /* next song */
+ case 32: /* next song */
+ return SND_JACK_BTN_4;
+ default:
+ return 0;
+ }
+}
+
+static int tac5xx2_sdca_button_detect(struct tac5xx2_prv *tac_dev)
+{
+ unsigned int btn_type, offset, idx;
+ int ret, value, owner;
+ u8 buf[2];
+
+ ret = regmap_read(tac_dev->regmap,
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1,
+ TAC_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner);
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "Failed to read current UMP message owner 0x%x", ret);
+ return ret;
+ }
+
+ if (owner == SDCA_UMP_OWNER_DEVICE) {
+ dev_dbg(tac_dev->dev, "skip button detect as current owner is not host\n");
+ return 0;
+ }
+
+ ret = regmap_read(tac_dev->regmap,
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1,
+ TAC_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset);
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "Failed to read current UMP message offset: %d", ret);
+ goto end_btn_det;
+ }
+
+ dev_dbg(tac_dev->dev, "button detect: message offset = %x", offset);
+
+ for (idx = 0; idx < sizeof(buf); idx++) {
+ ret = regmap_read(tac_dev->regmap,
+ TAC_BUF_ADDR_HID1 + offset + idx, &value);
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "Failed to read HID buffer: %d", ret);
+ goto end_btn_det;
+ }
+ buf[idx] = value & 0xff;
+ }
+
+ if (buf[0] == 0x1) {
+ btn_type = tac5xx2_sdca_btn_type(&buf[1], tac_dev);
+ ret = btn_type;
+ }
+
+end_btn_det:
+ regmap_write(tac_dev->regmap,
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1,
+ TAC_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01);
+
+ return ret;
+}
+
+static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev)
+{
+ int val, ret;
+
+ ret = regmap_read(tac_dev->regmap,
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35,
+ TAC_SDCA_CTL_DET_MODE, 0), &val);
+ if (ret) {
+ dev_err(tac_dev->dev, "Failed to read the detect mode");
+ return ret;
+ }
+
+ switch (val) {
+ case 4:
+ tac_dev->jack_type = SND_JACK_MICROPHONE;
+ break;
+ case 5:
+ tac_dev->jack_type = SND_JACK_HEADPHONE;
+ break;
+ case 6:
+ tac_dev->jack_type = SND_JACK_HEADSET;
+ break;
+ case 0:
+ default:
+ tac_dev->jack_type = 0;
+ break;
+ }
+
+ ret = regmap_write(tac_dev->regmap,
+ SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35,
+ TAC_SDCA_CTL_SEL_MODE, 0), val);
+ if (ret)
+ dev_err(tac_dev->dev, "Failed to update the jack type to device");
+
+ return 0;
+}
+
+static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev)
+{
+ int ret = 0;
+
+ if (!tac_dev->hs_jack)
+ goto disable_interrupts;
+
+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2,
+ SDW_SCP_SDCA_INTMASK_SDCA_11);
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "Failed to register jack detection interrupt: %d\n", ret);
+ goto disable_interrupts;
+ }
+
+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3,
+ SDW_SCP_SDCA_INTMASK_SDCA_16);
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "Failed to register for button detect interrupt: %d\n", ret);
+ goto disable_interrupts;
+ }
+
+ return 0;
+
+disable_interrupts:
+ /* ignore errors while disabling interrupts */
+ regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, 0);
+ regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, 0);
+
+ return ret;
+}
+
+static int tac5xx2_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *hs_jack, void *data)
+{
+ struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ tac_dev->hs_jack = hs_jack;
+
+ /* resume can happen only after first hw_init */
+ if (!tac_dev->first_hw_init_done)
+ return 0;
+
+ ret = pm_runtime_resume_and_get(component->dev);
+ if (ret < 0) {
+ if (ret != -EACCES) {
+ dev_err(component->dev,
+ "%s: failed to resume %d\n", __func__, ret);
+ return ret;
+ }
+
+ /* pm_runtime not enabled yet */
+ dev_dbg(component->dev,
+ "%s: skipping jack init for now\n", __func__);
+ return 0;
+ }
+
+ ret = tac5xx2_jack_init(tac_dev);
+ if (ret)
+ dev_err(tac_dev->dev, "jack init failed, err=%d\n", ret);
+
+ pm_runtime_mark_last_busy(component->dev);
+ pm_runtime_put_autosuspend(component->dev);
+
+ return ret;
+}
+
+static int tac_interrupt_callback(struct sdw_slave *slave,
+ struct sdw_slave_intr_status *status)
+{
+ unsigned int sdca_int2, sdca_int3, jack_report_mask = 0;
+ struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev);
+ struct device *dev = &slave->dev;
+ int btn_type = 0;
+ int ret = 0;
+
+ if (status->control_port) {
+ if (status->control_port & SDW_SCP_INT1_PARITY)
+ dev_warn(dev, "SCP: Parity error interrupt");
+ if (status->control_port & SDW_SCP_INT1_BUS_CLASH)
+ dev_warn(dev, "SCP: Bus clash interrupt");
+ }
+
+ if (!tac_has_uaj_support(tac_dev))
+ return 0;
+
+ ret = regmap_read(tac_dev->regmap, SDW_SCP_SDCA_INT2, &sdca_int2);
+ if (ret) {
+ dev_err(dev, "Failed to read UAJ Interrupt, reg:%#x err=%d\n",
+ SDW_SCP_SDCA_INT2, ret);
+ return ret;
+ }
+
+ ret = regmap_read(tac_dev->regmap, SDW_SCP_SDCA_INT3, &sdca_int3);
+ if (ret) {
+ dev_err(dev, "Failed to read HID interrupt reg=%#x: err=%d",
+ SDW_SCP_SDCA_INT3, ret);
+ return ret;
+ }
+
+ dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n",
+ sdca_int2, sdca_int3);
+
+ if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) {
+ ret = tac5xx2_sdca_headset_detect(tac_dev);
+ if (ret < 0)
+ goto clear;
+ jack_report_mask |= SND_JACK_HEADSET;
+ }
+
+ if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) {
+ btn_type = tac5xx2_sdca_button_detect(tac_dev);
+ if (btn_type < 0)
+ btn_type = 0;
+ jack_report_mask |= SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4;
+ }
+
+ if (tac_dev->jack_type == 0)
+ btn_type = 0;
+
+ dev_dbg(tac_dev->dev, "in %s, jack_type=%d\n", __func__, tac_dev->jack_type);
+ dev_dbg(tac_dev->dev, "in %s, btn_type=0x%x\n", __func__, btn_type);
+
+ if (!tac_dev->hs_jack)
+ goto clear;
+
+ snd_soc_jack_report(tac_dev->hs_jack, tac_dev->jack_type | btn_type,
+ jack_report_mask);
+
+clear:
+ if (sdca_int2) {
+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INT2, sdca_int2);
+ if (ret)
+ dev_dbg(tac_dev->dev, "Failed to clear jack interrupt\n");
+ }
+
+ if (sdca_int3) {
+ ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INT3, sdca_int3);
+ if (ret)
+ dev_dbg(tac_dev->dev, "failed to clear hid interrupt\n");
+ }
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver tac5572_dai_driver[] = {
+ {
+ .name = "tac5xx2-aif1",
+ .id = TAC5XX2_SPK,
+ .playback = {
+ .stream_name = "DP1 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+ {
+ .name = "tac5xx2-aif2",
+ .id = TAC5XX2_DMIC,
+ .capture = {
+ .stream_name = "DP3 Mic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+ {
+ .name = "tac5xx2-aif3",
+ .id = TAC5XX2_UAJ,
+ .playback = {
+ .stream_name = "DP4 UAJ Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP7 UAJ Mic Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+};
+
+static struct snd_soc_dai_driver tac5672_dai_driver[] = {
+ {
+ .name = "tac5xx2-aif1",
+ .id = TAC5XX2_SPK,
+ .playback = {
+ .stream_name = "DP1 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP8 IV Sense Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "tac5xx2-aif2",
+ .id = TAC5XX2_DMIC,
+ .capture = {
+ .stream_name = "DP3 Mic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+ {
+ .name = "tac5xx2-aif3",
+ .id = TAC5XX2_UAJ,
+ .playback = {
+ .stream_name = "DP4 UAJ Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP7 UAJ Mic Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+};
+
+static struct snd_soc_dai_driver tac5682_dai_driver[] = {
+ {
+ .name = "tac5xx2-aif1",
+ .id = TAC5XX2_SPK,
+ .playback = {
+ .stream_name = "DP1 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Echo Reference Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "tac5xx2-aif2",
+ .id = TAC5XX2_DMIC,
+ .capture = {
+ .stream_name = "DP3 Mic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+ {
+ .name = "tac5xx2-aif3",
+ .id = TAC5XX2_UAJ,
+ .playback = {
+ .stream_name = "DP4 UAJ Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP7 UAJ Mic Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+};
+
+static struct snd_soc_dai_driver tas2883_dai_driver[] = {
+ {
+ .name = "tac5xx2-aif1",
+ .id = TAC5XX2_SPK,
+ .playback = {
+ .stream_name = "DP1 Speaker Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ .symmetric_rate = 1,
+ },
+ {
+ .name = "tac5xx2-aif2",
+ .id = TAC5XX2_DMIC,
+ .capture = {
+ .stream_name = "DP3 Mic Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = TAC5XX2_DEVICE_RATES,
+ .formats = TAC5XX2_DEVICE_FORMATS,
+ },
+ .ops = &tac_dai_ops,
+ },
+};
+
+static s32 tac_component_probe(struct snd_soc_component *component)
+{
+ struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ if (!tac_has_uaj_support(tac_dev))
+ goto done_comp_probe;
+
+ ret = snd_soc_dapm_new_controls(snd_soc_component_to_dapm(component),
+ tac_uaj_widgets,
+ ARRAY_SIZE(tac_uaj_widgets));
+ if (ret) {
+ dev_err(component->dev, "Failed to add UAJ widgets: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component),
+ tac_uaj_routes, ARRAY_SIZE(tac_uaj_routes));
+ if (ret) {
+ dev_err(component->dev, "Failed to add UAJ routes: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_add_component_controls(component, tac_uaj_controls,
+ ARRAY_SIZE(tac_uaj_controls));
+ if (ret) {
+ dev_err(component->dev, "Failed to add UAJ controls: %d\n", ret);
+ return ret;
+ }
+
+done_comp_probe:
+ tac_dev->component = component;
+ return 0;
+}
+
+static void tac_component_remove(struct snd_soc_component *codec)
+{
+ struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(codec);
+
+ tac_dev->component = NULL;
+}
+
+static const struct snd_soc_component_driver soc_codec_driver_tacdevice = {
+ .probe = tac_component_probe,
+ .remove = tac_component_remove,
+ .controls = tac5xx2_snd_controls,
+ .num_controls = ARRAY_SIZE(tac5xx2_snd_controls),
+ .dapm_widgets = tac5xx2_common_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tac5xx2_common_widgets),
+ .dapm_routes = tac5xx2_common_routes,
+ .num_dapm_routes = ARRAY_SIZE(tac5xx2_common_routes),
+ .idle_bias_on = 0,
+ .endianness = 1,
+};
+
+static s32 tac_init(struct tac5xx2_prv *tac_dev)
+{
+ struct snd_soc_component_driver *component_driver;
+ struct snd_soc_dai_driver *dai_drv;
+ int num_dais;
+ s32 ret;
+
+ dev_set_drvdata(tac_dev->dev, tac_dev);
+
+ switch (tac_dev->part_id) {
+ case 0x5572:
+ dai_drv = tac5572_dai_driver;
+ num_dais = ARRAY_SIZE(tac5572_dai_driver);
+ break;
+ case 0x5672:
+ dai_drv = tac5672_dai_driver;
+ num_dais = ARRAY_SIZE(tac5672_dai_driver);
+ break;
+ case 0x5682:
+ dai_drv = tac5682_dai_driver;
+ num_dais = ARRAY_SIZE(tac5682_dai_driver);
+ break;
+ case 0x2883:
+ dai_drv = tas2883_dai_driver;
+ num_dais = ARRAY_SIZE(tas2883_dai_driver);
+ break;
+ default:
+ dev_err(tac_dev->dev, "Unsupported device: 0x%x\n",
+ tac_dev->part_id);
+ return -EINVAL;
+ }
+
+ component_driver = devm_kzalloc(tac_dev->dev, sizeof(*component_driver),
+ GFP_KERNEL);
+ if (!component_driver)
+ return -ENOMEM;
+
+ memcpy(component_driver, &soc_codec_driver_tacdevice, sizeof(*component_driver));
+ if (tac_has_uaj_support(tac_dev))
+ component_driver->set_jack = tac5xx2_set_jack;
+
+ ret = devm_snd_soc_register_component(tac_dev->dev, component_driver,
+ dai_drv, num_dais);
+ if (ret) {
+ dev_err(tac_dev->dev, "%s: codec register error:%d.\n",
+ __func__, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static s32 tac5xx2_sdca_dev_suspend(struct device *dev)
+{
+ struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev);
+
+ if (!tac_dev->hw_init)
+ return 0;
+
+ regcache_cache_only(tac_dev->regmap, true);
+ return 0;
+}
+
+static s32 tac5xx2_sdca_dev_system_suspend(struct device *dev)
+{
+ return tac5xx2_sdca_dev_suspend(dev);
+}
+
+static s32 tac5xx2_sdca_dev_resume(struct device *dev)
+{
+ struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev);
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ int ret;
+
+ if (!tac_dev->first_hw_init_done) {
+ dev_dbg(dev, "Device not initialized yet, skipping resume sync\n");
+ return 0;
+ }
+
+ ret = sdw_slave_wait_for_init(slave, TAC5XX2_PROBE_TIMEOUT_MS);
+ if (ret) {
+ sdw_show_ping_status(slave->bus, true);
+ return ret;
+ }
+
+ regcache_cache_only(tac_dev->regmap, false);
+ regcache_mark_dirty(tac_dev->regmap);
+ ret = regcache_sync(tac_dev->regmap);
+ if (ret < 0)
+ dev_warn(dev, "Failed to sync regcache: %d\n", ret);
+
+ /* Detect and set jack type for UAJ path before playback.
+ * This is required as jack detection does not trigger interrupt
+ * when device is in runtime_pm suspend with bus in clock stop mode.
+ */
+ if (tac_has_uaj_support(tac_dev))
+ tac5xx2_sdca_headset_detect(tac_dev);
+
+ return 0;
+}
+
+static const struct dev_pm_ops tac5xx2_sdca_pm = {
+ SYSTEM_SLEEP_PM_OPS(tac5xx2_sdca_dev_system_suspend, tac5xx2_sdca_dev_resume)
+ RUNTIME_PM_OPS(tac5xx2_sdca_dev_suspend, tac5xx2_sdca_dev_resume, NULL)
+};
+
+static s32 tac_fw_read_hdr(const u8 *data, struct tac_fw_hdr *hdr)
+{
+ hdr->size = get_unaligned_le32(data);
+ hdr->version_offset = get_unaligned_le32(data + 4);
+ hdr->plt_id = get_unaligned_le32(data + 8);
+ hdr->ppc3_ver = get_unaligned_le32(data + 12);
+ memcpy(hdr->ddc_name, data + 16, 64);
+ hdr->ddc_name[63] = 0;
+ hdr->timestamp = get_unaligned_le64(data + 80);
+
+ return TAC_FW_HDR_SIZE;
+}
+
+static s32 tac_fw_get_next_file(const u8 *data, size_t data_size, struct tac_fw_file *file)
+{
+ u32 file_length;
+
+ /* Validate file header size */
+ if (data_size < TAC_FW_FILE_HDR)
+ return -EINVAL;
+
+ file->vendor_id = get_unaligned_le32(&data[0]);
+ file->file_id = get_unaligned_le32(&data[4]);
+ file->version = get_unaligned_le32(&data[8]);
+ file->length = get_unaligned_le32(&data[12]);
+ file->dest_addr = get_unaligned_le32(&data[16]);
+ file_length = file->length;
+
+ /* Validate file payload exists */
+ if (data_size < TAC_FW_FILE_HDR + file_length)
+ return -EINVAL;
+
+ file->fw_data = (u8 *)&data[20];
+
+ return file_length + sizeof(u32) * 5;
+}
+
+static void tac5xx2_fw_ready(const struct firmware *fmw, void *context)
+{
+ struct tac5xx2_prv *tac_dev = context;
+ struct tac_fw_file *files;
+ u32 fw_hdr_size;
+ u32 num_files = 0;
+ struct tac_fw_hdr hdr;
+ struct tm tm_time;
+ size_t img_sz;
+ u32 offset;
+ s32 ret = 0;
+ u8 *buf;
+
+ if (!fmw || !fmw->data || fmw->size == 0 || fmw->size < TAC_FW_HDR_SIZE + TAC_FW_FILE_HDR) {
+ dev_err(tac_dev->dev, "fw file: %s is empty or invalid\n",
+ tac_dev->fw_binaryname);
+ goto out;
+ }
+
+ /* Verify firmware size from header */
+ fw_hdr_size = get_unaligned_le32(fmw->data);
+ if (fw_hdr_size != fmw->size) {
+ dev_err(tac_dev->dev, "firmware size mismatch: hdr=%u, actual=%zu\n",
+ fw_hdr_size, fmw->size);
+ goto out;
+ }
+
+ files = devm_kzalloc(tac_dev->dev, sizeof(*files) * TAC_MAX_FW_CHUNKS, GFP_KERNEL);
+ buf = devm_kmemdup(tac_dev->dev, fmw->data, fmw->size, GFP_KERNEL);
+ if (!files || !buf)
+ goto out;
+
+ /* validate the cache the firmware */
+ img_sz = fmw->size;
+ offset = tac_fw_read_hdr(buf, &hdr);
+ while (offset < img_sz && num_files < TAC_MAX_FW_CHUNKS) {
+ u32 file_length;
+
+ if (offset + TAC_FW_FILE_HDR > img_sz) {
+ dev_warn(tac_dev->dev, "Incomplete block header at offset %d\n",
+ offset);
+ goto out;
+ }
+ /* Validate that the file payload doesn't exceed buffer */
+ file_length = get_unaligned_le32(&buf[offset + 12]);
+ /* Check for integer overflow and buffer bounds */
+ if (file_length > img_sz || offset > img_sz - TAC_FW_FILE_HDR ||
+ file_length > img_sz - offset - TAC_FW_FILE_HDR) {
+ dev_warn(tac_dev->dev, "File at offset %d exceeds buffer: length=%u, available=%zu\n",
+ offset, file_length, img_sz - offset - TAC_FW_FILE_HDR);
+ goto out;
+ }
+ ret = tac_fw_get_next_file(&buf[offset], img_sz - offset, &files[num_files]);
+ if (ret < 0) {
+ dev_err(tac_dev->dev, "Failed to parse file at offset %d\n", offset);
+ goto out;
+ }
+ offset += ret;
+ num_files++;
+ }
+
+ if (num_files == 0) {
+ dev_err(tac_dev->dev, "firmware with no files\n");
+ goto out;
+ }
+
+ /* cache ready to use validated firmware */
+ tac_dev->fw_file_cnt = num_files;
+ tac_dev->fw_files = files;
+
+ time64_to_tm(hdr.timestamp, 0, &tm_time);
+ dev_dbg(tac_dev->dev, "fw file: %s, num_files=%u, ts:%04ld-%02d-%02d %02d:%02d\n",
+ tac_dev->fw_binaryname, tac_dev->fw_file_cnt,
+ tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday,
+ tm_time.tm_hour, tm_time.tm_min);
+ dev_dbg(tac_dev->dev, "fw file: DDC Name: %s\n", hdr.ddc_name);
+ dev_dbg(tac_dev->dev, "fw file: PPC3 Version: 3.%ld.%ld.%ld\n",
+ FIELD_GET(GENMASK(31, 24), hdr.ppc3_ver),
+ FIELD_GET(GENMASK(23, 16), hdr.ppc3_ver),
+ FIELD_GET(GENMASK(15, 8), hdr.ppc3_ver) & 0x3f);
+
+out:
+ complete_all(&tac_dev->fw_caching_complete);
+ if (fmw)
+ release_firmware(fmw);
+}
+
+static int tac_load_and_cache_firmware_async(struct tac5xx2_prv *tac_dev)
+{
+ tac_dev->fw_file_cnt = 0;
+ tac_dev->fw_files = NULL; /* ready to download files */
+
+ return request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
+ tac_dev->fw_binaryname, tac_dev->dev,
+ GFP_KERNEL, tac_dev, tac5xx2_fw_ready);
+}
+
+static int tac_download(struct tac5xx2_prv *tac_dev)
+{
+ struct tac_fw_file *files = tac_dev->fw_files;
+ u32 num_files = tac_dev->fw_file_cnt;
+ u32 i;
+ int ret = 0;
+
+ for (i = 0; i < num_files; i++) {
+ ret = sdw_nwrite_no_pm(tac_dev->sdw_peripheral, files[i].dest_addr,
+ files[i].length, files[i].fw_data);
+ if (ret < 0) {
+ dev_dbg(tac_dev->dev,
+ "FW write failed at addr 0x%x: %d\n",
+ files[i].dest_addr, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * tac5xx2 uses custom firmware binary fw.
+ * This is not using UMP File Download.
+ */
+static s32 tac_download_fw_to_hw(struct tac5xx2_prv *tac_dev)
+{
+ int ret;
+
+ ret = tac_download(tac_dev);
+ if (ret < 0) {
+ dev_err(tac_dev->dev, "Firmware download failed: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(tac_dev->dev, "Firmware download complete: %d chunks\n",
+ tac_dev->fw_file_cnt);
+ tac_dev->fw_dl_success = true;
+
+ return 0;
+}
+
+static struct pci_dev *tac_get_pci_dev(struct sdw_slave *peripheral)
+{
+ struct device *dev = &peripheral->dev;
+
+ for (; dev; dev = dev->parent) {
+ if (dev_is_pci(dev))
+ return to_pci_dev(dev);
+ }
+
+ return NULL;
+}
+
+static void tac_generate_fw_name(struct sdw_slave *slave, char *name, size_t size)
+{
+ struct sdw_bus *bus = slave->bus;
+ u16 part_id = slave->id.part_id;
+ u8 unique_id = slave->id.unique_id;
+ struct pci_dev *pci = tac_get_pci_dev(slave);
+
+ if (pci)
+ scnprintf(name, size, "%04X-%04X-%1X-%1X.bin", part_id,
+ pci->subsystem_device, bus->link_id, unique_id);
+ else
+ /* Default firmware name based on part ID */
+ scnprintf(name, size, "%s%04x-%1X-%1X.bin",
+ part_id == 0x2883 ? "tas" : "tac",
+ part_id, bus->link_id, unique_id);
+}
+
+static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first)
+{
+ struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev);
+ u64 time;
+ int ret;
+
+ if (tac_dev->hw_init) {
+ dev_dbg(dev, "early return hw_init already done..");
+ return 0;
+ }
+
+ time = wait_for_completion_timeout(&tac_dev->fw_caching_complete,
+ msecs_to_jiffies(TAC5XX2_FW_CACHE_TIMEOUT_MS));
+ if (!time) {
+ ret = -ETIMEDOUT;
+ dev_warn(tac_dev->dev, "%s: fw caching timeout\n", __func__);
+ goto io_init_err;
+ }
+
+ if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) {
+ ret = tac_download_fw_to_hw(tac_dev);
+ if (ret) {
+ dev_err(tac_dev->dev, "FW download failed, fw: %d\n", ret);
+ goto io_init_err;
+ }
+ }
+
+ if (tac_dev->sa_func_data) {
+ ret = sdca_regmap_write_init(dev, tac_dev->regmap,
+ tac_dev->sa_func_data);
+ if (ret) {
+ dev_err(dev, "smartamp init table update failed\n");
+ goto io_init_err;
+ }
+ dev_dbg(dev, "smartamp init done\n");
+
+ if (first) {
+ ret = regmap_multi_reg_write(tac_dev->regmap, tac_spk_seq,
+ ARRAY_SIZE(tac_spk_seq));
+ if (ret) {
+ dev_err(dev, "init writes failed, err=%d", ret);
+ goto io_init_err;
+ }
+ }
+ }
+
+ if (tac_dev->sm_func_data) {
+ ret = sdca_regmap_write_init(dev, tac_dev->regmap,
+ tac_dev->sm_func_data);
+ if (ret) {
+ dev_err(dev, "smartmic init table update failed\n");
+ goto io_init_err;
+ }
+ dev_dbg(dev, "smartmic init done\n");
+
+ if (first) {
+ ret = regmap_multi_reg_write(tac_dev->regmap, tac_sm_seq,
+ ARRAY_SIZE(tac_sm_seq));
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "init writes failed, err=%d", ret);
+ goto io_init_err;
+ }
+ }
+ }
+
+ if (tac_dev->uaj_func_data) {
+ ret = sdca_regmap_write_init(dev, tac_dev->regmap,
+ tac_dev->uaj_func_data);
+ if (ret) {
+ dev_err(dev, "uaj init table update failed\n");
+ goto io_init_err;
+ }
+ dev_dbg(dev, "uaj init done\n");
+
+ if (first) {
+ ret = regmap_multi_reg_write(tac_dev->regmap, tac_uaj_seq,
+ ARRAY_SIZE(tac_uaj_seq));
+ if (ret) {
+ dev_err(tac_dev->dev,
+ "init writes failed, err=%d", ret);
+ goto io_init_err;
+ }
+
+ if (tac_dev->hs_jack) {
+ ret = tac5xx2_jack_init(tac_dev);
+ if (ret) {
+ dev_err(tac_dev->dev, "jack init failed");
+ goto io_init_err;
+ }
+ }
+ }
+ }
+
+ if (tac_dev->hid_func_data) {
+ ret = sdca_regmap_write_init(dev, tac_dev->regmap,
+ tac_dev->hid_func_data);
+ if (ret) {
+ dev_err(dev, "hid init table update failed\n");
+ goto io_init_err;
+ }
+ dev_dbg(dev, "hid init done\n");
+ }
+
+ tac_dev->hw_init = true;
+
+ return 0;
+
+io_init_err:
+ dev_err(dev, "init writes failed, err=%d", ret);
+ return ret;
+}
+
+static int tac_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev);
+ struct device *dev = &slave->dev;
+ bool first = false;
+ int ret;
+
+ tac_dev->status = status;
+ if (status == SDW_SLAVE_UNATTACHED) {
+ tac_dev->hw_init = false;
+ tac_dev->fw_dl_success = false;
+ }
+
+ if (tac_dev->hw_init || tac_dev->status != SDW_SLAVE_ATTACHED) {
+ dev_dbg(dev, "%s: early return, hw_init=%d, status=%d",
+ __func__, tac_dev->hw_init, tac_dev->status);
+ return 0;
+ }
+
+ if (!tac_dev->first_hw_init_done) {
+ pm_runtime_set_active(tac_dev->dev);
+ tac_dev->first_hw_init_done = true;
+ first = true;
+ }
+
+ pm_runtime_get_noresume(tac_dev->dev);
+
+ regcache_mark_dirty(tac_dev->regmap);
+ regcache_cache_only(tac_dev->regmap, false);
+ ret = tac_io_init(&slave->dev, slave, first);
+ if (ret) {
+ dev_err(dev, "Device initialization failed: %d\n", ret);
+ goto err_out;
+ }
+
+ ret = regcache_sync(tac_dev->regmap);
+ if (ret)
+ dev_warn(dev, "Failed to sync regcache after init: %d\n", ret);
+
+err_out:
+ pm_runtime_mark_last_busy(tac_dev->dev);
+ pm_runtime_put_autosuspend(tac_dev->dev);
+
+ return ret;
+}
+
+static int tac5xx2_sdw_read_prop(struct sdw_slave *peripheral)
+{
+ struct device *dev = &peripheral->dev;
+ int ret;
+
+ ret = sdw_slave_read_prop(peripheral);
+ if (ret) {
+ dev_err(dev, "sdw_slave_read_prop failed: %d", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int tac_port_prep(struct sdw_slave *slave, struct sdw_prepare_ch *prep_ch,
+ enum sdw_port_prep_ops pre_ops)
+{
+ struct device *dev = &slave->dev;
+ struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (pre_ops != SDW_OPS_PORT_POST_PREP)
+ return 0;
+
+ if (!tac_dev->fw_dl_success)
+ return 0;
+
+ ret = regmap_read(tac_dev->regmap, TAC_DSP_ALGO_STATUS, &val);
+ if (ret) {
+ dev_err(dev, "Failed to read algo status: %d\n", ret);
+ return ret;
+ }
+
+ if (val != TAC_DSP_ALGO_STATUS_RUNNING) {
+ dev_dbg(dev, "Algo not running (0x%02x), re-enabling\n", val);
+ ret = regmap_write(tac_dev->regmap, TAC_DSP_ALGO_STATUS,
+ TAC_DSP_ALGO_STATUS_RUNNING);
+ if (ret) {
+ dev_err(dev, "Failed to re-enable algo: %d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct sdw_slave_ops tac_sdw_ops = {
+ .read_prop = tac5xx2_sdw_read_prop,
+ .update_status = tac_update_status,
+ .interrupt_callback = tac_interrupt_callback,
+ .port_prep = tac_port_prep,
+};
+
+static s32 tac_sdw_probe(struct sdw_slave *peripheral,
+ const struct sdw_device_id *id)
+{
+ struct sdca_function_data *function_data = NULL;
+ struct device *dev = &peripheral->dev;
+ struct tac5xx2_prv *tac_dev;
+ struct regmap *regmap;
+ int ret, i;
+
+ tac_dev = devm_kzalloc(dev, sizeof(*tac_dev), GFP_KERNEL);
+ if (!tac_dev)
+ return dev_err_probe(dev, -ENOMEM,
+ "Failed devm_kzalloc");
+
+ if (peripheral->sdca_data.num_functions > 0) {
+ dev_dbg(dev, "SDCA functions found: %d",
+ peripheral->sdca_data.num_functions);
+
+ for (i = 0; i < peripheral->sdca_data.num_functions; i++) {
+ struct sdca_function_data **func_ptr;
+ const char *func_name;
+
+ switch (peripheral->sdca_data.function[i].type) {
+ case SDCA_FUNCTION_TYPE_SMART_AMP:
+ func_ptr = &tac_dev->sa_func_data;
+ func_name = "smartamp";
+ break;
+ case SDCA_FUNCTION_TYPE_SMART_MIC:
+ func_ptr = &tac_dev->sm_func_data;
+ func_name = "smartmic";
+ break;
+ case SDCA_FUNCTION_TYPE_UAJ:
+ func_ptr = &tac_dev->uaj_func_data;
+ func_name = "uaj";
+ break;
+ case SDCA_FUNCTION_TYPE_HID:
+ func_ptr = &tac_dev->hid_func_data;
+ func_name = "hid";
+ break;
+ default:
+ continue;
+ }
+
+ function_data = devm_kzalloc(dev, sizeof(*function_data),
+ GFP_KERNEL);
+ if (!function_data)
+ return dev_err_probe(dev, -ENOMEM,
+ "failed to allocate %s function data",
+ func_name);
+ function_data->desc = &peripheral->sdca_data.function[i];
+ ret = sdca_parse_function(dev, peripheral, function_data);
+ if (!ret)
+ *func_ptr = function_data;
+ else
+ devm_kfree(dev, function_data);
+ }
+ }
+
+ dev_dbg(dev, "SDCA functions enabled: SA=%s SM=%s UAJ=%s HID=%s",
+ tac_dev->sa_func_data ? "yes" : "no",
+ tac_dev->sm_func_data ? "yes" : "no",
+ tac_dev->uaj_func_data ? "yes" : "no",
+ tac_dev->hid_func_data ? "yes" : "no");
+
+ tac_dev->dev = dev;
+ tac_dev->sdw_peripheral = peripheral;
+ tac_dev->hw_init = false;
+ tac_dev->first_hw_init_done = false;
+ tac_dev->part_id = id->part_id;
+ dev_set_drvdata(dev, tac_dev);
+
+ regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral,
+ &tac_regmap, &tac_mbq_cfg);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed devm_regmap_init_sdw\n");
+
+ regcache_cache_only(regmap, true);
+ tac_dev->regmap = regmap;
+ tac_dev->jack_type = 0;
+ init_completion(&tac_dev->fw_caching_complete);
+
+ if (tac_has_dsp_algo(tac_dev)) {
+ tac_generate_fw_name(peripheral, tac_dev->fw_binaryname,
+ sizeof(tac_dev->fw_binaryname));
+
+ ret = tac_load_and_cache_firmware_async(tac_dev);
+ if (ret) {
+ complete_all(&tac_dev->fw_caching_complete);
+ dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret);
+ }
+ } else {
+ complete_all(&tac_dev->fw_caching_complete);
+ }
+
+ ret = tac_init(tac_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "failed to initialize tac device\n");
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+ /* the device is still not in active */
+
+ return 0;
+}
+
+static void tac_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct tac5xx2_prv *tac_dev = dev_get_drvdata(&peripheral->dev);
+
+ pm_runtime_disable(tac_dev->dev);
+
+ dev_set_drvdata(&peripheral->dev, NULL);
+}
+
+static const struct sdw_device_id tac_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x0102, 0x5572, 0),
+ SDW_SLAVE_ENTRY(0x0102, 0x5672, 0),
+ SDW_SLAVE_ENTRY(0x0102, 0x5682, 0),
+ SDW_SLAVE_ENTRY(0x0102, 0x2883, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, tac_sdw_id);
+
+static struct sdw_driver tac_sdw_driver = {
+ .driver = {
+ .name = "slave-tac5xx2",
+ .pm = pm_ptr(&tac5xx2_sdca_pm),
+ },
+ .probe = tac_sdw_probe,
+ .remove = tac_sdw_remove,
+ .ops = &tac_sdw_ops,
+ .id_table = tac_sdw_id,
+};
+module_sdw_driver(tac_sdw_driver);
+
+MODULE_IMPORT_NS("SND_SOC_SDCA");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("ASoC TAC5XX2 SoundWire Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tac5xx2.h b/sound/soc/codecs/tac5xx2.h
new file mode 100644
index 000000000000..eed8e6cf3498
--- /dev/null
+++ b/sound/soc/codecs/tac5xx2.h
@@ -0,0 +1,259 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * ALSA SoC Texas Instruments TAC5XX2 Audio Smart Amplifier
+ *
+ * Copyright (C) 2025 Texas Instruments Incorporated
+ * https://www.ti.com
+ *
+ * This the header file for TAC5XX2 family of devices
+ * which includes TAC5572, TAC5672, TAC5682 and TAS2883
+ *
+ * Author: Niranjan H Y <niranjanhy@ti.com>
+ */
+#ifndef __RGL_TAC5XX2_H__
+#define __RGL_TAC5XX2_H__
+
+/* for soundwire */
+#define TAC_REG_SDW(book, page, reg) (((book) * 256 * 128) + \
+ 0x3000000 + ((page) * 128) + (reg))
+
+/* page 0 registers */
+#define TAC_SW_RESET TAC_REG_SDW(0, 0, 1)
+#define TAC_SLEEP_MODEZ TAC_REG_SDW(0, 0, 2)
+#define TAC_FEATURE_PDZ TAC_REG_SDW(0, 0, 3)
+#define TAC_TX_CH_EN TAC_REG_SDW(0, 0, 4)
+#define TAC_RX_CH_PD TAC_REG_SDW(0, 0, 5)
+#define TAC_SHDNZ_CFG TAC_REG_SDW(0, 0, 6)
+#define TAC_MISC_CFG0 TAC_REG_SDW(0, 0, 7)
+#define TAC_MISC_CFG1 TAC_REG_SDW(0, 0, 8)
+#define TAC_GPIO1_CFG0 TAC_REG_SDW(0, 0, 9)
+#define TAC_GPIO2_CFG0 TAC_REG_SDW(0, 0, 10)
+#define TAC_GPIO3_CFG0 TAC_REG_SDW(0, 0, 11)
+#define TAC_GPIO4_CFG0 TAC_REG_SDW(0, 0, 12)
+#define TAC_GPIO5_CFG0 TAC_REG_SDW(0, 0, 13)
+#define TAC_GPIO6_CFG0 TAC_REG_SDW(0, 0, 14)
+#define TAC_INTF_CFG1 TAC_REG_SDW(0, 0, 15)
+#define TAC_INTF_CFG5 TAC_REG_SDW(0, 0, 16)
+#define TAC_PASI_BCLK_CFG0 TAC_REG_SDW(0, 0, 17)
+#define TAC_PASI_FSYNC_CFG0 TAC_REG_SDW(0, 0, 18)
+#define TAC_PASI_DIN1_CFG0 TAC_REG_SDW(0, 0, 19)
+#define TAC_PASI_DIN2_CFG0 TAC_REG_SDW(0, 0, 20)
+#define TAC_PDM_DIN1_CFG0 TAC_REG_SDW(0, 0, 21)
+#define TAC_PDM_DIN2_CFG0 TAC_REG_SDW(0, 0, 22)
+#define TAC_MCLK_SEL TAC_REG_SDW(0, 0, 23)
+#define TAC_I2C2_CFG0 TAC_REG_SDW(0, 0, 24)
+#define TAC_SDW_IO_CFG0 TAC_REG_SDW(0, 0, 25)
+#define TAC_SDW_CLK_CFG0 TAC_REG_SDW(0, 0, 26)
+#define TAC_PASI_CFG0 TAC_REG_SDW(0, 0, 27)
+#define TAC_PASI_CFG1 TAC_REG_SDW(0, 0, 28)
+#define TAC_PASI_TX_CFG0 TAC_REG_SDW(0, 0, 29)
+#define TAC_PASI_TX_CFG1 TAC_REG_SDW(0, 0, 30)
+#define TAC_PASI_TX_CFG2 TAC_REG_SDW(0, 0, 31)
+#define TAC_PASI_TX_CFG3 TAC_REG_SDW(0, 0, 32)
+#define TAC_PASI_TX_CH1_CFG0 TAC_REG_SDW(0, 0, 33)
+#define TAC_PASI_TX_CH2_CFG0 TAC_REG_SDW(0, 0, 34)
+#define TAC_PASI_TX_CH3_CFG0 TAC_REG_SDW(0, 0, 35)
+#define TAC_PASI_TX_CH4_CFG0 TAC_REG_SDW(0, 0, 36)
+#define TAC_PASI_TX_CH5_CFG0 TAC_REG_SDW(0, 0, 37)
+#define TAC_PASI_TX_CH6_CFG0 TAC_REG_SDW(0, 0, 38)
+#define TAC_PASI_TX_CH7_CFG0 TAC_REG_SDW(0, 0, 39)
+#define TAC_PASI_TX_CH8_CFG0 TAC_REG_SDW(0, 0, 40)
+#define TAC_PASI_RX_CFG0 TAC_REG_SDW(0, 0, 41)
+#define TAC_PASI_RX_CFG1 TAC_REG_SDW(0, 0, 42)
+#define TAC_PASI_RX_CFG2 TAC_REG_SDW(0, 0, 43)
+#define TAC_PASI_RX_CH1_CFG0 TAC_REG_SDW(0, 0, 44)
+#define TAC_PASI_RX_CH2_CFG0 TAC_REG_SDW(0, 0, 45)
+#define TAC_PASI_RX_CH3_CFG0 TAC_REG_SDW(0, 0, 46)
+#define TAC_PASI_RX_CH4_CFG0 TAC_REG_SDW(0, 0, 47)
+#define TAC_PASI_RX_CH5_CFG0 TAC_REG_SDW(0, 0, 48)
+#define TAC_PASI_RX_CH6_CFG0 TAC_REG_SDW(0, 0, 49)
+#define TAC_PASI_RX_CH7_CFG0 TAC_REG_SDW(0, 0, 50)
+#define TAC_PASI_RX_CH8_CFG0 TAC_REG_SDW(0, 0, 51)
+#define TAC_ADC_CH1_CFG0 TAC_REG_SDW(0, 0, 52)
+#define TAC_ADC_DVOL_CFG0 TAC_REG_SDW(0, 0, 53)
+#define TAC_ADC_CH1_FGAIN TAC_REG_SDW(0, 0, 54)
+#define TAC_ADC_CH1_CFG1 TAC_REG_SDW(0, 0, 55)
+#define TAC_ADC_CH2_CFG0 TAC_REG_SDW(0, 0, 57)
+#define TAC_ADC_DVOL_CFG1 TAC_REG_SDW(0, 0, 58)
+#define TAC_ADC_CH2_FGAIN TAC_REG_SDW(0, 0, 59)
+#define TAC_ADC_CH2_CFG1 TAC_REG_SDW(0, 0, 60)
+#define TAC_ADC_CFG1 TAC_REG_SDW(0, 0, 62)
+#define TAC_PDM_CH1_DVOL TAC_REG_SDW(0, 0, 63)
+#define TAC_PDM_CH1_FGAIN TAC_REG_SDW(0, 0, 64)
+#define TAC_PDM_CH1_CFG0 TAC_REG_SDW(0, 0, 65)
+#define TAC_PDM_CH2_DVOL TAC_REG_SDW(0, 0, 67)
+#define TAC_PDM_CH2_FGAIN TAC_REG_SDW(0, 0, 68)
+#define TAC_PDM_CH2_CFG2 TAC_REG_SDW(0, 0, 69)
+#define TAC_PDM_CH3_DVOL TAC_REG_SDW(0, 0, 71)
+#define TAC_PDM_CH3_FGAIN TAC_REG_SDW(0, 0, 72)
+#define TAC_PDM_CH3_CFG0 TAC_REG_SDW(0, 0, 73)
+#define TAC_PDM_CH4_DVOL TAC_REG_SDW(0, 0, 75)
+#define TAC_PDM_CH4_FGAIN TAC_REG_SDW(0, 0, 76)
+#define TAC_PDM_CH4_CFG0 TAC_REG_SDW(0, 0, 77)
+#define TAC_MICBIAS_CFG0 TAC_REG_SDW(0, 0, 79)
+#define TAC_MICPREAMP_CFG TAC_REG_SDW(0, 0, 80)
+#define TAC_MICBIAS_CFG1 TAC_REG_SDW(0, 0, 81)
+#define TAC_CLASSD_CH1_DVOL TAC_REG_SDW(0, 0, 82)
+#define TAC_CLASSD_CH1_FGAIN TAC_REG_SDW(0, 0, 83)
+#define TAC_CLASSD_CH2_DVOL TAC_REG_SDW(0, 0, 85)
+#define TAC_CLASSD_CH2_FGAIN TAC_REG_SDW(0, 0, 86)
+#define TAC_GCHP_CH1_DVOL TAC_REG_SDW(0, 0, 88)
+#define TAC_GCHP_CH1_FGAIN TAC_REG_SDW(0, 0, 89)
+#define TAC_GCHP_CH2_DVOL TAC_REG_SDW(0, 0, 91)
+#define TAC_GCHP_CH2_FGAIN TAC_REG_SDW(0, 0, 92)
+#define TAC_AMP_LVL_CFG0 TAC_REG_SDW(0, 0, 94)
+#define TAC_AMP_LVL_CFG1 TAC_REG_SDW(0, 0, 95)
+#define TAC_AMP_LVL_CFG2 TAC_REG_SDW(0, 0, 96)
+#define TAC_AMP_LVL_CFG3 TAC_REG_SDW(0, 0, 97)
+#define TAC_EFF_MODE_CFG0 TAC_REG_SDW(0, 0, 98)
+#define TAC_EFF_MODE_CFG1 TAC_REG_SDW(0, 0, 99)
+#define TAC_CLASSD_CFG0 TAC_REG_SDW(0, 0, 100)
+#define TAC_CLASSD_CFG1 TAC_REG_SDW(0, 0, 101)
+#define TAC_CLASSD_CFG3 TAC_REG_SDW(0, 0, 102)
+#define TAC_CLASSD_CFG4 TAC_REG_SDW(0, 0, 103)
+#define TAC_CLASSD_CFG5 TAC_REG_SDW(0, 0, 104)
+#define TAC_CLASSD_CFG6 TAC_REG_SDW(0, 0, 105)
+#define TAC_CLASSD_CFG8 TAC_REG_SDW(0, 0, 106)
+#define TAC_ISNS_CFG TAC_REG_SDW(0, 0, 107)
+#define TAC_DSP_CFG0 TAC_REG_SDW(0, 0, 108)
+#define TAC_DSP_CFG1 TAC_REG_SDW(0, 0, 109)
+#define TAC_DSP_CFG2 TAC_REG_SDW(0, 0, 110)
+#define TAC_DSP_CFG3 TAC_REG_SDW(0, 0, 111)
+#define TAC_JACK_DET_CFG1 TAC_REG_SDW(0, 0, 112)
+#define TAC_JACK_DET_CFG2 TAC_REG_SDW(0, 0, 113)
+#define TAC_JACK_DET_CFG3 TAC_REG_SDW(0, 0, 114)
+#define TAC_JACK_DET_CFG4 TAC_REG_SDW(0, 0, 115)
+#define TAC_JACK_DET_CFG7 TAC_REG_SDW(0, 0, 116)
+#define TAC_UJ_IMPEDANCE_L TAC_REG_SDW(0, 0, 117)
+#define TAC_UJ_IMPEDANCE_R TAC_REG_SDW(0, 0, 118)
+#define UJ_IMPEDANCE_L TAC_REG_SDW(0, 0, 119)
+#define UJ_IMPEDANCE_R TAC_REG_SDW(0, 0, 120)
+#define TAC_GP_ANA_STS TAC_REG_SDW(0, 0, 123)
+#define TAC_DEV_ID TAC_REG_SDW(0, 0, 124)
+#define TAC_REV_ID TAC_REG_SDW(0, 0, 125)
+#define TAC_I2C_CKSUM TAC_REG_SDW(0, 0, 126)
+#define TAC_BOOK TAC_REG_SDW(0, 0, 127)
+
+#define TAC_INT_CFG TAC_REG_SDW(0, 2, 1)
+#define TAC_INT_CFG_CLR_REG BIT(3)
+
+/* smartamp function */
+#define TAC_FUNCTION_ID_SA 0x1
+
+#define TAC_SDCA_ENT_ENT0 0x0
+#define TAC_SDCA_ENT_PPU21 0x1
+#define TAC_SDCA_ENT_FU21 0x2
+#define TAC_SDCA_ENT_FU26 0x3
+#define TAC_SDCA_ENT_XU22 0x4
+#define TAC_SDCA_ENT_CS24 0x5
+#define TAC_SDCA_ENT_CS21 0x6
+#define TAC_SDCA_ENT_CS25 0x7
+#define TAC_SDCA_ENT_CS26 0x8
+#define TAC_SDCA_ENT_CS28 0x9
+#define TAC_SDCA_ENT_PPU26 0xa
+#define TAC_SDCA_ENT_FU23 0xb
+#define TAC_SDCA_ENT_PDE23 0xc
+#define TAC_SDCA_ENT_TG23 0x12
+#define TAC_SDCA_ENT_IT21 0x13
+#define TAC_SDCA_ENT_IT29 0x14
+#define TAC_SDCA_ENT_IT26 0x15
+#define TAC_SDCA_ENT_IT28 0x16
+#define TAC_SDCA_ENT_OT24 0x17
+#define TAC_SDCA_ENT_OT23 0x18
+#define TAC_SDCA_ENT_OT25 0x19
+#define TAC_SDCA_ENT_OT28 0x1a
+#define TAC_SDCA_ENT_OT27 0x1c
+#define TAC_SDCA_ENT_SPE199 0x21
+#define TAC_SDCA_ENT_OT20 0x24
+#define TAC_SDCA_ENT_FU27 0x26
+#define TAC_SDCA_ENT_FU20 0x27
+#define TAC_SDCA_ENT_PDE24 0x2e
+#define TAC_SDCA_ENT_PDE27 0x2f
+#define TAC_SDCA_ENT_PDE28 0x30
+#define TAC_SDCA_ENT_PDE20 0x31
+#define TAC_SDCA_ENT_SAPU29 0x35
+
+/* Control selector definitions */
+#define TAC_SDCA_MASTER_GAIN 0x0B
+#define TAC_SDCA_MASTER_MUTE 0x01
+#define TAC_SDCA_CHANNEL_MUTE 0x01
+#define TAC_SDCA_CHANNEL_GAIN 0x02
+#define TAC_SDCA_POSTURENUMBER 0x10
+#define TAC_SDCA_REQUESTED_PS 0x01
+#define TAC_SDCA_ACTUAL_PS 0x10
+#define TAC_SDCA_CHANNEL_VOLUME 0x02
+
+/* 2. smart mic function */
+#define TAC_FUNCTION_ID_SM 0x2
+
+#define TAC_SDCA_ENT_IT11 0x1
+#define TAC_SDCA_ENT_OT113 0x2
+#define TAC_SDCA_ENT_CS11 0x3
+#define TAC_SDCA_ENT_CS18 0x4
+#define TAC_SDCA_ENT_FU113 0x5
+#define TAC_SDCA_ENT_FU13 0x6
+#define TAC_SDCA_ENT_FU11 0x8
+#define TAC_SDCA_ENT_XU12 0xa
+#define TAC_SDCA_ENT_CS113 0xc
+#define TAC_SDCA_ENT_CX11 0xf
+#define TAC_SDCA_ENT_PDE11 0x12
+#define TAC_SDCA_ENT_PPU11 0x9
+
+/* controls */
+#define TAC_SDCA_CTL_USAGE 0x04
+#define TAC_SDCA_CTL_IT_CLUSTER 0x10
+#define TAC_SDCA_CTL_OT_DP_SEL 0x11
+#define TAC_SDCA_CTL_XU_BYPASS 0x01
+/* cx */
+#define TAC_SDCA_CTL_CX_CLK_SEL 0x01
+/* cs */
+#define TAC_SDCA_CTL_CS_CLKVLD 0x02
+#define TAC_SDCA_CTL_CS_SAMP_RATE_IDX 0x10
+/* cs113 end */
+/* ppu */
+#define TAC_SDCA_CTL_PPU_POSTURE_NUM 0x10
+
+/* 3. UAJ function */
+#define TAC_FUNCTION_ID_UAJ 0x3
+#define TAC_SDCA_ENT_PDE47 0x35
+#define TAC_SDCA_ENT_PDE34 0x32
+#define TAC_SDCA_ENT_FU41 0x26 /* user */
+#define TAC_SDCA_ENT_IT41 0x07
+#define TAC_SDCA_ENT_XU42 0x2C
+#define TAC_SDCA_ENT_CS41 0x30
+#define TAC_SDCA_ENT_OT45 0x0E
+#define TAC_SDCA_ENT_IT33 0x03
+#define TAC_SDCA_ENT_OT36 0x0A
+#define TAC_SDCA_ENT_FU36 0x28
+#define TAC_SDCA_ENT_CS36 0x2E
+#define TAC_SDCA_ENT_GE35 0x3B /* 59 */
+
+#define TAC_SDCA_CTL_SEL_MODE 0x1
+#define TAC_SDCA_CTL_DET_MODE 0x2
+
+/* 4. HID function */
+#define TAC_FUNCTION_ID_HID 0x4
+#define TAC_SDCA_ENT_HID1 0x1
+/* HID Control Selectors */
+#define TAC_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10
+#define TAC_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12
+#define TAC_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13
+#define TAC_SDCA_CTL_DETECTED_MODE 0x10
+#define TAC_SDCA_CTL_SELECTED_MODE 0x11
+
+#define TAC_BUF_ADDR_HID1 0x44007F80
+
+/* DAI interfaces */
+#define TAC5XX2_SPK 0
+#define TAC5XX2_DMIC 2
+#define TAC5XX2_UAJ 3
+
+/* Port numbers for DAIs */
+#define TAC_SDW_PORT_NUM_SPK_PLAYBACK 1
+#define TAC_SDW_PORT_NUM_SPK_CAPTURE 2
+#define TAC_SDW_PORT_NUM_DMIC 3
+#define TAC_SDW_PORT_NUM_UAJ_PLAYBACK 4
+#define TAC_SDW_PORT_NUM_UAJ_CAPTURE 7
+#define TAC_SDW_PORT_NUM_IV_SENSE 8
+
+#endif
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index 80206c2e0946..1a7650b9b2a7 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -745,7 +745,7 @@ static void tas2552_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id tas2552_id[] = {
- { "tas2552" },
+ { .name = "tas2552" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas2552_id);
diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c
index ceb367ae05ba..e1d62f30418a 100644
--- a/sound/soc/codecs/tas2562.c
+++ b/sound/soc/codecs/tas2562.c
@@ -675,11 +675,12 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
if (tas2562->sdz_gpio == NULL) {
tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down",
GPIOD_OUT_HIGH);
- if (IS_ERR(tas2562->sdz_gpio))
+ if (IS_ERR(tas2562->sdz_gpio)) {
if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER)
return -EPROBE_DEFER;
- tas2562->sdz_gpio = NULL;
+ tas2562->sdz_gpio = NULL;
+ }
}
if (tas2562->model_id == TAS2110)
@@ -711,9 +712,9 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562)
}
static const struct i2c_device_id tas2562_id[] = {
- { "tas2562", TAS2562 },
- { "tas2564", TAS2564 },
- { "tas2110", TAS2110 },
+ { .name = "tas2562", .driver_data = TAS2562 },
+ { .name = "tas2564", .driver_data = TAS2564 },
+ { .name = "tas2110", .driver_data = TAS2110 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas2562_id);
diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c
index 55211266927d..b11b998aa32a 100644
--- a/sound/soc/codecs/tas2764.c
+++ b/sound/soc/codecs/tas2764.c
@@ -1036,7 +1036,7 @@ static int tas2764_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id tas2764_i2c_id[] = {
- { "tas2764"},
+ { .name = "tas2764" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id);
diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c
index dbda9f327535..e79754edac5c 100644
--- a/sound/soc/codecs/tas2770.c
+++ b/sound/soc/codecs/tas2770.c
@@ -958,7 +958,7 @@ static int tas2770_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id tas2770_i2c_id[] = {
- { "tas2770"},
+ { .name = "tas2770" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id);
diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c
index cf3f6abd7e7b..1ec1c076204f 100644
--- a/sound/soc/codecs/tas2780.c
+++ b/sound/soc/codecs/tas2780.c
@@ -621,7 +621,7 @@ static int tas2780_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id tas2780_i2c_id[] = {
- { "tas2780"},
+ { .name = "tas2780" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas2780_i2c_id);
diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c
index 885e0b6fed00..bd16d5326a23 100644
--- a/sound/soc/codecs/tas2781-fmwlib.c
+++ b/sound/soc/codecs/tas2781-fmwlib.c
@@ -921,7 +921,8 @@ static int tasdevice_process_block(void *context, unsigned char *data,
data[subblk_offset + 1],
data[subblk_offset + 2]),
data[subblk_offset + 3]);
- if (rc < 0) {
+ if (rc < 0 &&
+ !(tas_priv->isspi && rc == -EXDEV)) {
is_err = true;
dev_err(tas_priv->dev,
"process_block: single write error\n");
@@ -953,7 +954,7 @@ static int tasdevice_process_block(void *context, unsigned char *data,
data[subblk_offset + 1],
data[subblk_offset + 2]),
&(data[subblk_offset + 4]), len);
- if (rc < 0) {
+ if (rc < 0 && !(tas_priv->isspi && rc == -EXDEV)) {
is_err = true;
dev_err(tas_priv->dev,
"%s: bulk_write error = %d\n",
@@ -991,7 +992,7 @@ static int tasdevice_process_block(void *context, unsigned char *data,
data[subblk_offset + 4]),
data[subblk_offset + 1],
data[subblk_offset + 5]);
- if (rc < 0) {
+ if (rc < 0 && !(tas_priv->isspi && rc == -EXDEV)) {
is_err = true;
dev_err(tas_priv->dev,
"%s: update_bits error = %d\n",
diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c
index a78a8f9b9833..9e6f0ad5f05d 100644
--- a/sound/soc/codecs/tas2781-i2c.c
+++ b/sound/soc/codecs/tas2781-i2c.c
@@ -100,27 +100,28 @@ static const struct bulk_reg_val tas2781_cali_start_reg[] = {
};
static const struct i2c_device_id tasdevice_id[] = {
- { "tas2020", TAS2020 },
- { "tas2118", TAS2118 },
- { "tas2120", TAS2120 },
- { "tas2320", TAS2320 },
- { "tas2563", TAS2563 },
- { "tas2568", TAS2568 },
- { "tas2570", TAS2570 },
- { "tas2572", TAS2572 },
- { "tas2574", TAS2574 },
- { "tas2781", TAS2781 },
- { "tas5802", TAS5802 },
- { "tas5806m", TAS5806M },
- { "tas5806md", TAS5806MD },
- { "tas5815", TAS5815 },
- { "tas5822", TAS5822 },
- { "tas5825", TAS5825 },
- { "tas5827", TAS5827 },
- { "tas5828", TAS5828 },
- { "tas5830", TAS5830 },
- { "tas5832", TAS5832 },
- {}
+ { .name = "tas2020", .driver_data = TAS2020 },
+ { .name = "tas2118", .driver_data = TAS2118 },
+ { .name = "tas2120", .driver_data = TAS2120 },
+ { .name = "tas2320", .driver_data = TAS2320 },
+ { .name = "tas2563", .driver_data = TAS2563 },
+ { .name = "tas2568", .driver_data = TAS2568 },
+ { .name = "tas2570", .driver_data = TAS2570 },
+ { .name = "tas2572", .driver_data = TAS2572 },
+ { .name = "tas2573", .driver_data = TAS2573 },
+ { .name = "tas2574", .driver_data = TAS2574 },
+ { .name = "tas2781", .driver_data = TAS2781 },
+ { .name = "tas5802", .driver_data = TAS5802 },
+ { .name = "tas5806m", .driver_data = TAS5806M },
+ { .name = "tas5806md", .driver_data = TAS5806MD },
+ { .name = "tas5815", .driver_data = TAS5815 },
+ { .name = "tas5822", .driver_data = TAS5822 },
+ { .name = "tas5825", .driver_data = TAS5825 },
+ { .name = "tas5827", .driver_data = TAS5827 },
+ { .name = "tas5828", .driver_data = TAS5828 },
+ { .name = "tas5830", .driver_data = TAS5830 },
+ { .name = "tas5832", .driver_data = TAS5832 },
+ { }
};
static const struct of_device_id tasdevice_of_match[] = {
@@ -132,6 +133,7 @@ static const struct of_device_id tasdevice_of_match[] = {
{ .compatible = "ti,tas2568", .data = &tasdevice_id[TAS2568] },
{ .compatible = "ti,tas2570", .data = &tasdevice_id[TAS2570] },
{ .compatible = "ti,tas2572", .data = &tasdevice_id[TAS2572] },
+ { .compatible = "ti,tas2573", .data = &tasdevice_id[TAS2573] },
{ .compatible = "ti,tas2574", .data = &tasdevice_id[TAS2574] },
{ .compatible = "ti,tas2781", .data = &tasdevice_id[TAS2781] },
{ .compatible = "ti,tas5802", .data = &tasdevice_id[TAS5802] },
@@ -1683,7 +1685,7 @@ 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 < TAS5802) {
+ if (tas_priv->chip_id == TAS2563 || tas_priv->chip_id == TAS2781) {
ret = tasdevice_create_cali_ctrls(tas_priv);
if (ret) {
dev_err(tas_priv->dev, "cali controls error\n");
@@ -1736,6 +1738,7 @@ out:
if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) {
switch (tas_priv->chip_id) {
case TAS2563:
+ case TAS2573:
case TAS2781:
case TAS5802:
case TAS5806M:
@@ -1900,6 +1903,7 @@ static int tasdevice_codec_probe(struct snd_soc_component *codec)
case TAS2568:
case TAS2570:
case TAS2572:
+ case TAS2573:
case TAS2574:
p = (struct snd_kcontrol_new *)tas2x20_snd_controls;
size = ARRAY_SIZE(tas2x20_snd_controls);
@@ -2094,6 +2098,7 @@ static const struct acpi_device_id tasdevice_acpi_match[] = {
{ "TXNW2568", (kernel_ulong_t)&tasdevice_id[TAS2568] },
{ "TXNW2570", (kernel_ulong_t)&tasdevice_id[TAS2570] },
{ "TXNW2572", (kernel_ulong_t)&tasdevice_id[TAS2572] },
+ { "TXNW2573", (kernel_ulong_t)&tasdevice_id[TAS2573] },
{ "TXNW2574", (kernel_ulong_t)&tasdevice_id[TAS2574] },
{ "TXNW2781", (kernel_ulong_t)&tasdevice_id[TAS2781] },
{ "TXNW5802", (kernel_ulong_t)&tasdevice_id[TAS5802] },
diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c
index 90008d2d06e2..3d0b116544cc 100644
--- a/sound/soc/codecs/tas2783-sdw.c
+++ b/sound/soc/codecs/tas2783-sdw.c
@@ -100,6 +100,8 @@ struct tas2783_prv {
wait_queue_head_t fw_wait;
bool fw_dl_task_done;
bool fw_dl_success;
+ /* use fallback fw name */
+ bool fw_use_fallback;
};
static const struct reg_default tas2783_reg_default[] = {
@@ -740,11 +742,19 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context)
goto out;
}
+ /* firmware binary not found*/
if (!fmw || !fmw->data) {
- /* firmware binary not found*/
- dev_err(tas_dev->dev,
- "Failed to read fw binary %s\n",
- tas_dev->rca_binaryname);
+ if (!tas_dev->fw_use_fallback) {
+ tas_dev->fw_use_fallback = true;
+ dev_info(tas_dev->dev,
+ "Failed to read preferred fw binary: %s, attempting fallback binary load\n",
+ tas_dev->rca_binaryname);
+ } else {
+ dev_err(tas_dev->dev,
+ "Failed to read fallback fw binary %s\n",
+ tas_dev->rca_binaryname);
+ }
+
ret = -EINVAL;
goto out;
}
@@ -1011,7 +1021,6 @@ static s32 tas_component_probe(struct snd_soc_component *component)
snd_soc_component_get_drvdata(component);
tas_dev->component = component;
- tas25xx_register_misc(tas_dev->sdw_peripheral);
return 0;
}
@@ -1020,7 +1029,6 @@ static void tas_component_remove(struct snd_soc_component *codec)
{
struct tas2783_prv *tas_dev =
snd_soc_component_get_drvdata(codec);
- tas25xx_deregister_misc();
tas_dev->component = NULL;
}
@@ -1082,22 +1090,14 @@ static s32 tas2783_sdca_dev_resume(struct device *dev)
{
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
- unsigned long t;
-
- if (!slave->unattach_request)
- goto regmap_sync;
+ int ret;
- t = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(TAS2783_PROBE_TIMEOUT));
- if (!t) {
- dev_err(&slave->dev, "resume: initialization timed out\n");
+ ret = sdw_slave_wait_for_init(slave, TAS2783_PROBE_TIMEOUT);
+ if (ret) {
sdw_show_ping_status(slave->bus, true);
- return -ETIMEDOUT;
+ return ret;
}
- slave->unattach_request = 0;
-
-regmap_sync:
regcache_cache_only(tas_dev->regmap, false);
regcache_sync(tas_dev->regmap);
return 0;
@@ -1115,13 +1115,16 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz
bool pci_found = false;
#if IS_ENABLED(CONFIG_PCI)
struct device *dev = &slave->dev;
+ struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev);
struct pci_dev *pci = NULL;
+ const char *fw_uid_prefix = tas_dev->fw_use_fallback ? "" : "0x";
for (; dev; dev = dev->parent) {
if (dev->bus == &pci_bus_type) {
pci = to_pci_dev(dev);
- scnprintf(name, size, "%04X-%1X-%1X.bin",
- pci->subsystem_device, bus->link_id, unique_id);
+ scnprintf(name, size, "%04X-%1X-%s%1X.bin",
+ pci->subsystem_device, bus->link_id,
+ fw_uid_prefix, unique_id);
pci_found = true;
break;
}
@@ -1133,28 +1136,15 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz
bus->link_id, unique_id);
}
-static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
+static s32 tas_fw_load(struct tas2783_prv *tas_dev, struct sdw_slave *slave)
{
- struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
s32 ret;
u8 unique_id = tas_dev->sdw_peripheral->id.unique_id;
- if (tas_dev->hw_init)
- return 0;
-
- tas_dev->fw_dl_task_done = false;
- tas_dev->fw_dl_success = false;
-
- ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
- if (ret) {
- dev_err(dev, "sw reset failed, err=%d", ret);
- return ret;
- }
- usleep_range(2000, 2200);
-
tas_generate_fw_name(slave, tas_dev->rca_binaryname,
sizeof(tas_dev->rca_binaryname));
+ tas_dev->fw_dl_task_done = false;
ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT,
tas_dev->rca_binaryname, tas_dev->dev,
GFP_KERNEL, tas_dev, tas2783_fw_ready);
@@ -1169,8 +1159,35 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
msecs_to_jiffies(TIMEOUT_FW_DL_MS));
if (!ret) {
dev_err(tas_dev->dev, "fw request, wait_event timeout\n");
- ret = -EAGAIN;
- } else {
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static s32 tas_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct tas2783_prv *tas_dev = dev_get_drvdata(dev);
+ s32 ret;
+
+ if (tas_dev->hw_init)
+ return 0;
+
+ tas_dev->fw_dl_success = false;
+
+ ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1);
+ if (ret) {
+ dev_err(dev, "sw reset failed, err=%d", ret);
+ return ret;
+ }
+ usleep_range(2000, 2200);
+
+ tas_dev->fw_use_fallback = false;
+ ret = tas_fw_load(tas_dev, slave);
+ if (!ret && tas_dev->fw_use_fallback)
+ ret = tas_fw_load(tas_dev, slave);
+
+ if (!ret) {
if (tas_dev->sa_func_data)
ret = sdca_regmap_write_init(dev, tas_dev->regmap,
tas_dev->sa_func_data);
@@ -1310,10 +1327,10 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral,
return dev_err_probe(dev, -ENOMEM,
"failed to parse sdca functions");
+ function_data->desc = &peripheral->sdca_data.function[i];
+
/* Parse the function */
- ret = sdca_parse_function(dev, peripheral,
- &peripheral->sdca_data.function[i],
- function_data);
+ ret = sdca_parse_function(dev, peripheral, function_data);
if (!ret)
tas_dev->sa_func_data = function_data;
else
diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h
index bf34319c9a9f..d5996c73526c 100644
--- a/sound/soc/codecs/tas2783.h
+++ b/sound/soc/codecs/tas2783.h
@@ -100,12 +100,4 @@
#define TAS2783_CALIB_DATA_SZ ((TAS2783_CALIB_HDR_SZ) + TAS2783_CALIB_CRC_SZ + \
((TAS2783_CALIB_PARAMS) * 4 * (TAS2783_CALIB_MAX_SPK_COUNT)))
-#if IS_ENABLED(CONFIG_SND_SOC_TAS2783_UTIL)
-int32_t tas25xx_register_misc(struct sdw_slave *peripheral);
-int32_t tas25xx_deregister_misc(void);
-#else
-static void tas25xx_register_misc(struct sdw_slave *peripheral) {}
-static void tas25xx_deregister_misc(void) {}
-#endif
-
#endif /*__TAS2783_H__ */
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index 12bf6a89dbd8..4d1c122c8f04 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -891,7 +891,7 @@ static const struct snd_soc_component_driver soc_component_dev_tas5086 = {
};
static const struct i2c_device_id tas5086_i2c_id[] = {
- { "tas5086" },
+ { .name = "tas5086" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
@@ -975,17 +975,13 @@ static int tas5086_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void tas5086_i2c_remove(struct i2c_client *i2c)
-{}
-
static struct i2c_driver tas5086_i2c_driver = {
.driver = {
- .name = "tas5086",
+ .name = "tas5086",
.of_match_table = of_match_ptr(tas5086_dt_ids),
},
- .id_table = tas5086_i2c_id,
- .probe = tas5086_i2c_probe,
- .remove = tas5086_i2c_remove,
+ .id_table = tas5086_i2c_id,
+ .probe = tas5086_i2c_probe,
};
module_i2c_driver(tas5086_i2c_driver);
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index 19ccf8641e16..8b5f9accf120 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -1064,13 +1064,13 @@ static const struct of_device_id tas571x_of_match[] __maybe_unused = {
MODULE_DEVICE_TABLE(of, tas571x_of_match);
static const struct i2c_device_id tas571x_i2c_id[] = {
- { "tas5707", (kernel_ulong_t) &tas5707_chip },
- { "tas5711", (kernel_ulong_t) &tas5711_chip },
- { "tas5717", (kernel_ulong_t) &tas5717_chip },
- { "tas5719", (kernel_ulong_t) &tas5717_chip },
- { "tas5721", (kernel_ulong_t) &tas5721_chip },
- { "tas5733", (kernel_ulong_t) &tas5733_chip },
- { "tas5753", (kernel_ulong_t) &tas5753_chip },
+ { .name = "tas5707", .driver_data = (kernel_ulong_t)&tas5707_chip },
+ { .name = "tas5711", .driver_data = (kernel_ulong_t)&tas5711_chip },
+ { .name = "tas5717", .driver_data = (kernel_ulong_t)&tas5717_chip },
+ { .name = "tas5719", .driver_data = (kernel_ulong_t)&tas5717_chip },
+ { .name = "tas5721", .driver_data = (kernel_ulong_t)&tas5721_chip },
+ { .name = "tas5733", .driver_data = (kernel_ulong_t)&tas5733_chip },
+ { .name = "tas5753", .driver_data = (kernel_ulong_t)&tas5753_chip },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
index 2dcdd0a4bf80..0bbcfaadf3f2 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -716,9 +716,9 @@ static struct snd_soc_dai_driver tas5720_dai[] = {
};
static const struct i2c_device_id tas5720_id[] = {
- { "tas5720", TAS5720 },
- { "tas5720a-q1", TAS5720A_Q1 },
- { "tas5722", TAS5722 },
+ { .name = "tas5720", .driver_data = TAS5720 },
+ { .name = "tas5720a-q1", .driver_data = TAS5720A_Q1 },
+ { .name = "tas5722", .driver_data = TAS5722 },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas5720_id);
diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c
index 867046b7aaa0..bcc8cab8d667 100644
--- a/sound/soc/codecs/tas5805m.c
+++ b/sound/soc/codecs/tas5805m.c
@@ -580,7 +580,7 @@ static void tas5805m_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id tas5805m_i2c_id[] = {
- { "tas5805m", },
+ { .name = "tas5805m" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas5805m_i2c_id);
diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c
index 85ecc246896f..f5d50f8a1cfb 100644
--- a/sound/soc/codecs/tas6424.c
+++ b/sound/soc/codecs/tas6424.c
@@ -794,7 +794,7 @@ static void tas6424_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id tas6424_i2c_ids[] = {
- { "tas6424" },
+ { .name = "tas6424" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids);
diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c
new file mode 100644
index 000000000000..82526362de7b
--- /dev/null
+++ b/sound/soc/codecs/tas675x.c
@@ -0,0 +1,2194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ALSA SoC Texas Instruments TAS67524 Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Sen Wang <sen@ti.com>
+ */
+
+#include <linux/bitfield.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
+#include <linux/delay.h>
+#include <linux/property.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/pm_runtime.h>
+#include <linux/iopoll.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "tas675x.h"
+
+#define TAS675X_FAULT_CHECK_INTERVAL_MS 200
+
+enum tas675x_type {
+ TAS67524,
+};
+
+struct tas675x_reg_param {
+ u8 page;
+ u8 reg;
+ u32 val;
+};
+
+struct tas675x_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ enum tas675x_type dev_type;
+ /* Custom regmap lock; protects writes across books */
+ struct mutex io_lock;
+
+ struct gpio_desc *pd_gpio;
+ struct gpio_desc *stby_gpio;
+ struct regulator_bulk_data supplies[2];
+ struct regulator *vbat;
+ bool fast_boot;
+
+ int audio_slot;
+ int llp_slot;
+ int vpredict_slot;
+ int isense_slot;
+ int bclk_offset;
+ int slot_width;
+ unsigned int tx_mask;
+
+ int gpio1_func;
+ int gpio2_func;
+
+ unsigned long active_playback_dais;
+ unsigned long active_capture_dais;
+ unsigned int rate;
+ unsigned int saved_rtldg_en;
+#define TAS675X_DSP_PARAM_NUM 2
+ struct tas675x_reg_param dsp_params[TAS675X_DSP_PARAM_NUM];
+
+ /* Fault monitor, disabled when Fault IRQ is used */
+ struct delayed_work fault_check_work;
+#define TAS675X_FAULT_REGS_NUM 9
+ unsigned int last_status[TAS675X_FAULT_REGS_NUM];
+};
+
+static const char * const tas675x_supply_names[] = {
+ "dvdd", /* Digital power supply */
+ "pvdd", /* Output powerstage supply */
+};
+
+/* Page 1 setup initialization defaults */
+static const struct reg_sequence tas675x_page1_init[] = {
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC8), 0x20), /* Charge pump clock */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x2F), 0x90), /* VBAT idle */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x29), 0x40), /* OC/CBC threshold */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x2E), 0x0C), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC5), 0x02), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC6), 0x10), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x1F), 0x20), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x16), 0x01), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0x1E), 0x04), /* OC/CBC config */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC1), 0x00), /* CH1 DC fault */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC2), 0x04), /* CH2 DC fault */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC3), 0x00), /* CH3 DC fault */
+ REG_SEQ0(TAS675X_PAGE_REG(1, 0xC4), 0x00), /* CH4 DC fault */
+};
+
+static inline const char *tas675x_state_name(unsigned int state)
+{
+ switch (state & 0x0F) {
+ case TAS675X_STATE_DEEPSLEEP: return "DEEPSLEEP";
+ case TAS675X_STATE_LOAD_DIAG: return "LOAD_DIAG";
+ case TAS675X_STATE_SLEEP: return "SLEEP";
+ case TAS675X_STATE_HIZ: return "HIZ";
+ case TAS675X_STATE_PLAY: return "PLAY";
+ case TAS675X_STATE_FAULT: return "FAULT";
+ case TAS675X_STATE_AUTOREC: return "AUTOREC";
+ default: return "UNKNOWN";
+ }
+}
+
+static inline int tas675x_set_state_all(struct tas675x_priv *tas, u8 state)
+{
+ const struct reg_sequence seq[] = {
+ REG_SEQ0(TAS675X_STATE_CTRL_CH1_CH2_REG, state),
+ REG_SEQ0(TAS675X_STATE_CTRL_CH3_CH4_REG, state),
+ };
+
+ return regmap_multi_reg_write(tas->regmap, seq, ARRAY_SIZE(seq));
+}
+
+static inline int tas675x_select_book(struct regmap *regmap, u8 book)
+{
+ int ret;
+
+ /* Reset page to 0 before switching books */
+ ret = regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00);
+ if (!ret)
+ ret = regmap_write(regmap, TAS675X_BOOK_CTRL_REG, book);
+
+ return ret;
+}
+
+/* Raw I2C version of tas675x_select_book, must be called with io_lock held */
+static inline int __tas675x_select_book(struct tas675x_priv *tas, u8 book)
+{
+ struct i2c_client *client = to_i2c_client(tas->dev);
+ int ret;
+
+ /* Reset page to 0 before switching books */
+ ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, 0x00);
+ if (ret)
+ return ret;
+
+ return i2c_smbus_write_byte_data(client, TAS675X_BOOK_CTRL_REG, book);
+}
+
+static int tas675x_dsp_mem_write(struct tas675x_priv *tas, u8 page, u8 reg, u32 val)
+{
+ struct i2c_client *client = to_i2c_client(tas->dev);
+ u8 buf[4];
+ int ret;
+
+ /* DSP registers are 32 bit big-endian */
+ buf[0] = (val >> 24) & 0xFF;
+ buf[1] = (val >> 16) & 0xFF;
+ buf[2] = (val >> 8) & 0xFF;
+ buf[3] = val & 0xFF;
+
+ /*
+ * DSP regs in a different book, therefore block
+ * regmap access before completion.
+ */
+ mutex_lock(&tas->io_lock);
+
+ ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_write_i2c_block_data(client, reg, sizeof(buf), buf);
+
+out:
+ __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
+ mutex_unlock(&tas->io_lock);
+
+ return ret;
+}
+
+static int tas675x_dsp_mem_read(struct tas675x_priv *tas, u8 page, u8 reg, u32 *val)
+{
+ struct i2c_client *client = to_i2c_client(tas->dev);
+ u8 buf[4];
+ int ret;
+
+ /*
+ * DSP regs in a different book, therefore block
+ * regmap access before completion.
+ */
+ mutex_lock(&tas->io_lock);
+
+ ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page);
+ if (ret)
+ goto out;
+
+ ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf);
+ if (ret == sizeof(buf)) {
+ *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
+ ret = 0;
+ } else if (ret >= 0) {
+ ret = -EIO;
+ }
+
+out:
+ __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT);
+ mutex_unlock(&tas->io_lock);
+
+ return ret;
+}
+
+static const struct {
+ const char *name;
+ int val;
+} tas675x_gpio_func_map[] = {
+ /* Output functions */
+ { "low", TAS675X_GPIO_SEL_LOW },
+ { "auto-mute", TAS675X_GPIO_SEL_AUTO_MUTE_ALL },
+ { "auto-mute-ch4", TAS675X_GPIO_SEL_AUTO_MUTE_CH4 },
+ { "auto-mute-ch3", TAS675X_GPIO_SEL_AUTO_MUTE_CH3 },
+ { "auto-mute-ch2", TAS675X_GPIO_SEL_AUTO_MUTE_CH2 },
+ { "auto-mute-ch1", TAS675X_GPIO_SEL_AUTO_MUTE_CH1 },
+ { "sdout2", TAS675X_GPIO_SEL_SDOUT2 },
+ { "sdout1", TAS675X_GPIO_SEL_SDOUT1 },
+ { "warn", TAS675X_GPIO_SEL_WARN },
+ { "fault", TAS675X_GPIO_SEL_FAULT },
+ { "clock-sync", TAS675X_GPIO_SEL_CLOCK_SYNC },
+ { "invalid-clock", TAS675X_GPIO_SEL_INVALID_CLK },
+ { "high", TAS675X_GPIO_SEL_HIGH },
+ /* Input functions */
+ { "mute", TAS675X_GPIO_IN_MUTE },
+ { "phase-sync", TAS675X_GPIO_IN_PHASE_SYNC },
+ { "sdin2", TAS675X_GPIO_IN_SDIN2 },
+ { "deep-sleep", TAS675X_GPIO_IN_DEEP_SLEEP },
+ { "hiz", TAS675X_GPIO_IN_HIZ },
+ { "play", TAS675X_GPIO_IN_PLAY },
+ { "sleep", TAS675X_GPIO_IN_SLEEP },
+};
+
+static int tas675x_gpio_func_parse(struct device *dev, const char *propname)
+{
+ const char *str;
+ int i, ret;
+
+ ret = device_property_read_string(dev, propname, &str);
+ if (ret)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(tas675x_gpio_func_map); i++) {
+ if (!strcmp(str, tas675x_gpio_func_map[i].name))
+ return tas675x_gpio_func_map[i].val;
+ }
+
+ dev_warn(dev, "Invalid %s value '%s'\n", propname, str);
+ return -1;
+}
+
+static const struct {
+ unsigned int reg;
+ unsigned int mask;
+} tas675x_gpio_input_table[TAS675X_GPIO_IN_NUM] = {
+ [TAS675X_GPIO_IN_ID_MUTE] = {
+ TAS675X_GPIO_INPUT_MUTE_REG, TAS675X_GPIO_IN_MUTE_MASK },
+ [TAS675X_GPIO_IN_ID_PHASE_SYNC] = {
+ TAS675X_GPIO_INPUT_SYNC_REG, TAS675X_GPIO_IN_SYNC_MASK },
+ [TAS675X_GPIO_IN_ID_SDIN2] = {
+ TAS675X_GPIO_INPUT_SDIN2_REG, TAS675X_GPIO_IN_SDIN2_MASK },
+ [TAS675X_GPIO_IN_ID_DEEP_SLEEP] = {
+ TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_DEEP_SLEEP_MASK },
+ [TAS675X_GPIO_IN_ID_HIZ] = {
+ TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_HIZ_MASK },
+ [TAS675X_GPIO_IN_ID_PLAY] = {
+ TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_PLAY_MASK },
+ [TAS675X_GPIO_IN_ID_SLEEP] = {
+ TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_SLEEP_MASK },
+};
+
+static void tas675x_config_gpio_pin(struct regmap *regmap, int func_id,
+ unsigned int out_sel_reg,
+ unsigned int pin_idx,
+ unsigned int *gpio_ctrl)
+{
+ int id;
+
+ if (func_id < 0)
+ return;
+
+ if (func_id & TAS675X_GPIO_FUNC_INPUT) {
+ /* 3-bit mux: 0 = disabled, 0b1 = GPIO1, 0b10 = GPIO2 */
+ id = func_id & ~TAS675X_GPIO_FUNC_INPUT;
+ regmap_update_bits(regmap,
+ tas675x_gpio_input_table[id].reg,
+ tas675x_gpio_input_table[id].mask,
+ (pin_idx + 1) << __ffs(tas675x_gpio_input_table[id].mask));
+ } else {
+ /* Output GPIO, update selection register and enable bit */
+ regmap_write(regmap, out_sel_reg, func_id);
+ *gpio_ctrl |= pin_idx ? TAS675X_GPIO2_OUTPUT_EN : TAS675X_GPIO1_OUTPUT_EN;
+ }
+}
+
+static int tas675x_rtldg_thresh_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;
+ /* threshold reg ranges up to 24bit */
+ uinfo->value.integer.max = 0x00FFFFFF;
+ return 0;
+}
+
+static int tas675x_set_rtldg_thresh(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ const struct tas675x_reg_param *t =
+ (const struct tas675x_reg_param *)kcontrol->private_value;
+ u32 val = ucontrol->value.integer.value[0];
+ int ret;
+
+ ret = tas675x_dsp_mem_write(tas, t->page, t->reg, val);
+
+ /* Cache the value */
+ if (!ret) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) {
+ if (tas->dsp_params[i].page == t->page &&
+ tas->dsp_params[i].reg == t->reg) {
+ tas->dsp_params[i].val = val;
+ break;
+ }
+ }
+ }
+
+ /* Return 1 to notify change, or propagate error */
+ return ret ? ret : 1;
+}
+
+static int tas675x_get_rtldg_thresh(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ const struct tas675x_reg_param *t =
+ (const struct tas675x_reg_param *)kcontrol->private_value;
+ u32 val = 0;
+ int ret;
+
+ ret = tas675x_dsp_mem_read(tas, t->page, t->reg, &val);
+ if (!ret)
+ ucontrol->value.integer.value[0] = val;
+
+ return ret;
+}
+
+static const struct tas675x_reg_param tas675x_dsp_defaults[] = {
+ [TAS675X_DSP_PARAM_ID_OL_THRESH] = {
+ TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_OL_THRESH_REG },
+ [TAS675X_DSP_PARAM_ID_SL_THRESH] = {
+ TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_SL_THRESH_REG },
+};
+
+static_assert(ARRAY_SIZE(tas675x_dsp_defaults) == TAS675X_DSP_PARAM_NUM);
+
+static int tas675x_set_dcldg_trigger(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int state, state34;
+ int ret;
+
+ if (!ucontrol->value.integer.value[0])
+ return 0;
+
+ if (snd_soc_component_active(comp))
+ return -EBUSY;
+
+ ret = pm_runtime_resume_and_get(tas->dev);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Abort automatic DC LDG retry loops (startup or init-after-fault)
+ * and clear faults before manual diagnostics.
+ */
+ regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT);
+ regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+
+ /* Wait for LOAD_DIAG to exit */
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, (state & 0x0F) != TAS675X_STATE_LOAD_DIAG &&
+ (state >> 4) != TAS675X_STATE_LOAD_DIAG,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+ ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, (state34 & 0x0F) != TAS675X_STATE_LOAD_DIAG &&
+ (state34 >> 4) != TAS675X_STATE_LOAD_DIAG,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "DC LDG: abort timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n",
+ state, tas675x_state_name(state), tas675x_state_name(state >> 4),
+ state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4));
+ goto out_restore_ldg_ctrl;
+ }
+
+ /* Transition to HIZ state */
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH);
+ if (ret)
+ goto out_restore_ldg_ctrl;
+
+ /* Set LOAD_DIAG state for manual DC LDG */
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_LOAD_DIAG_BOTH);
+ if (ret)
+ goto out_restore_ldg_ctrl;
+
+ /* Wait for device to transition to LOAD_DIAG state */
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, state == TAS675X_STATE_LOAD_DIAG_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+ ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, state34 == TAS675X_STATE_LOAD_DIAG_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_STATE_TRANSITION_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "DC LDG: LOAD_DIAG timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n",
+ state, tas675x_state_name(state), tas675x_state_name(state >> 4),
+ state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4));
+ goto out_restore_hiz;
+ }
+
+ /* Clear ABORT and BYPASS bits to enable manual DC LDG */
+ ret = regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ 0);
+ if (ret)
+ goto out_restore_hiz;
+
+ dev_dbg(tas->dev, "DC LDG: Started\n");
+
+ /* Poll all channels for SLEEP state */
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, state == TAS675X_STATE_SLEEP_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_DC_LDG_TIMEOUT_US);
+ ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, state34 == TAS675X_STATE_SLEEP_BOTH,
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_DC_LDG_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "DC LDG: SLEEP timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n",
+ state, tas675x_state_name(state), tas675x_state_name(state >> 4),
+ state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4));
+ goto out_restore_hiz;
+ }
+
+ dev_dbg(tas->dev, "DC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n",
+ state, state34);
+
+out_restore_hiz:
+ tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH);
+
+out_restore_ldg_ctrl:
+ regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ 0);
+
+ pm_runtime_mark_last_busy(tas->dev);
+ pm_runtime_put_autosuspend(tas->dev);
+
+ return ret;
+}
+
+static int tas675x_set_acldg_trigger(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int state, state34;
+ int ret;
+
+ if (!ucontrol->value.integer.value[0])
+ return 0;
+
+ if (snd_soc_component_active(comp))
+ return -EBUSY;
+
+ ret = pm_runtime_resume_and_get(tas->dev);
+ if (ret < 0)
+ return ret;
+
+ /* AC Load Diagnostics requires SLEEP state */
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+ if (ret) {
+ dev_err(tas->dev, "AC LDG: Failed to set SLEEP state: %d\n", ret);
+ goto out;
+ }
+
+ /* Start AC LDG on all 4 channels (0x0F) */
+ ret = regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x0F);
+ if (ret) {
+ dev_err(tas->dev, "AC LDG: Failed to start: %d\n", ret);
+ goto out;
+ }
+
+ dev_dbg(tas->dev, "AC LDG: Started\n");
+
+ /* Poll all channels for SLEEP state */
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG,
+ state, (state == TAS675X_STATE_SLEEP_BOTH),
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_AC_LDG_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "AC LDG: CH1/CH2 timeout: %d (state=0x%02x [%s/%s])\n",
+ ret, state, tas675x_state_name(state),
+ tas675x_state_name(state >> 4));
+ regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
+ goto out;
+ }
+
+ ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG,
+ state34, (state34 == TAS675X_STATE_SLEEP_BOTH),
+ TAS675X_POLL_INTERVAL_US,
+ TAS675X_AC_LDG_TIMEOUT_US);
+ if (ret) {
+ dev_err(tas->dev,
+ "AC LDG: CH3/CH4 timeout: %d (state=0x%02x [%s/%s])\n",
+ ret, state34, tas675x_state_name(state34),
+ tas675x_state_name(state34 >> 4));
+ regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
+ goto out;
+ }
+
+ dev_dbg(tas->dev, "AC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n",
+ state, state34);
+ regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00);
+
+out:
+ pm_runtime_mark_last_busy(tas->dev);
+ pm_runtime_put_autosuspend(tas->dev);
+
+ return ret;
+}
+
+static int tas675x_rtldg_impedance_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 = 0xFFFF;
+ return 0;
+}
+
+static int tas675x_get_rtldg_impedance(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int msb_reg = (unsigned int)kcontrol->private_value;
+ u8 buf[2];
+ int ret;
+
+ ret = regmap_bulk_read(tas->regmap, msb_reg, buf, 2);
+ if (ret)
+ return ret;
+
+ ucontrol->value.integer.value[0] = (buf[0] << 8) | buf[1];
+ return 0;
+}
+
+static int tas675x_dc_resistance_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ /* 10-bit: 2-bit MSB + 8-bit LSB, 0.1 ohm/code, 0-102.3 ohm */
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->count = 1;
+ uinfo->value.integer.min = 0;
+ uinfo->value.integer.max = 1023;
+ return 0;
+}
+
+static int tas675x_get_dc_resistance(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol);
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp);
+ unsigned int lsb_reg = (unsigned int)kcontrol->private_value;
+ unsigned int msb, lsb, shift;
+ int ret;
+
+ ret = regmap_read(tas->regmap, TAS675X_DC_LDG_DCR_MSB_REG, &msb);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(tas->regmap, lsb_reg, &lsb);
+ if (ret)
+ return ret;
+
+ /* 2-bit MSB: CH1=[7:6], CH2=[5:4], CH3=[3:2], CH4=[1:0] */
+ shift = 6 - (lsb_reg - TAS675X_CH1_DC_LDG_DCR_LSB_REG) * 2;
+ msb = (msb >> shift) & 0x3;
+
+ ucontrol->value.integer.value[0] = (msb << 8) | lsb;
+ return 0;
+}
+
+/* Counterparts with read-only access */
+#define SOC_SINGLE_RO(xname, xreg, xshift, xmax) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = snd_soc_info_volsw, \
+ .get = snd_soc_get_volsw, \
+ .private_value = SOC_SINGLE_VALUE(xreg, xshift, 0, xmax, 0, 0) }
+#define SOC_DC_RESIST_RO(xname, xlsb_reg) \
+{ .name = xname, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = tas675x_dc_resistance_info, \
+ .get = tas675x_get_dc_resistance, \
+ .private_value = (xlsb_reg) }
+#define SOC_RTLDG_IMP_RO(xname, xreg) \
+{ .name = xname, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \
+ .info = tas675x_rtldg_impedance_info, \
+ .get = tas675x_get_rtldg_impedance, \
+ .private_value = (xreg) }
+
+#define SOC_DSP_THRESH_EXT(xname, xthresh) \
+{ .name = xname, \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .info = tas675x_rtldg_thresh_info, \
+ .get = tas675x_get_rtldg_thresh, \
+ .put = tas675x_set_rtldg_thresh, \
+ .private_value = (unsigned long)&(xthresh) }
+
+/*
+ * DAC digital volumes. From -103 to 0 dB in 0.5 dB steps, -103.5 dB means mute.
+ * DAC analog gain. From -15.5 to 0 dB in 0.5 dB steps, no mute.
+ */
+static const DECLARE_TLV_DB_SCALE(tas675x_dig_vol_tlv, -10350, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tas675x_ana_gain_tlv, -1550, 50, 0);
+
+static const char * const tas675x_ss_texts[] = {
+ "Disabled", "Triangle", "Random", "Triangle and Random"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_enum, TAS675X_SS_CTRL_REG, 0, tas675x_ss_texts);
+
+static const char * const tas675x_ss_tri_range_texts[] = {
+ "6.5%", "13.5%", "5%", "10%"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_tri_range_enum,
+ TAS675X_SS_RANGE_CTRL_REG, 0,
+ tas675x_ss_tri_range_texts);
+
+static const char * const tas675x_ss_rdm_range_texts[] = {
+ "0.83%", "2.50%", "5.83%", "12.50%", "25.83%"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_range_enum,
+ TAS675X_SS_RANGE_CTRL_REG, 4,
+ tas675x_ss_rdm_range_texts);
+
+static const char * const tas675x_ss_rdm_dwell_texts[] = {
+ "1/FSS to 2/FSS", "1/FSS to 4/FSS", "1/FSS to 8/FSS", "1/FSS to 15/FSS"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_dwell_enum,
+ TAS675X_SS_RANGE_CTRL_REG, 2,
+ tas675x_ss_rdm_dwell_texts);
+
+static const char * const tas675x_oc_limit_texts[] = {
+ "Level 4", "Level 3", "Level 2", "Level 1"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_oc_limit_enum, TAS675X_CURRENT_LIMIT_CTRL_REG,
+ 0, tas675x_oc_limit_texts);
+
+static const char * const tas675x_otw_texts[] = {
+ "Disabled", ">95C", ">110C", ">125C", ">135C", ">145C", ">155C", ">165C"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ch1_otw_enum,
+ TAS675X_OTW_CTRL_CH1_CH2_REG, 4,
+ tas675x_otw_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch2_otw_enum,
+ TAS675X_OTW_CTRL_CH1_CH2_REG, 0,
+ tas675x_otw_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch3_otw_enum,
+ TAS675X_OTW_CTRL_CH3_CH4_REG, 4,
+ tas675x_otw_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch4_otw_enum,
+ TAS675X_OTW_CTRL_CH3_CH4_REG, 0,
+ tas675x_otw_texts);
+
+static const char * const tas675x_dc_ldg_sl_texts[] = {
+ "0.5 Ohm", "1 Ohm", "1.5 Ohm", "2 Ohm", "2.5 Ohm",
+ "3 Ohm", "3.5 Ohm", "4 Ohm", "4.5 Ohm", "5 Ohm"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ch1_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 4,
+ tas675x_dc_ldg_sl_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch2_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0,
+ tas675x_dc_ldg_sl_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch3_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 4,
+ tas675x_dc_ldg_sl_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch4_dc_ldg_sl_enum,
+ TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0,
+ tas675x_dc_ldg_sl_texts);
+
+static const char * const tas675x_dc_slol_ramp_texts[] = {
+ "15 ms", "30 ms", "10 ms", "20 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_ramp_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 6,
+ tas675x_dc_slol_ramp_texts);
+
+static const char * const tas675x_dc_slol_settling_texts[] = {
+ "10 ms", "5 ms", "20 ms", "15 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_settling_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 4,
+ tas675x_dc_slol_settling_texts);
+
+static const char * const tas675x_dc_s2pg_ramp_texts[] = {
+ "5 ms", "2.5 ms", "10 ms", "15 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_ramp_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 2,
+ tas675x_dc_s2pg_ramp_texts);
+
+static const char * const tas675x_dc_s2pg_settling_texts[] = {
+ "10 ms", "5 ms", "20 ms", "30 ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_settling_enum,
+ TAS675X_DC_LDG_TIME_CTRL_REG, 0,
+ tas675x_dc_s2pg_settling_texts);
+
+static const char * const tas675x_dsp_mode_texts[] = {
+ "Normal", "LLP", "FFLP"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_dsp_mode_enum,
+ TAS675X_LL_EN_REG, 0,
+ tas675x_dsp_mode_texts);
+
+static const char * const tas675x_ana_ramp_texts[] = {
+ "15us", "60us", "200us", "400us"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ana_ramp_enum,
+ TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 2,
+ tas675x_ana_ramp_texts);
+
+static const char * const tas675x_ramp_rate_texts[] = {
+ "4 FS", "16 FS", "32 FS", "Instant"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_rate_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 6,
+ tas675x_ramp_rate_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_rate_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 2,
+ tas675x_ramp_rate_texts);
+
+static const char * const tas675x_ramp_step_texts[] = {
+ "4dB", "2dB", "1dB", "0.5dB"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_step_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 4,
+ tas675x_ramp_step_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_step_enum,
+ TAS675X_DIG_VOL_RAMP_CTRL_REG, 0,
+ tas675x_ramp_step_texts);
+
+static const char * const tas675x_vol_combine_ch12_texts[] = {
+ "Independent", "CH2 follows CH1", "CH1 follows CH2"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch12_enum,
+ TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0,
+ tas675x_vol_combine_ch12_texts);
+
+static const char * const tas675x_vol_combine_ch34_texts[] = {
+ "Independent", "CH4 follows CH3", "CH3 follows CH4"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch34_enum,
+ TAS675X_DIG_VOL_COMBINE_CTRL_REG, 2,
+ tas675x_vol_combine_ch34_texts);
+
+static const char * const tas675x_auto_mute_time_texts[] = {
+ "11.5ms", "53ms", "106.5ms", "266.5ms",
+ "535ms", "1065ms", "2665ms", "5330ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(tas675x_ch1_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 4,
+ tas675x_auto_mute_time_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch2_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0,
+ tas675x_auto_mute_time_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch3_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 4,
+ tas675x_auto_mute_time_texts);
+static SOC_ENUM_SINGLE_DECL(tas675x_ch4_mute_time_enum,
+ TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0,
+ tas675x_auto_mute_time_texts);
+
+/*
+ * ALSA Mixer Controls
+ *
+ * For detailed documentation of each control see:
+ * Documentation/sound/codecs/tas675x.rst
+ */
+static const struct snd_kcontrol_new tas675x_snd_controls[] = {
+ /* Volume & Gain Control */
+ SOC_DOUBLE_R_TLV("Analog Playback Volume", TAS675X_ANALOG_GAIN_CH1_CH2_REG,
+ TAS675X_ANALOG_GAIN_CH3_CH4_REG, 1, 0x1F, 1, tas675x_ana_gain_tlv),
+ SOC_ENUM("Analog Gain Ramp Step", tas675x_ana_ramp_enum),
+ SOC_SINGLE_RANGE_TLV("CH1 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH1_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_SINGLE_RANGE_TLV("CH2 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH2_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_SINGLE_RANGE_TLV("CH3 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH3_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_SINGLE_RANGE_TLV("CH4 Digital Playback Volume",
+ TAS675X_DIG_VOL_CH4_REG, 0, 0x30, 0xFF, 1,
+ tas675x_dig_vol_tlv),
+ SOC_ENUM("Volume Ramp Down Rate", tas675x_ramp_down_rate_enum),
+ SOC_ENUM("Volume Ramp Down Step", tas675x_ramp_down_step_enum),
+ SOC_ENUM("Volume Ramp Up Rate", tas675x_ramp_up_rate_enum),
+ SOC_ENUM("Volume Ramp Up Step", tas675x_ramp_up_step_enum),
+ SOC_ENUM("CH1/2 Volume Combine", tas675x_vol_combine_ch12_enum),
+ SOC_ENUM("CH3/4 Volume Combine", tas675x_vol_combine_ch34_enum),
+
+ /* Auto Mute & Silence Detection */
+ SOC_SINGLE("CH1 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 0, 1, 0),
+ SOC_SINGLE("CH2 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 1, 1, 0),
+ SOC_SINGLE("CH3 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 2, 1, 0),
+ SOC_SINGLE("CH4 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 3, 1, 0),
+ SOC_SINGLE("Auto Mute Combine Switch", TAS675X_AUTO_MUTE_EN_REG, 4, 1, 0),
+ SOC_ENUM("CH1 Auto Mute Time", tas675x_ch1_mute_time_enum),
+ SOC_ENUM("CH2 Auto Mute Time", tas675x_ch2_mute_time_enum),
+ SOC_ENUM("CH3 Auto Mute Time", tas675x_ch3_mute_time_enum),
+ SOC_ENUM("CH4 Auto Mute Time", tas675x_ch4_mute_time_enum),
+
+ /* Clock & EMI Management */
+ SOC_ENUM("Spread Spectrum Mode", tas675x_ss_enum),
+ SOC_ENUM("SS Triangle Range", tas675x_ss_tri_range_enum),
+ SOC_ENUM("SS Random Range", tas675x_ss_rdm_range_enum),
+ SOC_ENUM("SS Random Dwell Range", tas675x_ss_rdm_dwell_enum),
+ SOC_SINGLE("SS Triangle Dwell Min", TAS675X_SS_DWELL_CTRL_REG, 4, 15, 0),
+ SOC_SINGLE("SS Triangle Dwell Max", TAS675X_SS_DWELL_CTRL_REG, 0, 15, 0),
+
+ /* Hardware Protection */
+ SOC_SINGLE("OTSD Auto Recovery Switch", TAS675X_OTSD_RECOVERY_EN_REG, 1, 1, 0),
+ SOC_ENUM("Overcurrent Limit Level", tas675x_oc_limit_enum),
+ SOC_ENUM("CH1 OTW Threshold", tas675x_ch1_otw_enum),
+ SOC_ENUM("CH2 OTW Threshold", tas675x_ch2_otw_enum),
+ SOC_ENUM("CH3 OTW Threshold", tas675x_ch3_otw_enum),
+ SOC_ENUM("CH4 OTW Threshold", tas675x_ch4_otw_enum),
+
+ /* DSP Signal Path & Mode */
+ SOC_ENUM("DSP Signal Path Mode", tas675x_dsp_mode_enum),
+
+ /* DC Load Diagnostics */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "DC LDG Trigger",
+ .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .put = tas675x_set_dcldg_trigger,
+ },
+ SOC_SINGLE("DC LDG Auto Diagnostics Switch", TAS675X_DC_LDG_CTRL_REG, 0, 1, 1),
+ SOC_SINGLE("CH1 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 3, 1, 0),
+ SOC_SINGLE("CH2 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 2, 1, 0),
+ SOC_SINGLE("CH3 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 1, 1, 0),
+ SOC_SINGLE("CH4 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 0, 1, 0),
+ SOC_ENUM("DC LDG SLOL Ramp Time", tas675x_dc_slol_ramp_enum),
+ SOC_ENUM("DC LDG SLOL Settling Time", tas675x_dc_slol_settling_enum),
+ SOC_ENUM("DC LDG S2PG Ramp Time", tas675x_dc_s2pg_ramp_enum),
+ SOC_ENUM("DC LDG S2PG Settling Time", tas675x_dc_s2pg_settling_enum),
+ SOC_ENUM("CH1 DC LDG SL Threshold", tas675x_ch1_dc_ldg_sl_enum),
+ SOC_ENUM("CH2 DC LDG SL Threshold", tas675x_ch2_dc_ldg_sl_enum),
+ SOC_ENUM("CH3 DC LDG SL Threshold", tas675x_ch3_dc_ldg_sl_enum),
+ SOC_ENUM("CH4 DC LDG SL Threshold", tas675x_ch4_dc_ldg_sl_enum),
+ SOC_SINGLE_RO("DC LDG Result", TAS675X_DC_LDG_RESULT_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH1 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 4, 0x0F),
+ SOC_SINGLE_RO("CH2 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 0, 0x0F),
+ SOC_SINGLE_RO("CH3 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 4, 0x0F),
+ SOC_SINGLE_RO("CH4 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 0, 0x0F),
+ SOC_SINGLE_RO("CH1 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 7, 1),
+ SOC_SINGLE_RO("CH2 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 6, 1),
+ SOC_SINGLE_RO("CH3 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 5, 1),
+ SOC_SINGLE_RO("CH4 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 4, 1),
+ SOC_DC_RESIST_RO("CH1 DC Resistance", TAS675X_CH1_DC_LDG_DCR_LSB_REG),
+ SOC_DC_RESIST_RO("CH2 DC Resistance", TAS675X_CH2_DC_LDG_DCR_LSB_REG),
+ SOC_DC_RESIST_RO("CH3 DC Resistance", TAS675X_CH3_DC_LDG_DCR_LSB_REG),
+ SOC_DC_RESIST_RO("CH4 DC Resistance", TAS675X_CH4_DC_LDG_DCR_LSB_REG),
+
+ /* AC Load Diagnostics */
+ {
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+ .name = "AC LDG Trigger",
+ .access = SNDRV_CTL_ELEM_ACCESS_WRITE,
+ .info = snd_ctl_boolean_mono_info,
+ .put = tas675x_set_acldg_trigger,
+ },
+ SOC_SINGLE("AC LDG Gain", TAS675X_AC_LDG_CTRL_REG, 4, 1, 0),
+ SOC_SINGLE("AC LDG Test Frequency", TAS675X_AC_LDG_FREQ_CTRL_REG, 0, 0xFF, 0),
+ SOC_SINGLE_RO("CH1 AC LDG Real", TAS675X_AC_LDG_REPORT_CH1_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH1 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH1_I_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH2 AC LDG Real", TAS675X_AC_LDG_REPORT_CH2_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH2 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH2_I_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH3 AC LDG Real", TAS675X_AC_LDG_REPORT_CH3_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH3 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH3_I_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH4 AC LDG Real", TAS675X_AC_LDG_REPORT_CH4_R_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH4 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH4_I_REG, 0, 0xFF),
+
+ /* Temperature and Voltage Monitoring */
+ SOC_SINGLE_RO("PVDD Sense", TAS675X_PVDD_SENSE_REG, 0, 0xFF),
+ SOC_SINGLE_RO("Global Temperature", TAS675X_TEMP_GLOBAL_REG, 0, 0xFF),
+ SOC_SINGLE_RO("CH1 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 0, 7),
+ SOC_SINGLE_RO("CH2 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 3, 7),
+ SOC_SINGLE_RO("CH3 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 0, 7),
+ SOC_SINGLE_RO("CH4 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 3, 7),
+
+ /* Speaker Protection & Detection */
+ SOC_SINGLE("Tweeter Detection Switch", TAS675X_TWEETER_DETECT_CTRL_REG, 0, 1, 1),
+ SOC_SINGLE("Tweeter Detect Threshold", TAS675X_TWEETER_DETECT_THRESH_REG, 0, 0xFF, 0),
+ SOC_SINGLE_RO("CH1 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 3, 1),
+ SOC_SINGLE_RO("CH2 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 2, 1),
+ SOC_SINGLE_RO("CH3 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 1, 1),
+ SOC_SINGLE_RO("CH4 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 0, 1),
+
+ /*
+ * Unavailable in LLP, available in Normal & FFLP
+ */
+ SOC_SINGLE("Thermal Foldback Switch", TAS675X_DSP_CTRL_REG, 0, 1, 0),
+ SOC_SINGLE("PVDD Foldback Switch", TAS675X_DSP_CTRL_REG, 4, 1, 0),
+ SOC_SINGLE("DC Blocker Bypass Switch", TAS675X_DC_BLOCK_BYP_REG, 0, 1, 0),
+ SOC_SINGLE("Clip Detect Switch", TAS675X_CLIP_DETECT_CTRL_REG, 6, 1, 0),
+ SOC_SINGLE("Audio SDOUT Switch", TAS675X_DSP_CTRL_REG, 5, 1, 0),
+
+ /*
+ * Unavailable in both FFLP and LLP, Normal mode only
+ */
+ /* Real-Time Load Diagnostics */
+ SOC_SINGLE("CH1 RTLDG Switch", TAS675X_RTLDG_EN_REG, 3, 1, 0),
+ SOC_SINGLE("CH2 RTLDG Switch", TAS675X_RTLDG_EN_REG, 2, 1, 0),
+ SOC_SINGLE("CH3 RTLDG Switch", TAS675X_RTLDG_EN_REG, 1, 1, 0),
+ SOC_SINGLE("CH4 RTLDG Switch", TAS675X_RTLDG_EN_REG, 0, 1, 0),
+ SOC_SINGLE("RTLDG Clip Mask Switch", TAS675X_RTLDG_EN_REG, 4, 1, 0),
+ SOC_SINGLE("ISENSE Calibration Switch", TAS675X_ISENSE_CAL_REG, 3, 1, 0),
+ SOC_DSP_THRESH_EXT("RTLDG Open Load Threshold",
+ tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_OL_THRESH]),
+ SOC_DSP_THRESH_EXT("RTLDG Short Load Threshold",
+ tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_SL_THRESH]),
+ SOC_RTLDG_IMP_RO("CH1 RTLDG Impedance", TAS675X_CH1_RTLDG_IMP_MSB_REG),
+ SOC_RTLDG_IMP_RO("CH2 RTLDG Impedance", TAS675X_CH2_RTLDG_IMP_MSB_REG),
+ SOC_RTLDG_IMP_RO("CH3 RTLDG Impedance", TAS675X_CH3_RTLDG_IMP_MSB_REG),
+ SOC_RTLDG_IMP_RO("CH4 RTLDG Impedance", TAS675X_CH4_RTLDG_IMP_MSB_REG),
+};
+
+static const struct snd_kcontrol_new tas675x_audio_path_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1);
+
+static const struct snd_kcontrol_new tas675x_anc_path_switch =
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1);
+
+static const struct snd_soc_dapm_widget tas675x_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Analog Core", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDOUT Vpredict", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("SDOUT Isense", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_DAC("Audio DAC", "Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC("ANC DAC", "ANC Playback", SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_ADC("Feedback ADC", "Feedback Capture", SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_SWITCH("Audio Path", SND_SOC_NOPM, 0, 0,
+ &tas675x_audio_path_switch),
+ SND_SOC_DAPM_SWITCH("ANC Path", SND_SOC_NOPM, 0, 0,
+ &tas675x_anc_path_switch),
+
+ /*
+ * Even though all channels are coupled in terms of power control,
+ * use logical outputs for each channel to allow independent routing
+ * and DAPM controls if needed.
+ */
+ SND_SOC_DAPM_OUTPUT("OUT_CH1"),
+ SND_SOC_DAPM_OUTPUT("OUT_CH2"),
+ SND_SOC_DAPM_OUTPUT("OUT_CH3"),
+ SND_SOC_DAPM_OUTPUT("OUT_CH4"),
+ SND_SOC_DAPM_INPUT("SPEAKER_LOAD"),
+};
+
+static const struct snd_soc_dapm_route tas675x_dapm_routes[] = {
+ { "Audio DAC", NULL, "Analog Core" },
+ { "Audio Path", "Switch", "Audio DAC" },
+ { "OUT_CH1", NULL, "Audio Path" },
+ { "OUT_CH2", NULL, "Audio Path" },
+ { "OUT_CH3", NULL, "Audio Path" },
+ { "OUT_CH4", NULL, "Audio Path" },
+
+ { "ANC DAC", NULL, "Analog Core" },
+ { "ANC Path", "Switch", "ANC DAC" },
+ { "OUT_CH1", NULL, "ANC Path" },
+ { "OUT_CH2", NULL, "ANC Path" },
+ { "OUT_CH3", NULL, "ANC Path" },
+ { "OUT_CH4", NULL, "ANC Path" },
+
+ { "Feedback ADC", NULL, "Analog Core" },
+ { "Feedback ADC", NULL, "SDOUT Vpredict" },
+ { "Feedback ADC", NULL, "SDOUT Isense" },
+ { "Feedback ADC", NULL, "SPEAKER_LOAD" },
+};
+
+static void tas675x_program_slot_offsets(struct tas675x_priv *tas,
+ int dai_id, int slot_width)
+{
+ int offset = 0;
+
+ switch (dai_id) {
+ case 0:
+ /* Standard Audio on SDIN */
+ if (tas->audio_slot >= 0)
+ offset = tas->audio_slot * slot_width;
+ else if (tas->tx_mask)
+ offset = __ffs(tas->tx_mask) * slot_width;
+ else
+ return;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG,
+ TAS675X_SDIN_AUDIO_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDIN_AUDIO_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_SDIN_AUDIO_OFFSET_REG,
+ offset & 0xFF);
+ break;
+ case 1:
+ /*
+ * Low-Latency Playback on SDIN, **only** enabled in LLP mode
+ * and to be mixed with main audio before output amplification
+ * to achieve ANC/RNC.
+ */
+ if (tas->llp_slot >= 0)
+ offset = tas->llp_slot * slot_width;
+ else if (tas->tx_mask)
+ offset = __ffs(tas->tx_mask) * slot_width;
+ else
+ return;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG,
+ TAS675X_SDIN_LL_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDIN_LL_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_SDIN_LL_OFFSET_REG,
+ offset & 0xFF);
+ break;
+ case 2:
+ /* SDOUT Data Output (Vpredict + Isense feedback) */
+ if (!tas->slot_width)
+ break;
+ if (tas->vpredict_slot >= 0) {
+ offset = tas->vpredict_slot * slot_width;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG,
+ TAS675X_SDOUT_VP_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDOUT_VP_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_VPREDICT_OFFSET_REG,
+ offset & 0xFF);
+ }
+ if (tas->isense_slot >= 0) {
+ offset = tas->isense_slot * slot_width;
+ offset += tas->bclk_offset;
+ regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG,
+ TAS675X_SDOUT_IS_OFF_MSB_MASK,
+ FIELD_PREP(TAS675X_SDOUT_IS_OFF_MSB_MASK, offset >> 8));
+ regmap_write(tas->regmap, TAS675X_ISENSE_OFFSET_REG,
+ offset & 0xFF);
+ }
+ break;
+ }
+
+ if (offset > 511)
+ dev_warn(tas->dev,
+ "DAI %d slot offset %d exceeds 511 SCLK limit\n",
+ dai_id, offset);
+}
+
+static int tas675x_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 tas675x_priv *tas = snd_soc_component_get_drvdata(component);
+ unsigned int rate = params_rate(params);
+ u8 word_length;
+
+ /*
+ * Single clock domain: SDIN and SDOUT share one SCLK/FSYNC pair,
+ * so all active DAIs must use the same sample rate.
+ */
+ if ((READ_ONCE(tas->active_playback_dais) || READ_ONCE(tas->active_capture_dais)) &&
+ tas->rate && tas->rate != rate) {
+ dev_err(component->dev,
+ "Rate %u conflicts with active rate %u\n",
+ rate, tas->rate);
+ return -EINVAL;
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ word_length = TAS675X_WL_16BIT;
+ break;
+ case 20:
+ word_length = TAS675X_WL_20BIT;
+ break;
+ case 24:
+ word_length = TAS675X_WL_24BIT;
+ break;
+ case 32:
+ word_length = TAS675X_WL_32BIT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ /*
+ * RTLDG is not supported above 96kHz. Auto-disable to
+ * prevent DSP overload and restore when rate drops back.
+ */
+ if (rate > 96000) {
+ unsigned int val;
+
+ regmap_read(component->regmap, TAS675X_RTLDG_EN_REG,
+ &val);
+ if (val & TAS675X_RTLDG_CH_EN_MASK) {
+ tas->saved_rtldg_en = val;
+ dev_dbg(component->dev,
+ "Sample rate %dHz > 96kHz: Auto-disabling RTLDG\n",
+ rate);
+ regmap_update_bits(component->regmap,
+ TAS675X_RTLDG_EN_REG,
+ TAS675X_RTLDG_CH_EN_MASK,
+ 0x00);
+ }
+ } else if (tas->saved_rtldg_en) {
+ unsigned int cur;
+
+ /*
+ * Respect overrides and only restore if RTLDG is still auto-disabled
+ */
+ regmap_read(component->regmap, TAS675X_RTLDG_EN_REG,
+ &cur);
+ if (!(cur & TAS675X_RTLDG_CH_EN_MASK)) {
+ dev_dbg(component->dev,
+ "Restoring RTLDG config after high-rate stream\n");
+ regmap_update_bits(component->regmap,
+ TAS675X_RTLDG_EN_REG,
+ TAS675X_RTLDG_CH_EN_MASK,
+ TAS675X_RTLDG_CH_EN_MASK &
+ tas->saved_rtldg_en);
+ }
+ tas->saved_rtldg_en = 0;
+ }
+
+ /* Set SDIN word length (audio path + low-latency path) */
+ regmap_update_bits(component->regmap, TAS675X_SDIN_CTRL_REG,
+ TAS675X_SDIN_WL_MASK,
+ FIELD_PREP(TAS675X_SDIN_AUDIO_WL_MASK, word_length) |
+ FIELD_PREP(TAS675X_SDIN_LL_WL_MASK, word_length));
+ } else {
+ /* Set SDOUT word length (VPREDICT + ISENSE) for capture */
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_WL_MASK,
+ FIELD_PREP(TAS675X_SDOUT_VP_WL_MASK, word_length) |
+ FIELD_PREP(TAS675X_SDOUT_IS_WL_MASK, word_length));
+ }
+
+ tas675x_program_slot_offsets(tas, dai->id,
+ tas->slot_width ?: params_width(params));
+
+ tas->rate = rate;
+
+ return 0;
+}
+
+static int tas675x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
+ bool tdm_mode = false, i2s_mode = false;
+
+ /* Enforce Clocking Direction (Codec is strictly a consumer) */
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_BC_FC:
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock provider format\n");
+ return -EINVAL;
+ }
+
+ /* SCLK polarity: NB_NF or IB_NF only (no FSYNC inversion support) */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG,
+ TAS675X_SCLK_INV_MASK, 0x00);
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG,
+ TAS675X_SCLK_INV_MASK, TAS675X_SCLK_INV_MASK);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported clock inversion\n");
+ return -EINVAL;
+ }
+
+ /* Configure Audio Format and TDM Enable */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ i2s_mode = true;
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_SAP_FMT_I2S);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_NON_TDM);
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_SAP_FMT_RIGHT_J);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_NON_TDM);
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_SAP_FMT_LEFT_J);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_NON_TDM);
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ tdm_mode = true;
+ tas->bclk_offset = 1;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM |
+ TAS675X_FS_PULSE_SHORT);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_TDM_SDOUT1);
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ tdm_mode = true;
+ tas->bclk_offset = 0;
+ regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK |
+ TAS675X_FS_PULSE_MASK,
+ TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM |
+ TAS675X_FS_PULSE_SHORT);
+ regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG,
+ TAS675X_SDOUT_SELECT_MASK,
+ TAS675X_SDOUT_SELECT_TDM_SDOUT1);
+ break;
+ default:
+ dev_err(component->dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ /* Setup Vpredict and Isense outputs */
+ if (dai->id == 2) {
+ unsigned int sdout_en = 0;
+
+ if (tdm_mode) {
+ /* TDM: Vpredict and Isense may coexist on separate slots */
+ if (tas->vpredict_slot >= 0)
+ sdout_en |= TAS675X_SDOUT_EN_VPREDICT;
+ if (tas->isense_slot >= 0)
+ sdout_en |= TAS675X_SDOUT_EN_ISENSE;
+ regmap_update_bits(component->regmap,
+ TAS675X_SDOUT_EN_REG,
+ TAS675X_SDOUT_EN_VPREDICT |
+ TAS675X_SDOUT_EN_ISENSE,
+ sdout_en);
+ if (tas->vpredict_slot >= 0 && tas->isense_slot >= 0 &&
+ abs(tas->vpredict_slot - tas->isense_slot) < 4)
+ dev_warn(component->dev,
+ "ti,vpredict-slot-no and ti,isense-slot-no overlaps (each occupies 4 consecutive slots)\n");
+ } else if (i2s_mode) {
+ /* I2S: only one source at a time; Vpredict takes priority */
+ if (tas->vpredict_slot >= 0)
+ sdout_en = TAS675X_SDOUT_NON_TDM_SEL_VPREDICT |
+ TAS675X_SDOUT_EN_NON_TDM_ALL;
+ else if (tas->isense_slot >= 0)
+ sdout_en = TAS675X_SDOUT_NON_TDM_SEL_ISENSE |
+ TAS675X_SDOUT_EN_NON_TDM_ALL;
+ regmap_update_bits(component->regmap,
+ TAS675X_SDOUT_EN_REG,
+ TAS675X_SDOUT_NON_TDM_SEL_MASK |
+ TAS675X_SDOUT_EN_NON_TDM_ALL,
+ sdout_en);
+ if (sdout_en &&
+ tas->gpio1_func != TAS675X_GPIO_SEL_SDOUT2 &&
+ tas->gpio2_func != TAS675X_GPIO_SEL_SDOUT2)
+ dev_warn(component->dev,
+ "sdout enabled in I2S mode but no GPIO configured as SDOUT2; Ch3/Ch4 will be absent\n");
+ }
+ }
+
+ return 0;
+}
+
+static int tas675x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(dai->component);
+
+ if (slots == 0) {
+ tas->slot_width = 0;
+ tas->tx_mask = 0;
+ return 0;
+ }
+
+ /* No rx_mask as hardware does not support channel muxing for capture */
+ tas->slot_width = slot_width;
+ tas->tx_mask = tx_mask;
+ return 0;
+}
+
+static int tas675x_mute_stream(struct snd_soc_dai *dai, int mute, int direction)
+{
+ struct snd_soc_component *component = dai->component;
+ struct tas675x_priv *tas = snd_soc_component_get_drvdata(component);
+ unsigned int discard;
+ int ret;
+
+ if (direction == SNDRV_PCM_STREAM_CAPTURE) {
+ if (mute)
+ clear_bit(dai->id, &tas->active_capture_dais);
+ else
+ set_bit(dai->id, &tas->active_capture_dais);
+ return 0;
+ }
+
+ /*
+ * Track which playback DAIs are active.
+ * The TAS675x has two playback DAIs (main audio and LLP).
+ * Only transition to SLEEP when ALL are muted.
+ */
+ if (mute)
+ clear_bit(dai->id, &tas->active_playback_dais);
+ else
+ set_bit(dai->id, &tas->active_playback_dais);
+
+ /* Last playback stream */
+ if (mute && !READ_ONCE(tas->active_playback_dais)) {
+ ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+ regmap_read(tas->regmap, TAS675X_CLK_FAULT_LATCHED_REG, &discard);
+ return ret;
+ }
+
+ return tas675x_set_state_all(tas,
+ READ_ONCE(tas->active_playback_dais) ?
+ TAS675X_STATE_PLAY_BOTH :
+ TAS675X_STATE_SLEEP_BOTH);
+}
+
+static const struct snd_soc_dai_ops tas675x_dai_ops = {
+ .hw_params = tas675x_hw_params,
+ .set_fmt = tas675x_set_fmt,
+ .set_tdm_slot = tas675x_set_tdm_slot,
+ .mute_stream = tas675x_mute_stream,
+};
+
+static struct snd_soc_dai_driver tas675x_dais[] = {
+ {
+ .name = "tas675x-audio",
+ .id = 0,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tas675x_dai_ops,
+ },
+ /* Only available when Low Latency Path (LLP) is enabled */
+ {
+ .name = "tas675x-anc",
+ .id = 1,
+ .playback = {
+ .stream_name = "ANC Playback",
+ .channels_min = 2,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tas675x_dai_ops,
+ },
+ {
+ .name = "tas675x-feedback",
+ .id = 2,
+ .capture = {
+ .stream_name = "Feedback Capture",
+ .channels_min = 2,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_48000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &tas675x_dai_ops,
+ }
+};
+
+/*
+ * Enable regulators and release hardware reset GPIOs.
+ * The device is not I2C-accessible until this returns.
+ */
+static int tas675x_hw_enable(struct tas675x_priv *tas)
+{
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(tas->supplies), tas->supplies);
+ if (ret) {
+ dev_err(tas->dev, "Failed to enable regulators: %d\n", ret);
+ return ret;
+ }
+
+ if (!IS_ERR(tas->vbat)) {
+ ret = regulator_enable(tas->vbat);
+ if (ret) {
+ dev_err(tas->dev, "Failed to enable vbat: %d\n", ret);
+ regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies);
+ return ret;
+ }
+ }
+
+ if (tas->pd_gpio && tas->stby_gpio) {
+ /*
+ * Independent Pin Control
+ * Deassert PD first to boot digital, then STBY for analog.
+ */
+ /* Min 4ms digital boot wait */
+ gpiod_set_value_cansleep(tas->pd_gpio, 0);
+ usleep_range(4000, 5000);
+
+ /* ~2ms analog stabilization */
+ gpiod_set_value_cansleep(tas->stby_gpio, 0);
+ usleep_range(2000, 3000);
+ } else if (tas->pd_gpio) {
+ /*
+ * Simultaneous Pin Release
+ * STBY tied to PD or hardwired HIGH.
+ */
+ /* 6ms wait for simultaneous release transition */
+ gpiod_set_value_cansleep(tas->pd_gpio, 0);
+ usleep_range(6000, 7000);
+ } else {
+ /*
+ * PD hardwired, device in DEEP_SLEEP.
+ * Digital core already booted, I2C active. Deassert STBY
+ * to bring up the analog output stage.
+ */
+ /* ~2ms analog stabilization */
+ gpiod_set_value_cansleep(tas->stby_gpio, 0);
+ usleep_range(2000, 3000);
+ }
+
+ return 0;
+}
+
+static void tas675x_hw_disable(struct tas675x_priv *tas)
+{
+ if (tas->stby_gpio)
+ gpiod_set_value_cansleep(tas->stby_gpio, 1);
+
+ if (tas->pd_gpio)
+ gpiod_set_value_cansleep(tas->pd_gpio, 1);
+
+ /*
+ * Hold PD/STBY asserted for at least 10ms
+ * before removing PVDD, VBAT or DVDD.
+ */
+ usleep_range(10000, 11000);
+
+ if (!IS_ERR(tas->vbat))
+ regulator_disable(tas->vbat);
+
+ regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies);
+}
+
+/*
+ * Write device start-up defaults.
+ * Must be called after tas675x_hw_enable() and after regcache is enabled.
+ */
+static int tas675x_init_device(struct tas675x_priv *tas)
+{
+ struct regmap *regmap = tas->regmap;
+ unsigned int val;
+ int ret, i;
+
+ /* Clear POR fault flag to prevent IRQ storm */
+ regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val);
+
+ /* Bypass DC Load Diagnostics for fast boot */
+ if (tas->fast_boot)
+ regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT);
+
+ tas675x_select_book(regmap, TAS675X_BOOK_DEFAULT);
+
+ /* Enter setup mode */
+ ret = regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_ENTER_VAL1);
+ if (ret)
+ goto err;
+ ret = regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_ENTER_VAL2);
+ if (ret)
+ goto err;
+
+ /* Set all channels to Sleep (required before Page 1 config) */
+ tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+
+ /* Set DAC clock per TRM startup script */
+ regmap_write(regmap, TAS675X_DAC_CLK_REG, 0x00);
+
+ /*
+ * Switch to Page 1 for safety-critical OC/CBC configuration,
+ * while bypassing regcache. (Page 1 not accessible post setup)
+ */
+ regcache_cache_bypass(regmap, true);
+ ret = regmap_multi_reg_write(regmap, tas675x_page1_init,
+ ARRAY_SIZE(tas675x_page1_init));
+ regcache_cache_bypass(regmap, false);
+ if (ret)
+ goto err_setup;
+
+ /* Resync regmap's cached page selector */
+ regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00);
+
+ /* Exit setup mode */
+ regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL);
+ regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL);
+
+ /* Write DSP parameters if cached */
+ for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) {
+ if (tas->dsp_params[i].val)
+ tas675x_dsp_mem_write(tas,
+ tas->dsp_params[i].page,
+ tas->dsp_params[i].reg,
+ tas->dsp_params[i].val);
+ }
+
+ /*
+ * Configure fault and warning event routing:
+ *
+ * ROUTING_1: CP fault/UVLO latch, OUTM soft short latch
+ * ROUTING_2: CBC latch, OTSD latch, OTSD, power fault
+ * ROUTING_3: CBC latch, OTSD latch, power latch, DC LDG,
+ * OTSD, power warnings
+ * ROUTING_4: OC latch, DC latch, protection shutdown
+ * OTW latch, OTW, clip latch
+ * ROUTING_5: clock latch+non-latch, RTLDG latch
+ * CBC warning, clip warning
+ */
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_1_REG, 0x70);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_2_REG, 0xA3);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_3_REG, 0xBB);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_4_REG, 0x7E);
+ regmap_write(regmap, TAS675X_REPORT_ROUTING_5_REG, 0xF3);
+
+ /* Configure GPIO pins if specified in DT */
+ if (tas->gpio1_func >= 0 || tas->gpio2_func >= 0) {
+ unsigned int gpio_ctrl = TAS675X_GPIO_CTRL_RSTVAL;
+
+ tas675x_config_gpio_pin(regmap, tas->gpio1_func,
+ TAS675X_GPIO1_OUTPUT_SEL_REG,
+ 0, &gpio_ctrl);
+ tas675x_config_gpio_pin(regmap, tas->gpio2_func,
+ TAS675X_GPIO2_OUTPUT_SEL_REG,
+ 1, &gpio_ctrl);
+ regmap_write(regmap, TAS675X_GPIO_CTRL_REG, gpio_ctrl);
+ }
+
+ /* Clear fast boot bits */
+ if (tas->fast_boot)
+ regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG,
+ TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT,
+ 0);
+
+ /* Clear any stale faults from the boot sequence */
+ regmap_read(regmap, TAS675X_POWER_FAULT_STATUS_1_REG, &val);
+ regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val);
+ regmap_read(regmap, TAS675X_CLK_FAULT_LATCHED_REG, &val);
+ regmap_write(regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+
+ return 0;
+
+err_setup:
+ regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL);
+ regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL);
+err:
+ dev_err(tas->dev, "Init device failed: %d\n", ret);
+ return ret;
+}
+
+static void tas675x_power_off(struct tas675x_priv *tas)
+{
+ regcache_cache_only(tas->regmap, true);
+ regcache_mark_dirty(tas->regmap);
+ tas675x_hw_disable(tas);
+}
+
+static int tas675x_power_on(struct tas675x_priv *tas)
+{
+ int ret;
+
+ ret = tas675x_hw_enable(tas);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(tas->regmap, false);
+ regcache_mark_dirty(tas->regmap);
+
+ ret = tas675x_init_device(tas);
+ if (ret)
+ goto err_disable;
+
+ ret = regcache_sync(tas->regmap);
+ if (ret) {
+ dev_err(tas->dev, "Failed to sync regcache: %d\n", ret);
+ goto err_disable;
+ }
+
+ /* Reset fault tracking */
+ memset(tas->last_status, 0, sizeof(tas->last_status));
+
+ return 0;
+
+err_disable:
+ tas675x_power_off(tas);
+ return ret;
+}
+
+static int tas675x_runtime_suspend(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+
+ disable_delayed_work_sync(&tas->fault_check_work);
+ tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+
+ return 0;
+}
+
+static int tas675x_runtime_resume(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+
+ tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH);
+
+ if (!to_i2c_client(dev)->irq) {
+ enable_delayed_work(&tas->fault_check_work);
+ schedule_delayed_work(&tas->fault_check_work,
+ msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
+ }
+
+ return 0;
+}
+
+static int tas675x_system_suspend(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+ int ret;
+
+ ret = tas675x_runtime_suspend(dev);
+ if (ret)
+ return ret;
+
+ if (to_i2c_client(dev)->irq)
+ disable_irq(to_i2c_client(dev)->irq);
+
+ tas675x_power_off(tas);
+ return 0;
+}
+
+static int tas675x_system_resume(struct device *dev)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(dev);
+ int ret;
+
+ ret = tas675x_power_on(tas);
+ if (ret)
+ return ret;
+
+ if (to_i2c_client(dev)->irq)
+ enable_irq(to_i2c_client(dev)->irq);
+
+ return tas675x_runtime_resume(dev);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_tas675x = {
+ .controls = tas675x_snd_controls,
+ .num_controls = ARRAY_SIZE(tas675x_snd_controls),
+ .dapm_widgets = tas675x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas675x_dapm_widgets),
+ .dapm_routes = tas675x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas675x_dapm_routes),
+ .endianness = 1,
+};
+
+/* Fault register flags */
+#define TAS675X_FAULT_CRITICAL BIT(0) /* causes FAULT state, FAULT_CLEAR required */
+#define TAS675X_FAULT_TRACK BIT(1) /* track last value, only log on change */
+#define TAS675X_FAULT_ACTIVE BIT(2) /* skip when no stream is active */
+
+struct tas675x_fault_reg {
+ unsigned int reg;
+ unsigned int flags;
+ const char *name;
+};
+
+static const struct tas675x_fault_reg tas675x_fault_table[] = {
+ /* Critical */
+ { TAS675X_OTSD_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "Overtemperature Shutdown" },
+ { TAS675X_OC_DC_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "Overcurrent / DC Fault" },
+ { TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "Real-Time Load Diagnostic Fault" },
+ { TAS675X_CBC_FAULT_WARN_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK,
+ "CBC Fault/Warning" },
+ /* Warning */
+ { TAS675X_POWER_FAULT_STATUS_1_REG, TAS675X_FAULT_TRACK,
+ "CP / OUTM Fault" },
+ { TAS675X_POWER_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK,
+ "Power Fault" },
+ { TAS675X_CLK_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK | TAS675X_FAULT_ACTIVE,
+ "Clock Fault" },
+ { TAS675X_OTW_LATCHED_REG, TAS675X_FAULT_TRACK,
+ "Overtemperature Warning" },
+ { TAS675X_CLIP_WARN_LATCHED_REG, TAS675X_FAULT_ACTIVE,
+ "Clip Warning" },
+};
+
+static_assert(ARRAY_SIZE(tas675x_fault_table) == TAS675X_FAULT_REGS_NUM);
+
+/*
+ * Read and log all latched fault registers.
+ * Shared by both the polled fault_check_work and IRQ handler paths
+ * (which are mutually exclusive, only one is active per device).
+ * Returns true if any fault register needs to be cleared.
+ *
+ * For deciphering fault messages, see "Fault Monitoring" in
+ * Documentation/sound/codecs/tas675x.rst
+ */
+static bool tas675x_check_faults(struct tas675x_priv *tas)
+{
+ struct device *dev = tas->dev;
+ bool needs_clear = false;
+ unsigned int reg;
+ int i, ret;
+
+ for (i = 0; i < ARRAY_SIZE(tas675x_fault_table); i++) {
+ const struct tas675x_fault_reg *f = &tas675x_fault_table[i];
+
+ ret = regmap_read(tas->regmap, f->reg, &reg);
+ if (ret) {
+ if (f->flags & TAS675X_FAULT_CRITICAL) {
+ dev_err(dev, "failed to read %s: %d\n", f->name, ret);
+ return needs_clear;
+ }
+ continue;
+ }
+
+ if (reg)
+ needs_clear = true;
+
+ /* Skip logging stream-dependent events when no stream is active */
+ if ((f->flags & TAS675X_FAULT_ACTIVE) &&
+ !READ_ONCE(tas->active_playback_dais) &&
+ !READ_ONCE(tas->active_capture_dais))
+ continue;
+
+ /* Log on change or on every non-zero read */
+ if (reg && (!(f->flags & TAS675X_FAULT_TRACK) ||
+ reg != tas->last_status[i])) {
+ if (f->flags & TAS675X_FAULT_CRITICAL)
+ dev_crit(dev, "%s Latched: 0x%02x\n", f->name, reg);
+ else
+ dev_warn(dev, "%s Latched: 0x%02x\n", f->name, reg);
+ }
+
+ if (f->flags & TAS675X_FAULT_TRACK)
+ tas->last_status[i] = reg;
+ }
+
+ return needs_clear;
+}
+
+static void tas675x_fault_check_work(struct work_struct *work)
+{
+ struct tas675x_priv *tas = container_of(work, struct tas675x_priv,
+ fault_check_work.work);
+
+ if (tas675x_check_faults(tas))
+ regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+
+ schedule_delayed_work(&tas->fault_check_work,
+ msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
+}
+
+static irqreturn_t tas675x_irq_handler(int irq, void *data)
+{
+ struct tas675x_priv *tas = data;
+ irqreturn_t ret = IRQ_NONE;
+
+ if (pm_runtime_resume_and_get(tas->dev) < 0)
+ return IRQ_NONE;
+
+ if (tas675x_check_faults(tas)) {
+ regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR);
+ ret = IRQ_HANDLED;
+ }
+
+ pm_runtime_mark_last_busy(tas->dev);
+ pm_runtime_put_autosuspend(tas->dev);
+ return ret;
+}
+
+static const struct reg_default tas675x_reg_defaults[] = {
+ { TAS675X_PAGE_CTRL_REG, 0x00 },
+ { TAS675X_OUTPUT_CTRL_REG, 0x00 },
+ { TAS675X_STATE_CTRL_CH1_CH2_REG, TAS675X_STATE_SLEEP_BOTH },
+ { TAS675X_STATE_CTRL_CH3_CH4_REG, TAS675X_STATE_SLEEP_BOTH },
+ { TAS675X_ISENSE_CTRL_REG, 0x0F },
+ { TAS675X_DC_DETECT_CTRL_REG, 0x00 },
+ { TAS675X_SCLK_INV_CTRL_REG, 0x00 },
+ { TAS675X_AUDIO_IF_CTRL_REG, 0x00 },
+ { TAS675X_SDIN_CTRL_REG, 0x0A },
+ { TAS675X_SDOUT_CTRL_REG, 0x1A },
+ { TAS675X_SDIN_OFFSET_MSB_REG, 0x00 },
+ { TAS675X_SDIN_AUDIO_OFFSET_REG, 0x00 },
+ { TAS675X_SDIN_LL_OFFSET_REG, 0x60 },
+ { TAS675X_SDIN_CH_SWAP_REG, 0x00 },
+ { TAS675X_SDOUT_OFFSET_MSB_REG, 0xCF },
+ { TAS675X_VPREDICT_OFFSET_REG, 0xFF },
+ { TAS675X_ISENSE_OFFSET_REG, 0x00 },
+ { TAS675X_SDOUT_EN_REG, 0x00 },
+ { TAS675X_LL_EN_REG, 0x00 },
+ { TAS675X_RTLDG_EN_REG, 0x10 },
+ { TAS675X_DC_BLOCK_BYP_REG, 0x00 },
+ { TAS675X_DSP_CTRL_REG, 0x00 },
+ { TAS675X_PAGE_AUTO_INC_REG, 0x00 },
+ { TAS675X_DIG_VOL_CH1_REG, 0x30 },
+ { TAS675X_DIG_VOL_CH2_REG, 0x30 },
+ { TAS675X_DIG_VOL_CH3_REG, 0x30 },
+ { TAS675X_DIG_VOL_CH4_REG, 0x30 },
+ { TAS675X_DIG_VOL_RAMP_CTRL_REG, 0x77 },
+ { TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0x00 },
+ { TAS675X_AUTO_MUTE_EN_REG, 0x00 },
+ { TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0x00 },
+ { TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0x00 },
+ { TAS675X_ANALOG_GAIN_CH1_CH2_REG, 0x00 },
+ { TAS675X_ANALOG_GAIN_CH3_CH4_REG, 0x00 },
+ { TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 0x00 },
+ { TAS675X_PULSE_INJECTION_EN_REG, 0x03 },
+ { TAS675X_CBC_CTRL_REG, 0x07 },
+ { TAS675X_CURRENT_LIMIT_CTRL_REG, 0x00 },
+ { TAS675X_ISENSE_CAL_REG, 0x00 },
+ { TAS675X_PWM_PHASE_CTRL_REG, 0x00 },
+ { TAS675X_SS_CTRL_REG, 0x00 },
+ { TAS675X_SS_RANGE_CTRL_REG, 0x00 },
+ { TAS675X_SS_DWELL_CTRL_REG, 0x00 },
+ { TAS675X_RAMP_PHASE_CTRL_GPO_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH1_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 },
+ { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 },
+ { TAS675X_DC_LDG_CTRL_REG, 0x00 },
+ { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 },
+ { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 },
+ { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 },
+ { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 },
+ { TAS675X_AC_LDG_CTRL_REG, 0x10 },
+ { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 },
+ { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 },
+ { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 },
+ { TAS675X_REPORT_ROUTING_1_REG, 0x00 },
+ { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 },
+ { TAS675X_REPORT_ROUTING_2_REG, 0xA2 },
+ { TAS675X_REPORT_ROUTING_3_REG, 0x00 },
+ { TAS675X_REPORT_ROUTING_4_REG, 0x06 },
+ { TAS675X_CLIP_DETECT_CTRL_REG, 0x00 },
+ { TAS675X_REPORT_ROUTING_5_REG, 0x00 },
+ { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 },
+ { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 },
+ { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL },
+ { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 },
+ { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 },
+};
+
+static bool tas675x_is_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS675X_RESET_REG:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static bool tas675x_is_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS675X_RESET_REG:
+ case TAS675X_BOOK_CTRL_REG:
+ case TAS675X_AUTO_MUTE_STATUS_REG:
+ case TAS675X_STATE_REPORT_CH1_CH2_REG:
+ case TAS675X_STATE_REPORT_CH3_CH4_REG:
+ case TAS675X_PVDD_SENSE_REG:
+ case TAS675X_TEMP_GLOBAL_REG:
+ case TAS675X_TEMP_CH1_CH2_REG:
+ case TAS675X_TEMP_CH3_CH4_REG:
+ case TAS675X_FS_MON_REG:
+ case TAS675X_SCLK_MON_REG:
+ case TAS675X_POWER_FAULT_STATUS_1_REG:
+ case TAS675X_POWER_FAULT_STATUS_2_REG:
+ case TAS675X_OT_FAULT_REG:
+ case TAS675X_OTW_STATUS_REG:
+ case TAS675X_CLIP_WARN_STATUS_REG:
+ case TAS675X_CBC_WARNING_STATUS_REG:
+ case TAS675X_POWER_FAULT_LATCHED_REG:
+ case TAS675X_OTSD_LATCHED_REG:
+ case TAS675X_OTW_LATCHED_REG:
+ case TAS675X_CLIP_WARN_LATCHED_REG:
+ case TAS675X_CLK_FAULT_LATCHED_REG:
+ case TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG:
+ case TAS675X_CBC_FAULT_WARN_LATCHED_REG:
+ case TAS675X_OC_DC_FAULT_LATCHED_REG:
+ case TAS675X_WARN_OT_MAX_FLAG_REG:
+ case TAS675X_DC_LDG_REPORT_CH1_CH2_REG ... TAS675X_TWEETER_REPORT_REG:
+ case TAS675X_CH1_RTLDG_IMP_MSB_REG ... TAS675X_CH4_DC_LDG_DCR_LSB_REG:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range_cfg tas675x_ranges[] = {
+ {
+ .name = "Pages",
+ .range_min = 0,
+ .range_max = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1,
+ .selector_reg = TAS675X_PAGE_CTRL_REG,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = TAS675X_PAGE_SIZE,
+ },
+};
+
+static void tas675x_regmap_lock(void *lock_arg)
+{
+ struct tas675x_priv *tas = lock_arg;
+
+ mutex_lock(&tas->io_lock);
+}
+
+static void tas675x_regmap_unlock(void *lock_arg)
+{
+ struct tas675x_priv *tas = lock_arg;
+
+ mutex_unlock(&tas->io_lock);
+}
+
+static const struct regmap_config tas675x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1,
+ .ranges = tas675x_ranges,
+ .num_ranges = ARRAY_SIZE(tas675x_ranges),
+ .cache_type = REGCACHE_MAPLE,
+ .reg_defaults = tas675x_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas675x_reg_defaults),
+ .readable_reg = tas675x_is_readable_register,
+ .volatile_reg = tas675x_is_volatile_register,
+};
+
+static int tas675x_i2c_probe(struct i2c_client *client)
+{
+ struct regmap_config cfg = tas675x_regmap_config;
+ struct tas675x_priv *tas;
+ u32 val;
+ int i, ret;
+
+ tas = devm_kzalloc(&client->dev, sizeof(*tas), GFP_KERNEL);
+ if (!tas)
+ return -ENOMEM;
+
+ tas->dev = &client->dev;
+ i2c_set_clientdata(client, tas);
+
+ mutex_init(&tas->io_lock);
+ cfg.lock = tas675x_regmap_lock;
+ cfg.unlock = tas675x_regmap_unlock;
+ cfg.lock_arg = tas;
+
+ memcpy(tas->dsp_params, tas675x_dsp_defaults, sizeof(tas->dsp_params));
+ INIT_DELAYED_WORK(&tas->fault_check_work, tas675x_fault_check_work);
+
+ tas->regmap = devm_regmap_init_i2c(client, &cfg);
+ if (IS_ERR(tas->regmap))
+ return PTR_ERR(tas->regmap);
+
+ /* Keep regmap cache-only until hardware is powered on */
+ regcache_cache_only(tas->regmap, true);
+
+ tas->dev_type = (enum tas675x_type)(unsigned long)device_get_match_data(tas->dev);
+ tas->fast_boot = device_property_read_bool(tas->dev, "ti,fast-boot");
+
+ tas->audio_slot = -1;
+ tas->llp_slot = -1;
+ tas->vpredict_slot = -1;
+ tas->isense_slot = -1;
+ if (!device_property_read_u32(tas->dev, "ti,audio-slot-no", &val))
+ tas->audio_slot = val;
+ if (!device_property_read_u32(tas->dev, "ti,llp-slot-no", &val))
+ tas->llp_slot = val;
+ if (!device_property_read_u32(tas->dev, "ti,vpredict-slot-no", &val))
+ tas->vpredict_slot = val;
+ if (!device_property_read_u32(tas->dev, "ti,isense-slot-no", &val))
+ tas->isense_slot = val;
+
+ tas->gpio1_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio1-function");
+ tas->gpio2_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio2-function");
+
+ for (i = 0; i < ARRAY_SIZE(tas675x_supply_names); i++)
+ tas->supplies[i].supply = tas675x_supply_names[i];
+
+ ret = devm_regulator_bulk_get(tas->dev, ARRAY_SIZE(tas->supplies), tas->supplies);
+ if (ret)
+ return dev_err_probe(tas->dev, ret, "Failed to request supplies\n");
+
+ tas->vbat = devm_regulator_get_optional(tas->dev, "vbat");
+ if (IS_ERR(tas->vbat) && PTR_ERR(tas->vbat) != -ENODEV)
+ return dev_err_probe(tas->dev, PTR_ERR(tas->vbat),
+ "Failed to get vbat supply\n");
+
+ tas->pd_gpio = devm_gpiod_get_optional(tas->dev, "powerdown", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas->pd_gpio))
+ return dev_err_probe(tas->dev, PTR_ERR(tas->pd_gpio), "Failed powerdown-gpios\n");
+
+ tas->stby_gpio = devm_gpiod_get_optional(tas->dev, "standby", GPIOD_OUT_HIGH);
+ if (IS_ERR(tas->stby_gpio))
+ return dev_err_probe(tas->dev, PTR_ERR(tas->stby_gpio), "Failed standby-gpios\n");
+
+ if (!tas->pd_gpio && !tas->stby_gpio)
+ return dev_err_probe(tas->dev, -EINVAL,
+ "At least one of powerdown-gpios or standby-gpios is required\n");
+
+ ret = tas675x_power_on(tas);
+ if (ret)
+ return ret;
+
+ if (client->irq) {
+ ret = devm_request_threaded_irq(tas->dev, client->irq, NULL,
+ tas675x_irq_handler,
+ IRQF_ONESHOT | IRQF_TRIGGER_FALLING,
+ "tas675x-fault", tas);
+ if (ret) {
+ tas675x_power_off(tas);
+ return dev_err_probe(tas->dev, ret, "Failed to request IRQ\n");
+ }
+ } else {
+ /* Schedule delayed work for fault checking at probe and runtime resume */
+ schedule_delayed_work(&tas->fault_check_work,
+ msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS));
+ }
+
+ /* Enable runtime PM with 2s autosuspend */
+ pm_runtime_set_autosuspend_delay(tas->dev, 2000);
+ pm_runtime_use_autosuspend(tas->dev);
+ pm_runtime_set_active(tas->dev);
+ pm_runtime_mark_last_busy(tas->dev);
+ pm_runtime_enable(tas->dev);
+
+ ret = devm_snd_soc_register_component(tas->dev, &soc_codec_dev_tas675x,
+ tas675x_dais, ARRAY_SIZE(tas675x_dais));
+ if (ret)
+ goto err_pm_disable;
+
+ return 0;
+
+err_pm_disable:
+ pm_runtime_force_suspend(tas->dev);
+ pm_runtime_disable(tas->dev);
+ tas675x_power_off(tas);
+ return ret;
+}
+
+static void tas675x_i2c_remove(struct i2c_client *client)
+{
+ struct tas675x_priv *tas = dev_get_drvdata(&client->dev);
+
+ disable_delayed_work_sync(&tas->fault_check_work);
+ if (client->irq)
+ disable_irq(client->irq);
+
+ pm_runtime_force_suspend(&client->dev);
+ pm_runtime_disable(&client->dev);
+ tas675x_power_off(tas);
+}
+
+static const struct dev_pm_ops tas675x_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(tas675x_system_suspend, tas675x_system_resume)
+ RUNTIME_PM_OPS(tas675x_runtime_suspend, tas675x_runtime_resume, NULL)
+};
+
+static const struct of_device_id tas675x_of_match[] = {
+ { .compatible = "ti,tas67524", .data = (void *)TAS67524 },
+ { }
+};
+MODULE_DEVICE_TABLE(of, tas675x_of_match);
+
+static const struct i2c_device_id tas675x_i2c_id[] = {
+ { "tas67524", TAS67524 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, tas675x_i2c_id);
+
+static struct i2c_driver tas675x_i2c_driver = {
+ .driver = {
+ .name = "tas675x",
+ .of_match_table = tas675x_of_match,
+ .pm = pm_ptr(&tas675x_pm_ops),
+ },
+ .probe = tas675x_i2c_probe,
+ .remove = tas675x_i2c_remove,
+ .id_table = tas675x_i2c_id,
+};
+
+module_i2c_driver(tas675x_i2c_driver);
+
+MODULE_AUTHOR("Sen Wang <sen@ti.com>");
+MODULE_DESCRIPTION("ASoC TAS675x Audio Amplifier Driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tas675x.h b/sound/soc/codecs/tas675x.h
new file mode 100644
index 000000000000..db29bb377336
--- /dev/null
+++ b/sound/soc/codecs/tas675x.h
@@ -0,0 +1,367 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * ALSA SoC Texas Instruments TAS675x Quad-Channel Audio Amplifier
+ *
+ * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/
+ * Author: Sen Wang <sen@ti.com>
+ */
+
+#ifndef __TAS675X_H__
+#define __TAS675X_H__
+
+/*
+ * Book 0, Page 0 — Register Addresses
+ */
+
+#define TAS675X_PAGE_SIZE 256
+#define TAS675X_PAGE_REG(page, reg) ((page) * TAS675X_PAGE_SIZE + (reg))
+
+/* Page Control & Basic Config */
+#define TAS675X_PAGE_CTRL_REG 0x00
+#define TAS675X_RESET_REG 0x01
+#define TAS675X_OUTPUT_CTRL_REG 0x02
+#define TAS675X_STATE_CTRL_CH1_CH2_REG 0x03
+#define TAS675X_STATE_CTRL_CH3_CH4_REG 0x04
+#define TAS675X_ISENSE_CTRL_REG 0x05
+#define TAS675X_DC_DETECT_CTRL_REG 0x06
+
+/* Serial Audio Port */
+#define TAS675X_SCLK_INV_CTRL_REG 0x20
+#define TAS675X_AUDIO_IF_CTRL_REG 0x21
+#define TAS675X_SDIN_CTRL_REG 0x23
+#define TAS675X_SDOUT_CTRL_REG 0x25
+#define TAS675X_SDIN_OFFSET_MSB_REG 0x27
+#define TAS675X_SDIN_AUDIO_OFFSET_REG 0x28
+#define TAS675X_SDIN_LL_OFFSET_REG 0x29
+#define TAS675X_SDIN_CH_SWAP_REG 0x2A
+#define TAS675X_SDOUT_OFFSET_MSB_REG 0x2C
+#define TAS675X_VPREDICT_OFFSET_REG 0x2D
+#define TAS675X_ISENSE_OFFSET_REG 0x2E
+#define TAS675X_SDOUT_EN_REG 0x31
+#define TAS675X_LL_EN_REG 0x32
+
+/* DSP & Core Audio Control */
+#define TAS675X_RTLDG_EN_REG 0x37
+#define TAS675X_DC_BLOCK_BYP_REG 0x39
+#define TAS675X_DSP_CTRL_REG 0x3A
+#define TAS675X_PAGE_AUTO_INC_REG 0x3B
+
+/* Volume & Mute */
+#define TAS675X_DIG_VOL_CH1_REG 0x40
+#define TAS675X_DIG_VOL_CH2_REG 0x41
+#define TAS675X_DIG_VOL_CH3_REG 0x42
+#define TAS675X_DIG_VOL_CH4_REG 0x43
+#define TAS675X_DIG_VOL_RAMP_CTRL_REG 0x44
+#define TAS675X_DIG_VOL_COMBINE_CTRL_REG 0x46
+#define TAS675X_AUTO_MUTE_EN_REG 0x47
+#define TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG 0x48
+#define TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG 0x49
+
+/* Analog Gain & Power Stage */
+#define TAS675X_ANALOG_GAIN_CH1_CH2_REG 0x4A
+#define TAS675X_ANALOG_GAIN_CH3_CH4_REG 0x4B
+#define TAS675X_ANALOG_GAIN_RAMP_CTRL_REG 0x4E
+#define TAS675X_PULSE_INJECTION_EN_REG 0x52
+#define TAS675X_CBC_CTRL_REG 0x54
+#define TAS675X_CURRENT_LIMIT_CTRL_REG 0x55
+#define TAS675X_DAC_CLK_REG 0x5A
+#define TAS675X_ISENSE_CAL_REG 0x5B
+
+/* Spread Spectrum & PWM Phase */
+#define TAS675X_PWM_PHASE_CTRL_REG 0x60
+#define TAS675X_SS_CTRL_REG 0x61
+#define TAS675X_SS_RANGE_CTRL_REG 0x62
+#define TAS675X_SS_DWELL_CTRL_REG 0x66
+#define TAS675X_RAMP_PHASE_CTRL_GPO_REG 0x68
+#define TAS675X_PWM_PHASE_M_CTRL_CH1_REG 0x69
+#define TAS675X_PWM_PHASE_M_CTRL_CH2_REG 0x6A
+#define TAS675X_PWM_PHASE_M_CTRL_CH3_REG 0x6B
+#define TAS675X_PWM_PHASE_M_CTRL_CH4_REG 0x6C
+
+/* Status & Reporting */
+#define TAS675X_AUTO_MUTE_STATUS_REG 0x71
+#define TAS675X_STATE_REPORT_CH1_CH2_REG 0x72
+#define TAS675X_STATE_REPORT_CH3_CH4_REG 0x73
+#define TAS675X_PVDD_SENSE_REG 0x74
+#define TAS675X_TEMP_GLOBAL_REG 0x75
+#define TAS675X_FS_MON_REG 0x76
+#define TAS675X_SCLK_MON_REG 0x77
+#define TAS675X_REPORT_ROUTING_1_REG 0x7C
+
+/* Memory Paging & Book Control */
+#define TAS675X_SETUP_REG1 0x7D
+#define TAS675X_SETUP_REG2 0x7E
+#define TAS675X_BOOK_CTRL_REG 0x7F
+
+/* Fault Status */
+#define TAS675X_POWER_FAULT_STATUS_1_REG 0x7D
+#define TAS675X_POWER_FAULT_STATUS_2_REG 0x80
+#define TAS675X_OT_FAULT_REG 0x81
+#define TAS675X_OTW_STATUS_REG 0x82
+#define TAS675X_CLIP_WARN_STATUS_REG 0x83
+#define TAS675X_CBC_WARNING_STATUS_REG 0x85
+
+/* Latched Fault Registers */
+#define TAS675X_POWER_FAULT_LATCHED_REG 0x86
+#define TAS675X_OTSD_LATCHED_REG 0x87
+#define TAS675X_OTW_LATCHED_REG 0x88
+#define TAS675X_CLIP_WARN_LATCHED_REG 0x89
+#define TAS675X_CLK_FAULT_LATCHED_REG 0x8A
+#define TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG 0x8B
+#define TAS675X_CBC_FAULT_WARN_LATCHED_REG 0x8D
+#define TAS675X_OC_DC_FAULT_LATCHED_REG 0x8E
+#define TAS675X_OTSD_RECOVERY_EN_REG 0x8F
+
+/* Protection & Routing Controls */
+#define TAS675X_REPORT_ROUTING_2_REG 0x90
+#define TAS675X_REPORT_ROUTING_3_REG 0x91
+#define TAS675X_REPORT_ROUTING_4_REG 0x92
+#define TAS675X_CLIP_DETECT_CTRL_REG 0x93
+#define TAS675X_REPORT_ROUTING_5_REG 0x94
+
+/* GPIO Pin Configuration */
+#define TAS675X_GPIO1_OUTPUT_SEL_REG 0x95
+#define TAS675X_GPIO2_OUTPUT_SEL_REG 0x96
+#define TAS675X_GPIO_INPUT_SLEEP_HIZ_REG 0x9B
+#define TAS675X_GPIO_INPUT_PLAY_SLEEP_REG 0x9C
+#define TAS675X_GPIO_INPUT_MUTE_REG 0x9D
+#define TAS675X_GPIO_INPUT_SYNC_REG 0x9E
+#define TAS675X_GPIO_INPUT_SDIN2_REG 0x9F
+#define TAS675X_GPIO_CTRL_REG 0xA0
+#define TAS675X_GPIO_INVERT_REG 0xA1
+
+/* Load Diagnostics Config */
+#define TAS675X_DC_LDG_CTRL_REG 0xB0
+#define TAS675X_DC_LDG_LO_CTRL_REG 0xB1
+#define TAS675X_DC_LDG_TIME_CTRL_REG 0xB2
+#define TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG 0xB3
+#define TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG 0xB4
+#define TAS675X_AC_LDG_CTRL_REG 0xB5
+#define TAS675X_TWEETER_DETECT_CTRL_REG 0xB6
+#define TAS675X_TWEETER_DETECT_THRESH_REG 0xB7
+#define TAS675X_AC_LDG_FREQ_CTRL_REG 0xB8
+#define TAS675X_TEMP_CH1_CH2_REG 0xBB
+#define TAS675X_TEMP_CH3_CH4_REG 0xBC
+#define TAS675X_WARN_OT_MAX_FLAG_REG 0xBD
+
+/* DC Load Diagnostic Reports */
+#define TAS675X_DC_LDG_REPORT_CH1_CH2_REG 0xC0
+#define TAS675X_DC_LDG_REPORT_CH3_CH4_REG 0xC1
+#define TAS675X_DC_LDG_RESULT_REG 0xC2
+#define TAS675X_AC_LDG_REPORT_CH1_R_REG 0xC3
+#define TAS675X_AC_LDG_REPORT_CH1_I_REG 0xC4
+#define TAS675X_AC_LDG_REPORT_CH2_R_REG 0xC5
+#define TAS675X_AC_LDG_REPORT_CH2_I_REG 0xC6
+#define TAS675X_AC_LDG_REPORT_CH3_R_REG 0xC7
+#define TAS675X_AC_LDG_REPORT_CH3_I_REG 0xC8
+#define TAS675X_AC_LDG_REPORT_CH4_R_REG 0xC9
+#define TAS675X_AC_LDG_REPORT_CH4_I_REG 0xCA
+#define TAS675X_TWEETER_REPORT_REG 0xCB
+
+/* RTLDG Impedance */
+#define TAS675X_CH1_RTLDG_IMP_MSB_REG 0xD1
+#define TAS675X_CH1_RTLDG_IMP_LSB_REG 0xD2
+#define TAS675X_CH2_RTLDG_IMP_MSB_REG 0xD3
+#define TAS675X_CH2_RTLDG_IMP_LSB_REG 0xD4
+#define TAS675X_CH3_RTLDG_IMP_MSB_REG 0xD5
+#define TAS675X_CH3_RTLDG_IMP_LSB_REG 0xD6
+#define TAS675X_CH4_RTLDG_IMP_MSB_REG 0xD7
+#define TAS675X_CH4_RTLDG_IMP_LSB_REG 0xD8
+
+/* DC Load Diagnostic Resistance */
+#define TAS675X_DC_LDG_DCR_MSB_REG 0xD9
+#define TAS675X_CH1_DC_LDG_DCR_LSB_REG 0xDA
+#define TAS675X_CH2_DC_LDG_DCR_LSB_REG 0xDB
+#define TAS675X_CH3_DC_LDG_DCR_LSB_REG 0xDC
+#define TAS675X_CH4_DC_LDG_DCR_LSB_REG 0xDD
+
+/* Over-Temperature Warning */
+#define TAS675X_OTW_CTRL_CH1_CH2_REG 0xE2
+#define TAS675X_OTW_CTRL_CH3_CH4_REG 0xE3
+
+/* RESET_REG (all bits auto-clear) */
+#define TAS675X_DEVICE_RESET BIT(4)
+#define TAS675X_FAULT_CLEAR BIT(3)
+#define TAS675X_REGISTER_RESET BIT(0)
+
+/* STATE_CTRL and STATE_REPORT — Channel state values */
+#define TAS675X_STATE_DEEPSLEEP 0x00
+#define TAS675X_STATE_LOAD_DIAG 0x01
+#define TAS675X_STATE_SLEEP 0x02
+#define TAS675X_STATE_HIZ 0x03
+#define TAS675X_STATE_PLAY 0x04
+
+/* Additional STATE_REPORT values */
+#define TAS675X_STATE_FAULT 0x05
+#define TAS675X_STATE_AUTOREC 0x06
+
+/* Combined values for both channel pairs in one register */
+#define TAS675X_STATE_DEEPSLEEP_BOTH \
+ (TAS675X_STATE_DEEPSLEEP | (TAS675X_STATE_DEEPSLEEP << 4))
+#define TAS675X_STATE_LOAD_DIAG_BOTH \
+ (TAS675X_STATE_LOAD_DIAG | (TAS675X_STATE_LOAD_DIAG << 4))
+#define TAS675X_STATE_SLEEP_BOTH \
+ (TAS675X_STATE_SLEEP | (TAS675X_STATE_SLEEP << 4))
+#define TAS675X_STATE_HIZ_BOTH \
+ (TAS675X_STATE_HIZ | (TAS675X_STATE_HIZ << 4))
+#define TAS675X_STATE_PLAY_BOTH \
+ (TAS675X_STATE_PLAY | (TAS675X_STATE_PLAY << 4))
+#define TAS675X_STATE_FAULT_BOTH \
+ (TAS675X_STATE_FAULT | (TAS675X_STATE_FAULT << 4))
+
+/* STATE_CTRL_CH1_CH2 / STATE_CTRL_CH3_CH4 — mute bits */
+#define TAS675X_CH1_MUTE_BIT BIT(7)
+#define TAS675X_CH2_MUTE_BIT BIT(3)
+#define TAS675X_CH_MUTE_BOTH (TAS675X_CH1_MUTE_BIT | TAS675X_CH2_MUTE_BIT)
+
+/* SCLK_INV_CTRL_REG */
+#define TAS675X_SCLK_INV_TX_BIT BIT(5)
+#define TAS675X_SCLK_INV_RX_BIT BIT(4)
+#define TAS675X_SCLK_INV_MASK (TAS675X_SCLK_INV_TX_BIT | TAS675X_SCLK_INV_RX_BIT)
+
+/* AUDIO_IF_CTRL_REG */
+#define TAS675X_TDM_EN_BIT BIT(4)
+#define TAS675X_SAP_FMT_MASK GENMASK(3, 2)
+#define TAS675X_SAP_FMT_I2S (0x00 << 2)
+#define TAS675X_SAP_FMT_TDM (0x01 << 2)
+#define TAS675X_SAP_FMT_RIGHT_J (0x02 << 2)
+#define TAS675X_SAP_FMT_LEFT_J (0x03 << 2)
+#define TAS675X_FS_PULSE_MASK GENMASK(1, 0)
+#define TAS675X_FS_PULSE_SHORT 0x01
+
+/* SDIN_CTRL_REG */
+#define TAS675X_SDIN_AUDIO_WL_MASK GENMASK(3, 2)
+#define TAS675X_SDIN_LL_WL_MASK GENMASK(1, 0)
+#define TAS675X_SDIN_WL_MASK (TAS675X_SDIN_AUDIO_WL_MASK | TAS675X_SDIN_LL_WL_MASK)
+
+/* SDOUT_CTRL_REG */
+#define TAS675X_SDOUT_SELECT_MASK GENMASK(7, 4)
+#define TAS675X_SDOUT_SELECT_TDM_SDOUT1 0x00
+#define TAS675X_SDOUT_SELECT_NON_TDM 0x10
+#define TAS675X_SDOUT_VP_WL_MASK GENMASK(3, 2)
+#define TAS675X_SDOUT_IS_WL_MASK GENMASK(1, 0)
+#define TAS675X_SDOUT_WL_MASK (TAS675X_SDOUT_VP_WL_MASK | TAS675X_SDOUT_IS_WL_MASK)
+
+/* SDOUT_EN_REG */
+#define TAS675X_SDOUT_NON_TDM_SEL_MASK GENMASK(5, 4)
+#define TAS675X_SDOUT_NON_TDM_SEL_VPREDICT (0x0 << 4)
+#define TAS675X_SDOUT_NON_TDM_SEL_ISENSE (0x1 << 4)
+#define TAS675X_SDOUT_EN_VPREDICT BIT(0)
+#define TAS675X_SDOUT_EN_ISENSE BIT(1)
+#define TAS675X_SDOUT_EN_NON_TDM_ALL GENMASK(1, 0)
+
+/* Word length values (shared by SDIN_CTRL and SDOUT_CTRL) */
+#define TAS675X_WL_16BIT 0x00
+#define TAS675X_WL_20BIT 0x01
+#define TAS675X_WL_24BIT 0x02
+#define TAS675X_WL_32BIT 0x03
+
+/* SDIN_OFFSET_MSB_REG */
+#define TAS675X_SDIN_AUDIO_OFF_MSB_MASK GENMASK(7, 6)
+#define TAS675X_SDIN_LL_OFF_MSB_MASK GENMASK(5, 4)
+
+/* SDOUT_OFFSET_MSB_REG */
+#define TAS675X_SDOUT_VP_OFF_MSB_MASK GENMASK(7, 6)
+#define TAS675X_SDOUT_IS_OFF_MSB_MASK GENMASK(5, 4)
+
+/* RTLDG_EN_REG */
+#define TAS675X_RTLDG_CLIP_MASK_BIT BIT(4)
+#define TAS675X_RTLDG_CH_EN_MASK GENMASK(3, 0)
+
+/* DC_LDG_CTRL_REG */
+#define TAS675X_LDG_ABORT_BIT BIT(7)
+#define TAS675X_LDG_BUFFER_WAIT_MASK GENMASK(6, 5)
+#define TAS675X_LDG_WAIT_BYPASS_BIT BIT(2)
+#define TAS675X_SLOL_DISABLE_BIT BIT(1)
+#define TAS675X_LDG_BYPASS_BIT BIT(0)
+
+/* DC_LDG_TIME_CTRL_REG */
+#define TAS675X_LDG_RAMP_SLOL_MASK GENMASK(7, 6)
+#define TAS675X_LDG_SETTLING_SLOL_MASK GENMASK(5, 4)
+#define TAS675X_LDG_RAMP_S2PG_MASK GENMASK(3, 2)
+#define TAS675X_LDG_SETTLING_S2PG_MASK GENMASK(1, 0)
+
+/* AC_LDG_CTRL_REG */
+#define TAS675X_AC_DIAG_GAIN_BIT BIT(4)
+#define TAS675X_AC_DIAG_START_MASK GENMASK(3, 0)
+
+/* DC_LDG_RESULT_REG */
+#define TAS675X_DC_LDG_LO_RESULT_MASK GENMASK(7, 4)
+#define TAS675X_DC_LDG_PASS_MASK GENMASK(3, 0)
+
+/* Load Diagnostics Timing Constants */
+#define TAS675X_POLL_INTERVAL_US 10000
+#define TAS675X_STATE_TRANSITION_TIMEOUT_US 50000
+#define TAS675X_DC_LDG_TIMEOUT_US 300000
+#define TAS675X_AC_LDG_TIMEOUT_US 400000
+
+/* GPIO_CTRL_REG */
+#define TAS675X_GPIO1_OUTPUT_EN BIT(7)
+#define TAS675X_GPIO2_OUTPUT_EN BIT(6)
+#define TAS675X_GPIO_CTRL_RSTVAL 0x22
+
+/* GPIO output select values */
+#define TAS675X_GPIO_SEL_LOW 0x00
+#define TAS675X_GPIO_SEL_AUTO_MUTE_ALL 0x02
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH4 0x03
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH3 0x04
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH2 0x05
+#define TAS675X_GPIO_SEL_AUTO_MUTE_CH1 0x06
+#define TAS675X_GPIO_SEL_SDOUT2 0x08
+#define TAS675X_GPIO_SEL_SDOUT1 0x09
+#define TAS675X_GPIO_SEL_WARN 0x0A
+#define TAS675X_GPIO_SEL_FAULT 0x0B
+#define TAS675X_GPIO_SEL_CLOCK_SYNC 0x0E
+#define TAS675X_GPIO_SEL_INVALID_CLK 0x0F
+#define TAS675X_GPIO_SEL_HIGH 0x13
+
+/* GPIO input function encoding (flag bit | function ID) */
+#define TAS675X_GPIO_FUNC_INPUT 0x100
+
+/* Input Function IDs */
+#define TAS675X_GPIO_IN_ID_MUTE 0
+#define TAS675X_GPIO_IN_ID_PHASE_SYNC 1
+#define TAS675X_GPIO_IN_ID_SDIN2 2
+#define TAS675X_GPIO_IN_ID_DEEP_SLEEP 3
+#define TAS675X_GPIO_IN_ID_HIZ 4
+#define TAS675X_GPIO_IN_ID_PLAY 5
+#define TAS675X_GPIO_IN_ID_SLEEP 6
+#define TAS675X_GPIO_IN_NUM 7
+
+#define TAS675X_GPIO_IN_MUTE (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_MUTE)
+#define TAS675X_GPIO_IN_PHASE_SYNC \
+ (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_PHASE_SYNC)
+#define TAS675X_GPIO_IN_SDIN2 (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_SDIN2)
+#define TAS675X_GPIO_IN_DEEP_SLEEP \
+ (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_DEEP_SLEEP)
+#define TAS675X_GPIO_IN_HIZ (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_HIZ)
+#define TAS675X_GPIO_IN_PLAY (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_PLAY)
+#define TAS675X_GPIO_IN_SLEEP (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_SLEEP)
+
+/* GPIO input 3-bit mux field masks */
+#define TAS675X_GPIO_IN_MUTE_MASK GENMASK(2, 0)
+#define TAS675X_GPIO_IN_SYNC_MASK GENMASK(2, 0)
+#define TAS675X_GPIO_IN_SDIN2_MASK GENMASK(6, 4)
+#define TAS675X_GPIO_IN_DEEP_SLEEP_MASK GENMASK(6, 4)
+#define TAS675X_GPIO_IN_HIZ_MASK GENMASK(2, 0)
+#define TAS675X_GPIO_IN_PLAY_MASK GENMASK(6, 4)
+#define TAS675X_GPIO_IN_SLEEP_MASK GENMASK(2, 0)
+
+/* Book addresses for tas675x_select_book() */
+#define TAS675X_BOOK_DEFAULT 0x00
+#define TAS675X_BOOK_DSP 0x8C
+
+/* DSP memory addresses (DSP Book) */
+#define TAS675X_DSP_PAGE_RTLDG 0x22
+#define TAS675X_DSP_RTLDG_OL_THRESH_REG 0x98
+#define TAS675X_DSP_RTLDG_SL_THRESH_REG 0x9C
+
+#define TAS675X_DSP_PARAM_ID_OL_THRESH 0
+#define TAS675X_DSP_PARAM_ID_SL_THRESH 1
+
+/* Setup Mode Entry/Exit*/
+#define TAS675X_SETUP_ENTER_VAL1 0x11
+#define TAS675X_SETUP_ENTER_VAL2 0xFF
+#define TAS675X_SETUP_EXIT_VAL 0x00
+
+#endif /* __TAS675X_H__ */
diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c
index 7d6fcba9986e..7ddea2fbe2d2 100644
--- a/sound/soc/codecs/tda7419.c
+++ b/sound/soc/codecs/tda7419.c
@@ -614,7 +614,7 @@ static int tda7419_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id tda7419_i2c_id[] = {
- { "tda7419" },
+ { .name = "tda7419" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tda7419_i2c_id);
diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c
index ac0c5c337677..f30479f1f53b 100644
--- a/sound/soc/codecs/tfa9879.c
+++ b/sound/soc/codecs/tfa9879.c
@@ -296,7 +296,7 @@ static int tfa9879_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id tfa9879_i2c_id[] = {
- { "tfa9879" },
+ { .name = "tfa9879" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id);
diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c
index 270eee1ea534..d7d958ecd8db 100644
--- a/sound/soc/codecs/tlv320adc3xxx.c
+++ b/sound/soc/codecs/tlv320adc3xxx.c
@@ -1391,9 +1391,9 @@ static const struct snd_soc_component_driver soc_component_dev_adc3xxx = {
};
static const struct i2c_device_id adc3xxx_i2c_id[] = {
- { "tlv320adc3001", ADC3001 },
- { "tlv320adc3101", ADC3101 },
- {}
+ { .name = "tlv320adc3001", .driver_data = ADC3001 },
+ { .name = "tlv320adc3101", .driver_data = ADC3101 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id);
diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c
index e4f27a734501..4eb9cea27276 100644
--- a/sound/soc/codecs/tlv320adcx140.c
+++ b/sound/soc/codecs/tlv320adcx140.c
@@ -1326,10 +1326,10 @@ static int adcx140_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id adcx140_i2c_id[] = {
- { "tlv320adc3140", 0 },
- { "tlv320adc5140", 1 },
- { "tlv320adc6140", 2 },
- {}
+ { .name = "tlv320adc3140", .driver_data = 0 },
+ { .name = "tlv320adc5140", .driver_data = 1 },
+ { .name = "tlv320adc6140", .driver_data = 2 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, adcx140_i2c_id);
diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c
index a31fb95048b8..2f928ae23887 100644
--- a/sound/soc/codecs/tlv320aic23-i2c.c
+++ b/sound/soc/codecs/tlv320aic23-i2c.c
@@ -28,8 +28,8 @@ static int tlv320aic23_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id tlv320aic23_id[] = {
- {"tlv320aic23"},
- {}
+ { .name = "tlv320aic23" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, tlv320aic23_id);
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 4362c2c06ce8..1d2e0ea6d4fe 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -1630,14 +1630,14 @@ static void aic31xx_configure_ocmv(struct aic31xx_priv *priv)
}
static const struct i2c_device_id aic31xx_i2c_id[] = {
- { "tlv320aic310x", AIC3100 },
- { "tlv320aic311x", AIC3110 },
- { "tlv320aic3100", AIC3100 },
- { "tlv320aic3110", AIC3110 },
- { "tlv320aic3120", AIC3120 },
- { "tlv320aic3111", AIC3111 },
- { "tlv320dac3100", DAC3100 },
- { "tlv320dac3101", DAC3101 },
+ { .name = "tlv320aic310x", .driver_data = AIC3100 },
+ { .name = "tlv320aic311x", .driver_data = AIC3110 },
+ { .name = "tlv320aic3100", .driver_data = AIC3100 },
+ { .name = "tlv320aic3110", .driver_data = AIC3110 },
+ { .name = "tlv320aic3120", .driver_data = AIC3120 },
+ { .name = "tlv320aic3111", .driver_data = AIC3111 },
+ { .name = "tlv320dac3100", .driver_data = DAC3100 },
+ { .name = "tlv320dac3101", .driver_data = DAC3101 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c
index b27b5ae1e4b2..449353d5f088 100644
--- a/sound/soc/codecs/tlv320aic32x4-i2c.c
+++ b/sound/soc/codecs/tlv320aic32x4-i2c.c
@@ -38,9 +38,9 @@ static void aic32x4_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id aic32x4_i2c_id[] = {
- { "tlv320aic32x4", (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 },
- { "tlv320aic32x6", (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 },
- { "tas2505", (kernel_ulong_t)AIC32X4_TYPE_TAS2505 },
+ { .name = "tlv320aic32x4", .driver_data = (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 },
+ { .name = "tlv320aic32x6", .driver_data = (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 },
+ { .name = "tas2505", .driver_data = (kernel_ulong_t)AIC32X4_TYPE_TAS2505 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id);
diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c
index 0b585925c1ac..71528ad3a3b6 100644
--- a/sound/soc/codecs/tlv320aic3x-i2c.c
+++ b/sound/soc/codecs/tlv320aic3x-i2c.c
@@ -18,11 +18,11 @@
#include "tlv320aic3x.h"
static const struct i2c_device_id aic3x_i2c_id[] = {
- { "tlv320aic3x", AIC3X_MODEL_3X },
- { "tlv320aic33", AIC3X_MODEL_33 },
- { "tlv320aic3007", AIC3X_MODEL_3007 },
- { "tlv320aic3104", AIC3X_MODEL_3104 },
- { "tlv320aic3106", AIC3X_MODEL_3106 },
+ { .name = "tlv320aic3x", .driver_data = AIC3X_MODEL_3X },
+ { .name = "tlv320aic33", .driver_data = AIC3X_MODEL_33 },
+ { .name = "tlv320aic3007", .driver_data = AIC3X_MODEL_3007 },
+ { .name = "tlv320aic3104", .driver_data = AIC3X_MODEL_3104 },
+ { .name = "tlv320aic3106", .driver_data = AIC3X_MODEL_3106 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index ce22298b43ef..b38393a8130f 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
+ static const u8 dual_rate_q[] = {4, 8, 9, 12, 16};
struct snd_soc_component *component = dai->component;
struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component);
int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0;
u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
u16 d, pll_d = 1;
+ bool dual_rate;
int clk;
int width = aic3x->slot_width;
@@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
/* Fsref can be 44100 or 48000 */
fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000;
+ dual_rate = params_rate(params) >= 64000;
/* Try to find a value for Q which allows us to bypass the PLL and
* generate CODEC_CLK directly. */
- for (pll_q = 2; pll_q < 18; pll_q++)
- if (aic3x->sysclk / (128 * pll_q) == fsref) {
- bypass_pll = 1;
- break;
+ if (dual_rate) {
+ for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) {
+ pll_q = dual_rate_q[i];
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
}
+ } else {
+ for (pll_q = 2; pll_q < 18; pll_q++)
+ if (aic3x->sysclk / (128 * pll_q) == fsref) {
+ bypass_pll = 1;
+ break;
+ }
+ }
if (bypass_pll) {
pll_q &= 0xf;
@@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
* right DAC to right channel input */
data = (LDAC2LCH | RDAC2RCH);
data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000;
- if (params_rate(params) >= 64000)
+ if (dual_rate)
data |= DUAL_RATE_MODE;
snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data);
/* codec sample rate select */
data = (fsref * 20) / params_rate(params);
- if (params_rate(params) < 64000)
+ if (!dual_rate)
data /= 2;
data /= 5;
data -= 2;
@@ -1689,8 +1702,15 @@ static int aic3x_component_probe(struct snd_soc_component *component)
return 0;
}
+static int aic3x_of_xlate_dai_id(struct snd_soc_component *component,
+ struct device_node *endpoint)
+{
+ return 0;
+}
+
static const struct snd_soc_component_driver soc_component_dev_aic3x = {
.set_bias_level = aic3x_set_bias_level,
+ .of_xlate_dai_id = aic3x_of_xlate_dai_id,
.probe = aic3x_component_probe,
.controls = aic3x_snd_controls,
.num_controls = ARRAY_SIZE(aic3x_snd_controls),
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
index 5a7beeadb009..39284e4d420e 100644
--- a/sound/soc/codecs/ts3a227e.c
+++ b/sound/soc/codecs/ts3a227e.c
@@ -423,7 +423,7 @@ static const struct dev_pm_ops ts3a227e_pm = {
};
static const struct i2c_device_id ts3a227e_i2c_ids[] = {
- { "ts3a227e" },
+ { .name = "ts3a227e" },
{ }
};
MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids);
diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c
index 7390ab250ebb..dba581857920 100644
--- a/sound/soc/codecs/tscs42xx.c
+++ b/sound/soc/codecs/tscs42xx.c
@@ -1483,8 +1483,8 @@ static int tscs42xx_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id tscs42xx_i2c_id[] = {
- { "tscs42A1" },
- { "tscs42A2" },
+ { .name = "tscs42A1" },
+ { .name = "tscs42A2" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tscs42xx_i2c_id);
diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c
index 64d0da40fbaf..aad394937ce6 100644
--- a/sound/soc/codecs/tscs454.c
+++ b/sound/soc/codecs/tscs454.c
@@ -3454,7 +3454,7 @@ static int tscs454_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id tscs454_i2c_id[] = {
- { "tscs454" },
+ { .name = "tscs454" },
{ }
};
MODULE_DEVICE_TABLE(i2c, tscs454_i2c_id);
diff --git a/sound/soc/codecs/uda1334.c b/sound/soc/codecs/uda1334.c
index f799772ff747..54c5cb5b3d4b 100644
--- a/sound/soc/codecs/uda1334.c
+++ b/sound/soc/codecs/uda1334.c
@@ -4,7 +4,6 @@
//
// Based on WM8523 ALSA SoC Audio driver written by Mark Brown
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c
index b0b29012842d..12f5757f4210 100644
--- a/sound/soc/codecs/uda1342.c
+++ b/sound/soc/codecs/uda1342.c
@@ -319,7 +319,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops,
uda1342_suspend, uda1342_resume, NULL);
static const struct i2c_device_id uda1342_i2c_id[] = {
- { "uda1342" },
+ { .name = "uda1342" },
{ }
};
MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id);
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 55c83d95bfba..1dd09d10fc4a 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -38,9 +38,9 @@ struct uda1380_priv {
unsigned int dac_clk;
struct work_struct work;
struct i2c_client *i2c;
- u16 *reg_cache;
struct gpio_desc *power;
struct gpio_desc *reset;
+ u16 reg_cache[];
};
/*
@@ -767,11 +767,15 @@ static int uda1380_i2c_probe(struct i2c_client *i2c)
struct uda1380_priv *uda1380;
int ret;
- uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv),
+ uda1380 = devm_kzalloc(&i2c->dev,
+ struct_size(uda1380, reg_cache, ARRAY_SIZE(uda1380_reg)),
GFP_KERNEL);
if (uda1380 == NULL)
return -ENOMEM;
+ memcpy(uda1380->reg_cache, uda1380_reg,
+ ARRAY_SIZE(uda1380_reg) * sizeof(*uda1380->reg_cache));
+
uda1380->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(uda1380->reset))
return dev_err_probe(dev, PTR_ERR(uda1380->reset),
@@ -789,11 +793,6 @@ static int uda1380_i2c_probe(struct i2c_client *i2c)
if (device_property_match_string(dev, "dac-clk", "wspll") >= 0)
uda1380->dac_clk = UDA1380_DAC_CLK_WSPLL;
- uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg),
- sizeof(uda1380_reg[0]), GFP_KERNEL);
- if (!uda1380->reg_cache)
- return -ENOMEM;
-
i2c_set_clientdata(i2c, uda1380);
uda1380->i2c = i2c;
@@ -803,7 +802,7 @@ static int uda1380_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id uda1380_i2c_id[] = {
- { "uda1380" },
+ { .name = "uda1380" },
{ }
};
MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c
index 72a53f95d688..e0169e783ee9 100644
--- a/sound/soc/codecs/wcd937x.c
+++ b/sound/soc/codecs/wcd937x.c
@@ -2499,18 +2499,13 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component)
{
struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component);
struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component);
- struct sdw_slave *tx_sdw_dev = wcd937x->tx_sdw_dev;
struct device *dev = component->dev;
- unsigned long time_left;
int i, ret;
u32 chipid;
- time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
- msecs_to_jiffies(5000));
- if (!time_left) {
- dev_err(dev, "soundwire device init timeout\n");
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(wcd937x->tx_sdw_dev, 5000);
+ if (ret)
+ return ret;
snd_soc_component_init_regmap(component, wcd937x->regmap);
ret = pm_runtime_resume_and_get(dev);
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index cb0a0bfdb6e3..c69e18667a85 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -3016,18 +3016,13 @@ static int wcd938x_irq_init(struct wcd938x_priv *wcd, struct device *dev)
static int wcd938x_soc_codec_probe(struct snd_soc_component *component)
{
struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component);
- struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev;
struct device *dev = component->dev;
- unsigned long time_left;
unsigned int variant;
int ret, i;
- time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
- msecs_to_jiffies(2000));
- if (!time_left) {
- dev_err(dev, "soundwire device init timeout\n");
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(wcd938x->tx_sdw_dev, 2000);
+ if (ret)
+ return ret;
snd_soc_component_init_regmap(component, wcd938x->regmap);
diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c
index 01f1a08f48e6..010d12466722 100644
--- a/sound/soc/codecs/wcd939x.c
+++ b/sound/soc/codecs/wcd939x.c
@@ -2968,17 +2968,12 @@ static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev)
static int wcd939x_soc_codec_probe(struct snd_soc_component *component)
{
struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component);
- struct sdw_slave *tx_sdw_dev = wcd939x->tx_sdw_dev;
struct device *dev = component->dev;
- unsigned long time_left;
int ret, i;
- time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete,
- msecs_to_jiffies(2000));
- if (!time_left) {
- dev_err(dev, "soundwire device init timeout\n");
- return -ETIMEDOUT;
- }
+ ret = sdw_slave_wait_for_init(wcd939x->tx_sdw_dev, 2000);
+ if (ret)
+ return ret;
snd_soc_component_init_regmap(component, wcd939x->regmap);
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index 1f59309d8c69..5c1b00acb5bf 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -204,7 +204,7 @@ static int wm1250_ev1_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm1250_ev1_i2c_id[] = {
- { "wm1250-ev1" },
+ { .name = "wm1250-ev1" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id);
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index 126be2a2a8f3..9b68ee69324b 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -929,7 +929,7 @@ out:
}
static const struct i2c_device_id wm2000_i2c_id[] = {
- { "wm2000" },
+ { .name = "wm2000" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id);
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 87418c838ca0..ba8ce2e6e615 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2471,7 +2471,7 @@ static const struct dev_pm_ops wm2200_pm = {
};
static const struct i2c_device_id wm2200_i2c_id[] = {
- { "wm2200" },
+ { .name = "wm2200" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id);
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 96fd098a9d36..bd94fa53c362 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2669,7 +2669,7 @@ static const struct dev_pm_ops wm5100_pm = {
};
static const struct i2c_device_id wm5100_i2c_id[] = {
- { "wm5100" },
+ { .name = "wm5100" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id);
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index bebee333d3fd..137dcd3d7487 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -7,7 +7,6 @@
* Author: Liam Girdwood <lrg@slimlogic.co.uk>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -669,7 +668,7 @@ static int wm8510_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8510_i2c_id[] = {
- { "wm8510" },
+ { .name = "wm8510" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id);
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index f003f19766e2..b8832a1d61fe 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -7,7 +7,6 @@
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -518,7 +517,7 @@ err_enable:
}
static const struct i2c_device_id wm8523_i2c_id[] = {
- { "wm8523" },
+ { .name = "wm8523" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id);
diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c
index 6b1a7450b0ac..23daf158f35f 100644
--- a/sound/soc/codecs/wm8524.c
+++ b/sound/soc/codecs/wm8524.c
@@ -8,7 +8,6 @@
* Based on WM8523 ALSA SoC Audio driver written by Mark Brown
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 2be265bb0751..eb374ba6e5b5 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -15,7 +15,6 @@
* the secondary audio interfaces are not.
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
@@ -1034,8 +1033,8 @@ static const struct of_device_id wm8580_of_match[] = {
MODULE_DEVICE_TABLE(of, wm8580_of_match);
static const struct i2c_device_id wm8580_i2c_id[] = {
- { "wm8580", (kernel_ulong_t)&wm8580_data },
- { "wm8581", (kernel_ulong_t)&wm8581_data },
+ { .name = "wm8580", .driver_data = (kernel_ulong_t)&wm8580_data },
+ { .name = "wm8581", .driver_data = (kernel_ulong_t)&wm8581_data },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id);
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index 2bab9d189519..2db8829661c3 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -9,7 +9,6 @@
* Based on wm8731.c by Richard Purdie
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -455,7 +454,7 @@ static int wm8711_i2c_probe(struct i2c_client *client)
}
static const struct i2c_device_id wm8711_i2c_id[] = {
- { "wm8711" },
+ { .name = "wm8711" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id);
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 4c1a80561f06..3109a6a0df74 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -7,7 +7,6 @@
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -296,7 +295,7 @@ static int wm8728_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8728_i2c_id[] = {
- { "wm8728" },
+ { .name = "wm8728" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id);
diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c
index 1254e583af51..f44f4d3d9394 100644
--- a/sound/soc/codecs/wm8731-i2c.c
+++ b/sound/soc/codecs/wm8731-i2c.c
@@ -11,7 +11,6 @@
*/
#include <linux/i2c.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include "wm8731.h"
@@ -47,7 +46,7 @@ static int wm8731_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8731_i2c_id[] = {
- { "wm8731" },
+ { .name = "wm8731" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id);
diff --git a/sound/soc/codecs/wm8731-spi.c b/sound/soc/codecs/wm8731-spi.c
index c02086afa7fb..29e58e1e6b79 100644
--- a/sound/soc/codecs/wm8731-spi.c
+++ b/sound/soc/codecs/wm8731-spi.c
@@ -11,7 +11,6 @@
*/
#include <linux/spi/spi.h>
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include "wm8731.h"
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index fee8a37ed1df..33a3f88fffb3 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -7,7 +7,6 @@
* Author: Mark Brown <broonie@opensource.wolfsonmicro.com>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -641,7 +640,7 @@ static int wm8737_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8737_i2c_id[] = {
- { "wm8737" },
+ { .name = "wm8737" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id);
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 4dfbb33edb09..ca56fdfb5088 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -606,7 +606,7 @@ static int wm8741_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8741_i2c_id[] = {
- { "wm8741" },
+ { .name = "wm8741" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id);
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index 0e1d3ebb15c4..d3f1178c2d34 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -803,8 +803,8 @@ static int wm8750_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8750_i2c_id[] = {
- { "wm8750" },
- { "wm8987" },
+ { .name = "wm8750" },
+ { .name = "wm8987" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id);
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index a532a95e8048..ac4008b4832d 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -26,7 +26,6 @@
* an alsa kcontrol. This allows the PCM to remain open.
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
@@ -1581,7 +1580,7 @@ static int wm8753_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8753_i2c_id[] = {
- { "wm8753" },
+ { .name = "wm8753" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id);
diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c
index d382b476c89c..b8b4d1e823e6 100644
--- a/sound/soc/codecs/wm8770.c
+++ b/sound/soc/codecs/wm8770.c
@@ -7,7 +7,6 @@
* Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com>
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index c3f340657f0c..a8e4f71c77d1 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -9,7 +9,6 @@
* TODO: Input ALC/limiter support
*/
-#include <linux/mod_devicetable.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -513,8 +512,8 @@ static int wm8776_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8776_i2c_id[] = {
- { "wm8775", WM8775 },
- { "wm8776", WM8776 },
+ { .name = "wm8775", .driver_data = WM8775 },
+ { .name = "wm8776", .driver_data = WM8776 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id);
diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c
index 3380d7301b17..3c49abd33d28 100644
--- a/sound/soc/codecs/wm8804-i2c.c
+++ b/sound/soc/codecs/wm8804-i2c.c
@@ -31,7 +31,7 @@ static void wm8804_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8804_i2c_id[] = {
- { "wm8804" },
+ { .name = "wm8804" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id);
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index fea629541acd..a9128cfa6ff1 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1286,11 +1286,8 @@ static int wm8900_i2c_probe(struct i2c_client *i2c)
return ret;
}
-static void wm8900_i2c_remove(struct i2c_client *client)
-{}
-
static const struct i2c_device_id wm8900_i2c_id[] = {
- { "wm8900" },
+ { .name = "wm8900" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id);
@@ -1299,8 +1296,7 @@ static struct i2c_driver wm8900_i2c_driver = {
.driver = {
.name = "wm8900",
},
- .probe = wm8900_i2c_probe,
- .remove = wm8900_i2c_remove,
+ .probe = wm8900_i2c_probe,
.id_table = wm8900_i2c_id,
};
#endif
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index f73f6ad06b38..320d7737699d 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -2203,7 +2203,7 @@ static const struct of_device_id wm8903_of_match[] = {
MODULE_DEVICE_TABLE(of, wm8903_of_match);
static const struct i2c_device_id wm8903_i2c_id[] = {
- { "wm8903" },
+ { .name = "wm8903" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id);
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 4c73a340f25f..9e5782e50f47 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2621,9 +2621,9 @@ err_enable:
}
static const struct i2c_device_id wm8904_i2c_id[] = {
- { "wm8904", WM8904 },
- { "wm8912", WM8912 },
- { "wm8918", WM8904 }, /* Actually a subset, updates to follow */
+ { .name = "wm8904", .driver_data = WM8904 },
+ { .name = "wm8912", .driver_data = WM8912 },
+ { .name = "wm8918", .driver_data = WM8904 }, /* Actually a subset, updates to follow */
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id);
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index 2f55d0c572a4..e631ec072249 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -851,7 +851,7 @@ static int wm8940_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8940_i2c_id[] = {
- { "wm8940" },
+ { .name = "wm8940" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id);
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index e1c61e026cbc..c897a9ab764d 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -996,7 +996,7 @@ static int wm8955_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8955_i2c_id[] = {
- { "wm8955" },
+ { .name = "wm8955" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id);
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 384e8e703446..a810732c6af0 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -1551,7 +1551,7 @@ static void wm8960_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id wm8960_i2c_id[] = {
- { "wm8960" },
+ { .name = "wm8960" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index cfb8cfc91873..504fc59e4be2 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -967,7 +967,7 @@ static int wm8961_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8961_i2c_id[] = {
- { "wm8961" },
+ { .name = "wm8961" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id);
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index 8d2435bf44ea..de18b1f85a32 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3991,7 +3991,7 @@ static const struct dev_pm_ops wm8962_pm = {
};
static const struct i2c_device_id wm8962_i2c_id[] = {
- { "wm8962" },
+ { .name = "wm8962" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id);
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 46aa556b44fa..a5d2e91b66bb 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -692,7 +692,7 @@ static int wm8971_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8971_i2c_id[] = {
- { "wm8971" },
+ { .name = "wm8971" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id);
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index 0bb5e947f46d..4656652b8e97 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -712,7 +712,7 @@ static int wm8974_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8974_i2c_id[] = {
- { "wm8974" },
+ { .name = "wm8974" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id);
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 935761e50865..ad8064bbaaac 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1059,7 +1059,7 @@ static int wm8978_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8978_i2c_id[] = {
- { "wm8978" },
+ { .name = "wm8978" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index cd34f71cf42a..fab25f35fd14 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -1060,7 +1060,7 @@ static int wm8983_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8983_i2c_id[] = {
- { "wm8983" },
+ { .name = "wm8983" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id);
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index be23c0c608d1..2a64d5a851da 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1195,8 +1195,8 @@ static int wm8985_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8985_i2c_id[] = {
- { "wm8985", WM8985 },
- { "wm8758", WM8758 },
+ { .name = "wm8985", .driver_data = WM8985 },
+ { .name = "wm8758", .driver_data = WM8758 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id);
diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c
index 9bffe7a6ccec..741aecc7641a 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -897,7 +897,7 @@ static int wm8988_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8988_i2c_id[] = {
- { "wm8988" },
+ { .name = "wm8988" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id);
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 9f2b42025ec9..11dfbbbdcd92 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1241,7 +1241,7 @@ static int wm8990_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8990_i2c_id[] = {
- { "wm8990" },
+ { .name = "wm8990" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id);
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index b8ed2a3e699b..225c235d3d89 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -1315,7 +1315,7 @@ static int wm8991_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8991_i2c_id[] = {
- { "wm8991" },
+ { .name = "wm8991" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id);
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 1c9299979898..2a94b23319b5 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1735,7 +1735,7 @@ static void wm8993_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8993_i2c_id[] = {
- { "wm8993" },
+ { .name = "wm8993" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id);
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 104ce09c02e0..799989a5bf0f 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -2259,8 +2259,8 @@ static int wm8995_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8995_i2c_id[] = {
- {"wm8995"},
- {}
+ { .name = "wm8995" },
+ { }
};
MODULE_DEVICE_TABLE(i2c, wm8995_i2c_id);
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index 2d9cbf66f7d4..dface555f928 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -3072,7 +3072,7 @@ static void wm8996_i2c_remove(struct i2c_client *client)
}
static const struct i2c_device_id wm8996_i2c_id[] = {
- { "wm8996" },
+ { .name = "wm8996" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id);
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 5bfe43c6c1f4..2e4e0a76499d 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1357,11 +1357,8 @@ static int wm9081_i2c_probe(struct i2c_client *i2c)
return 0;
}
-static void wm9081_i2c_remove(struct i2c_client *client)
-{}
-
static const struct i2c_device_id wm9081_i2c_id[] = {
- { "wm9081" },
+ { .name = "wm9081" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id);
@@ -1370,8 +1367,7 @@ static struct i2c_driver wm9081_i2c_driver = {
.driver = {
.name = "wm9081",
},
- .probe = wm9081_i2c_probe,
- .remove = wm9081_i2c_remove,
+ .probe = wm9081_i2c_probe,
.id_table = wm9081_i2c_id,
};
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index 5182f0839b7c..8fc24b19313d 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -607,8 +607,8 @@ static int wm9090_i2c_probe(struct i2c_client *i2c)
}
static const struct i2c_device_id wm9090_id[] = {
- { "wm9090" },
- { "wm9093" },
+ { .name = "wm9090" },
+ { .name = "wm9093" },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm9090_id);
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index a637e22c3929..baa75e7ff53b 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -679,6 +679,9 @@ static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl)
{
struct wm_coeff_ctl *ctl = cs_ctl->priv;
+ if (!ctl)
+ return;
+
cancel_work_sync(&ctl->work);
kfree(ctl->name);
@@ -1167,7 +1170,14 @@ EXPORT_SYMBOL_GPL(wm_adsp2_component_probe);
int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component)
{
+ if (!dsp)
+ return 0;
+
+ if (!dsp->component)
+ return 0;
+
cs_dsp_cleanup_debugfs(&dsp->cs_dsp);
+ dsp->component = NULL;
return 0;
}
diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c
index 2fc234adca5f..5174614c3e83 100644
--- a/sound/soc/codecs/wsa881x.c
+++ b/sound/soc/codecs/wsa881x.c
@@ -3,7 +3,6 @@
// Copyright (c) 2019, Linaro Limited
#include <linux/bitops.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -672,11 +671,6 @@ struct wsa881x_priv {
struct sdw_stream_runtime *sruntime;
struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS];
struct gpio_desc *sd_n;
- /*
- * Logical state for SD_N GPIO: high for shutdown, low for enable.
- * For backwards compatibility.
- */
- unsigned int sd_n_val;
int active_ports;
bool hw_init;
bool port_prepared[WSA881X_MAX_SWR_PORTS];
@@ -1121,31 +1115,11 @@ static int wsa881x_probe(struct sdw_slave *pdev,
if (!wsa881x)
return -ENOMEM;
- wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", 0);
+ wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW);
if (IS_ERR(wsa881x->sd_n))
return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n),
"Shutdown Control GPIO not found\n");
- /*
- * Backwards compatibility work-around.
- *
- * The SD_N GPIO is active low, however upstream DTS used always active
- * high. Changing the flag in driver and DTS will break backwards
- * compatibility, so add a simple value inversion to work with both old
- * and new DTS.
- *
- * This won't work properly with DTS using the flags properly in cases:
- * 1. Old DTS with proper ACTIVE_LOW, however such case was broken
- * before as the driver required the active high.
- * 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case
- * (not existing upstream) but possible. This is the price of
- * backwards compatibility, therefore this hack should be removed at
- * some point.
- */
- wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n);
- if (!wsa881x->sd_n_val)
- dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO.");
-
dev_set_drvdata(dev, wsa881x);
wsa881x->slave = pdev;
wsa881x->dev = dev;
@@ -1158,7 +1132,6 @@ static int wsa881x_probe(struct sdw_slave *pdev,
pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop;
pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
pdev->prop.clk_stop_mode1 = true;
- gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config);
if (IS_ERR(wsa881x->regmap))
@@ -1181,7 +1154,7 @@ static int wsa881x_runtime_suspend(struct device *dev)
struct regmap *regmap = dev_get_regmap(dev, NULL);
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
- gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
+ gpiod_direction_output(wsa881x->sd_n, 1);
regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
@@ -1194,16 +1167,14 @@ static int wsa881x_runtime_resume(struct device *dev)
struct sdw_slave *slave = dev_to_sdw_dev(dev);
struct regmap *regmap = dev_get_regmap(dev, NULL);
struct wsa881x_priv *wsa881x = dev_get_drvdata(dev);
- unsigned long time;
+ int ret;
- gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val);
+ gpiod_direction_output(wsa881x->sd_n, 0);
- time = wait_for_completion_timeout(&slave->initialization_complete,
- msecs_to_jiffies(WSA881X_PROBE_TIMEOUT));
- if (!time) {
- dev_err(dev, "Initialization not complete, timed out\n");
- gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val);
- return -ETIMEDOUT;
+ ret = sdw_slave_wait_for_init(slave, WSA881X_PROBE_TIMEOUT);
+ if (ret) {
+ gpiod_direction_output(wsa881x->sd_n, 1);
+ return ret;
}
regcache_cache_only(regmap, false);