diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2023-06-29 10:46:47 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2023-06-29 10:46:47 -0700 |
commit | f8824e151fbfa0ac0a258015d606ea6f4a10251b (patch) | |
tree | 805b029bf3a550168d66ac9f1f5e9aa6f2a68b84 /sound/soc/codecs | |
parent | 86e203edf24bb327ce8fcd3c5c8c6bf530a846df (diff) | |
parent | d6048fdc870240e5020343f8af0c825829c232bd (diff) | |
download | linux-stable-f8824e151fbfa0ac0a258015d606ea6f4a10251b.tar.gz linux-stable-f8824e151fbfa0ac0a258015d606ea6f4a10251b.tar.bz2 linux-stable-f8824e151fbfa0ac0a258015d606ea6f4a10251b.zip |
Merge tag 'sound-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
Pull sound updates from Takashi Iwai:
"Lots of changes as usual, but the only significant stuff in ALSA core
part is the MIDI 2.0 support, while ASoC core kept receiving the code
refactoring. The majority of changes are seen rather in device
drivers, and quite a few new drivers can be found there.
Here we go, some highlights:
ALSA and ASoC Core:
- Support of MIDI 2.0 devices: rawmidi and sequencer API have been
extended for the support of the new UMP (Universal MIDI Packet)
protocol, USB audio driver got the USB MIDI 2.0 interface support
- Continued refactoring around ASoC DAI links and the ordering of
trigger callbacks
- PCM ABI extension for better drain support
ASoC Drivers:
- Conversions of many drivers to use maple tree based caches
- Everlasting improvement works on ASoC Intel drivers
- Compressed audio support for Qualcomm
- Support for AMD SoundWire, Analog Devices SSM3515, Google
Chameleon, Ingenic X1000, Intel systems with various CODECs,
Loongson platforms, Maxim MAX98388, Mediatek MT8188, Nuvoton
NAU8825C, NXP platforms with NAU8822, Qualcomm WSA884x, StarFive
JH7110, Texas Instruments TAS2781
HD-audio:
- Quirks for HP and ASUS machines
- CS35L41 HD-audio codec fixes
- Loongson HD-audio support
Misc:
- A new virtual PCM test driver for kselftests
- Continued refactoring and improvements on the legacy emu10k1
driver"
* tag 'sound-6.5-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (556 commits)
ALSA: hda/realtek: Enable mute/micmute LEDs and limit mic boost on EliteBook
ASoC: hdmi-codec: fix channel info for compressed formats
ALSA: pcm: fix ELD constraints for (E)AC3, DTS(-HD) and MLP formats
ASoC: core: Always store of_node when getting DAI link component
ASoC: tas2781: Fix error code in tas2781_load_calibration()
ASoC: amd: update pm_runtime enable sequence
ALSA: ump: Export MIDI1 / UMP conversion helpers
ASoC: tas2781: fix Kconfig dependencies
ASoC: amd: acp: remove acp poweroff function
ASoC: amd: acp: clear pdm dma interrupt mask
ASoC: codecs: max98090: Allow dsp_a mode
ASoC: qcom: common: add default jack dapm pins
ASoC: loongson: fix address space confusion
ASoC: dt-bindings: microchip,sama7g5-pdmc: Simplify "microchip,mic-pos" constraints
ASoC: tegra: Remove stale comments in AHUB
ASoC: tegra: Use normal system sleep for ASRC
ALSA: hda/realtek: Add quirks for ROG ALLY CS35l41 audio
ASoC: fsl-asoc-card: Allow passing the number of slots in use
ASoC: codecs: wsa884x: Add WSA884x family of speakers
ASoC: dt-bindings: qcom,wsa8840: Add WSA884x family of speakers
...
Diffstat (limited to 'sound/soc/codecs')
242 files changed, 10629 insertions, 490 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 0c4c5cbaa809..2a62dbd5339e 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -57,6 +57,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AW88395 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 + imply SND_SOC_CHV3_CODEC imply SND_SOC_CQ0093VC imply SND_SOC_CROS_EC_CODEC imply SND_SOC_CS35L32 @@ -136,6 +137,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_MAX98363 imply SND_SOC_MAX98373_I2C imply SND_SOC_MAX98373_SDW + imply SND_SOC_MAX98388 imply SND_SOC_MAX98390 imply SND_SOC_MAX98396 imply SND_SOC_MAX9850 @@ -207,6 +209,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT712_SDCA_DMIC_SDW imply SND_SOC_RT715_SDW imply SND_SOC_RT715_SDCA_SDW + imply SND_SOC_RT722_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW imply SND_SOC_RT1318_SDW @@ -234,6 +237,9 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 imply SND_SOC_TAS2780 + imply SND_SOC_TAS2781_COMLIB + imply SND_SOC_TAS2781_FMWLIB + imply SND_SOC_TAS2781_I2C imply SND_SOC_TAS5086 imply SND_SOC_TAS571X imply SND_SOC_TAS5720 @@ -324,6 +330,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WM9713 imply SND_SOC_WSA881X imply SND_SOC_WSA883X + imply SND_SOC_WSA884X imply SND_SOC_ZL38060 help Normally ASoC codec drivers are only built if a machine driver which @@ -643,6 +650,13 @@ config SND_SOC_BD28623 config SND_SOC_BT_SCO tristate "Dummy BT SCO codec driver" +config SND_SOC_CHV3_CODEC + tristate "Google Chameleon v3 codec driver" + help + Enable support for the Google Chameleon v3 audio codec. + This codec does not have a control interface, it always outputs + 8 channel S32_LE audio. + config SND_SOC_CPCAP tristate "Motorola CPCAP codec" depends on MFD_CPCAP || COMPILE_TEST @@ -1166,6 +1180,15 @@ config SND_SOC_MAX98373_SDW interface for control data. Select this if MAX98373 is connected via soundwire. +config SND_SOC_MAX98388 + tristate "Analog Devices MAX98388 Speaker Amplifier" + depends on I2C + help + Enable support for Analog Devices MAX98388 audio + amplifier. The device provides a PCM interface for + audio data and a standard I2C interface for control + data communication. + config SND_SOC_MAX98390 tristate "Maxim Integrated MAX98390 Speaker Amplifier" depends on I2C @@ -1539,6 +1562,12 @@ config SND_SOC_RT712_SDCA_DMIC_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_RT722_SDCA_SDW + tristate "Realtek RT722 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT715 tristate @@ -1652,6 +1681,12 @@ config SND_SOC_SSM2602_I2C select SND_SOC_SSM2602 select REGMAP_I2C +config SND_SOC_SSM3515 + tristate "Analog Devices SSM3515 amplifier driver" + select REGMAP_I2C + depends on I2C + depends on OF + config SND_SOC_SSM4567 tristate "Analog Devices ssm4567 amplifier driver support" depends on I2C @@ -1699,6 +1734,29 @@ config SND_SOC_TAS2780 Enable support for Texas Instruments TAS2780 high-efficiency digital input mono Class-D audio power amplifiers. +config SND_SOC_TAS2781_COMLIB + depends on I2C + select CRC8 + select REGMAP_I2C + tristate + +config SND_SOC_TAS2781_FMWLIB + depends on SND_SOC_TAS2781_COMLIB + tristate + default n + +config SND_SOC_TAS2781_I2C + tristate "Texas Instruments TAS2781 speaker amplifier based on I2C" + depends on I2C + select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_FMWLIB + help + Enable support for Texas Instruments TAS2781 Smart Amplifier + Digital input mono Class-D and DSP-inside audio power amplifiers. + Note the TAS2781 driver implements a flexible and configurable + algo coefficient setting, for one, two or even multiple TAS2781 + chips. + config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C @@ -2162,6 +2220,15 @@ config SND_SOC_WSA883X This enables support for Qualcomm WSA8830/WSA8835 Class-D Smart Speaker Amplifier. +config SND_SOC_WSA884X + tristate "WSA884X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D + Smart Speaker Amplifier. + config SND_SOC_ZL38060 tristate "Microsemi ZL38060 Connected Home Audio Processor" depends on SPI_MASTER diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5cdbae88e6e3..b48a9a323b84 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -51,6 +51,7 @@ snd-soc-aw88395-objs := aw88395/aw88395.o \ aw88395/aw88395_device.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o +snd-soc-chv3-codec-objs := chv3-codec.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cros-ec-codec-objs := cros_ec_codec.o @@ -152,6 +153,7 @@ snd-soc-max98363-objs := max98363.o snd-soc-max98373-objs := max98373.o snd-soc-max98373-i2c-objs := max98373-i2c.o snd-soc-max98373-sdw-objs := max98373-sdw.o +snd-soc-max98388-objs := max98388.o snd-soc-max98390-objs := max98390.o snd-soc-max98396-objs := max98396.o snd-soc-max9850-objs := max9850.o @@ -237,6 +239,7 @@ snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o snd-soc-rt712-sdca-dmic-objs := rt712-sdca-dmic.o snd-soc-rt715-objs := rt715.o rt715-sdw.o snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o +snd-soc-rt722-sdca-objs := rt722-sdca.o rt722-sdca-sdw.o snd-soc-rt9120-objs := rt9120.o snd-soc-sdw-mockup-objs := sdw-mockup.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -256,6 +259,7 @@ snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-ssm2602-spi-objs := ssm2602-spi.o snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o +snd-soc-ssm3515-objs := ssm3515.o snd-soc-ssm4567-objs := ssm4567.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta350-objs := sta350.o @@ -269,6 +273,9 @@ snd-soc-tas5805m-objs := tas5805m.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o snd-soc-tas2770-objs := tas2770.o +snd-soc-tas2781-comlib-objs := tas2781-comlib.o +snd-soc-tas2781-fmwlib-objs := tas2781-fmwlib.o +snd-soc-tas2781-i2c-objs := tas2781-i2c.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tfa989x-objs := tfa989x.o snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o @@ -359,6 +366,7 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-wsa881x-objs := wsa881x.o snd-soc-wsa883x-objs := wsa883x.o +snd-soc-wsa884x-objs := wsa884x.o snd-soc-zl38060-objs := zl38060.o # Amp snd-soc-max9877-objs := max9877.o @@ -425,6 +433,7 @@ obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o +obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o @@ -521,6 +530,7 @@ obj-$(CONFIG_SND_SOC_MAX98363) += snd-soc-max98363.o obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o +obj-$(CONFIG_SND_SOC_MAX98388) += snd-soc-max98388.o obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o @@ -607,6 +617,7 @@ obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW) += snd-soc-rt712-sdca-dmic.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o +obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o @@ -623,6 +634,7 @@ obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o +obj-$(CONFIG_SND_SOC_SSM3515) += snd-soc-ssm3515.o obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o @@ -633,6 +645,9 @@ 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 obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o +obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o +obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o +obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o @@ -733,6 +748,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o +obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o # Amp diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index 4cb8d87f9011..15d74bb31c4c 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -38,7 +38,7 @@ static struct i2c_driver ad193x_i2c_driver = { .driver = { .name = "ad193x", }, - .probe_new = ad193x_i2c_probe, + .probe = ad193x_i2c_probe, .id_table = ad193x_id, }; module_i2c_driver(ad193x_i2c_driver); diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c index 8ed0ffdedbc9..132b9e2cca59 100644 --- a/sound/soc/codecs/adau1372-i2c.c +++ b/sound/soc/codecs/adau1372-i2c.c @@ -30,7 +30,7 @@ static struct i2c_driver adau1372_i2c_driver = { .driver = { .name = "adau1372", }, - .probe_new = adau1372_i2c_probe, + .probe = adau1372_i2c_probe, .id_table = adau1372_i2c_ids, }; module_i2c_driver(adau1372_i2c_driver); diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 37e178e8a1d0..c5b087b8fffc 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1505,7 +1505,7 @@ static struct i2c_driver adau1373_i2c_driver = { .driver = { .name = "adau1373", }, - .probe_new = adau1373_i2c_probe, + .probe = adau1373_i2c_probe, .id_table = adau1373_i2c_id, }; diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 135a7db7fcf9..8c8de3b3c901 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -876,7 +876,7 @@ static struct i2c_driver adau1701_i2c_driver = { .name = "adau1701", .of_match_table = of_match_ptr(adau1701_dt_ids), }, - .probe_new = adau1701_i2c_probe, + .probe = adau1701_i2c_probe, .id_table = adau1701_i2c_id, }; diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index 0cefff49569c..a554255186ae 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -60,7 +60,7 @@ static struct i2c_driver adau1761_i2c_driver = { .name = "adau1761", .of_match_table = of_match_ptr(adau1761_i2c_dt_ids), }, - .probe_new = adau1761_i2c_probe, + .probe = adau1761_i2c_probe, .remove = adau1761_i2c_remove, .id_table = adau1761_i2c_ids, }; diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 39021b8cfb62..3a170fd78ff3 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -56,7 +56,7 @@ static struct i2c_driver adau1781_i2c_driver = { .name = "adau1781", .of_match_table = of_match_ptr(adau1781_i2c_dt_ids), }, - .probe_new = adau1781_i2c_probe, + .probe = adau1781_i2c_probe, .remove = adau1781_i2c_remove, .id_table = adau1781_i2c_ids, }; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 634d4dbca5ec..f2932713b4de 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -1059,13 +1059,12 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap, if (!adau) return -ENOMEM; - adau->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(adau->mclk)) { - if (PTR_ERR(adau->mclk) != -ENOENT) - return PTR_ERR(adau->mclk); - /* Clock is optional (for the driver) */ - adau->mclk = NULL; - } else if (adau->mclk) { + /* Clock is optional (for the driver) */ + adau->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(adau->mclk)) + return PTR_ERR(adau->mclk); + + if (adau->mclk) { adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO; /* diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index 9f137a0634d5..24c7b9c84c19 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -42,7 +42,7 @@ static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", }, - .probe_new = adau1977_i2c_probe, + .probe = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, }; module_i2c_driver(adau1977_i2c_driver); diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c index afed48401b25..73f181f7757e 100644 --- a/sound/soc/codecs/adau7118-i2c.c +++ b/sound/soc/codecs/adau7118-i2c.c @@ -78,7 +78,7 @@ static struct i2c_driver adau7118_driver = { .name = "adau7118", .of_match_table = adau7118_of_match, }, - .probe_new = adau7118_probe_i2c, + .probe = adau7118_probe_i2c, .id_table = adau7118_id, }; module_i2c_driver(adau7118_driver); diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c index bf181bbaabed..78a317947df9 100644 --- a/sound/soc/codecs/adav803.c +++ b/sound/soc/codecs/adav803.c @@ -29,7 +29,7 @@ static struct i2c_driver adav803_driver = { .driver = { .name = "adav803", }, - .probe_new = adav803_probe, + .probe = adav803_probe, .id_table = adav803_id, }; module_i2c_driver(adav803_driver); diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index b6d9a10bdccd..e34e5533765c 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -264,8 +264,6 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data) struct ak4118_priv *ak4118 = data; struct snd_soc_component *component = ak4118->component; struct snd_kcontrol_new *kctl_new; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_id *id; unsigned int i; if (!component) @@ -273,13 +271,8 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data) for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) { kctl_new = &ak4118_iec958_controls[i]; - kctl = snd_soc_card_get_kcontrol(component->card, - kctl_new->name); - if (!kctl) - continue; - id = &kctl->id; - snd_ctl_notify(component->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, id); + + snd_soc_component_notify_control(component, kctl_new->name); } return IRQ_HANDLED; @@ -414,7 +407,7 @@ static struct i2c_driver ak4118_i2c_driver = { .of_match_table = of_match_ptr(ak4118_of_match), }, .id_table = ak4118_id_table, - .probe_new = ak4118_i2c_probe, + .probe = ak4118_i2c_probe, }; module_i2c_driver(ak4118_i2c_driver); diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c index 573389e402f8..f287acb98646 100644 --- a/sound/soc/codecs/ak4375.c +++ b/sound/soc/codecs/ak4375.c @@ -597,7 +597,7 @@ static struct i2c_driver ak4375_i2c_driver = { .pm = &ak4375_pm, .of_match_table = ak4375_of_match, }, - .probe_new = ak4375_i2c_probe, + .probe = ak4375_i2c_probe, .remove = ak4375_i2c_remove, }; module_i2c_driver(ak4375_i2c_driver); diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 8c69c8cf9b67..77678f85ad94 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -818,7 +818,7 @@ static struct i2c_driver ak4458_i2c_driver = { .pm = &ak4458_pm, .of_match_table = ak4458_of_match, }, - .probe_new = ak4458_i2c_probe, + .probe = ak4458_i2c_probe, .remove = ak4458_i2c_remove, }; diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 8c8c93eea704..904bf91090aa 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -439,7 +439,7 @@ static struct i2c_driver ak4535_i2c_driver = { .driver = { .name = "ak4535", }, - .probe_new = ak4535_i2c_probe, + .probe = ak4535_i2c_probe, .id_table = ak4535_i2c_id, }; diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index f75c19ef3551..ad56caec9dac 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -925,7 +925,7 @@ static struct i2c_driver ak4613_i2c_driver = { .name = "ak4613-codec", .of_match_table = ak4613_of_match, }, - .probe_new = ak4613_i2c_probe, + .probe = ak4613_i2c_probe, .id_table = ak4613_i2c_id, }; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 0d3ee195b3cc..5b7df2f0dd6a 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -628,7 +628,7 @@ static struct i2c_driver ak4641_i2c_driver = { .driver = { .name = "ak4641", }, - .probe_new = ak4641_i2c_probe, + .probe = ak4641_i2c_probe, .remove = ak4641_i2c_remove, .id_table = ak4641_i2c_id, }; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 914d5b1969f8..2a8984c1fa9c 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -698,7 +698,7 @@ static struct i2c_driver ak4642_i2c_driver = { .name = "ak4642-codec", .of_match_table = ak4642_of_match, }, - .probe_new = ak4642_i2c_probe, + .probe = ak4642_i2c_probe, .id_table = ak4642_i2c_id, }; diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index cd76765f8cc0..5b849b390c2a 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -655,7 +655,7 @@ static struct i2c_driver ak4671_i2c_driver = { .driver = { .name = "ak4671-codec", }, - .probe_new = ak4671_i2c_probe, + .probe = ak4671_i2c_probe, .id_table = ak4671_i2c_id, }; diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 60abcffe6a0c..442e2cb42df4 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -497,7 +497,7 @@ static struct i2c_driver ak5558_i2c_driver = { .of_match_table = of_match_ptr(ak5558_i2c_dt_ids), .pm = &ak5558_pm, }, - .probe_new = ak5558_i2c_probe, + .probe = ak5558_i2c_probe, .remove = ak5558_i2c_remove, }; diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 9ef01b1dd294..b24c32206884 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -1083,7 +1083,7 @@ static struct i2c_driver alc5623_i2c_driver = { .name = "alc562x-codec", .of_match_table = of_match_ptr(alc5623_of_match), }, - .probe_new = alc5623_i2c_probe, + .probe = alc5623_i2c_probe, .id_table = alc5623_i2c_table, }; diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index a770704a4e17..d5021f266930 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1182,7 +1182,7 @@ static struct i2c_driver alc5632_i2c_driver = { .name = "alc5632", .of_match_table = of_match_ptr(alc5632_of_match), }, - .probe_new = alc5632_i2c_probe, + .probe = alc5632_i2c_probe, .id_table = alc5632_i2c_table, }; diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index afdce6b7fa26..9dcd75dd799a 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -570,7 +570,7 @@ static struct i2c_driver aw88395_i2c_driver = { .driver = { .name = AW88395_I2C_NAME, }, - .probe_new = aw88395_i2c_probe, + .probe = aw88395_i2c_probe, .id_table = aw88395_i2c_id, }; module_i2c_driver(aw88395_i2c_driver); diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c new file mode 100644 index 000000000000..ab99effa6874 --- /dev/null +++ b/sound/soc/codecs/chv3-codec.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/module.h> +#include <sound/soc.h> + +static struct snd_soc_dai_driver chv3_codec_dai = { + .name = "chv3-codec-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 8, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_chv3_codec = { +}; + +static int chv3_codec_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_chv3_codec, &chv3_codec_dai, 1); +} + +static const struct of_device_id chv3_codec_of_match[] = { + { .compatible = "google,chv3-codec", }, + { } +}; + +static struct platform_driver chv3_codec_platform_driver = { + .driver = { + .name = "chv3-codec", + .of_match_table = chv3_codec_of_match, + }, + .probe = chv3_codec_probe, +}; +module_platform_driver(chv3_codec_platform_driver); + +MODULE_DESCRIPTION("ASoC Chameleon v3 codec driver"); +MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index dc7a58d68076..6e658bb16fb0 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -260,7 +260,7 @@ static const struct regmap_config cs35l32_regmap = { .volatile_reg = cs35l32_volatile_register, .readable_reg = cs35l32_readable_register, .precious_reg = cs35l32_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -572,7 +572,7 @@ static struct i2c_driver cs35l32_i2c_driver = { .of_match_table = cs35l32_of_match, }, .id_table = cs35l32_id, - .probe_new = cs35l32_i2c_probe, + .probe = cs35l32_i2c_probe, .remove = cs35l32_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index 15e79168d256..9968c2e189e6 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -852,7 +852,7 @@ static const struct regmap_config cs35l33_regmap = { .volatile_reg = cs35l33_volatile_register, .readable_reg = cs35l33_readable_register, .writeable_reg = cs35l33_writeable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1282,7 +1282,7 @@ static struct i2c_driver cs35l33_i2c_driver = { }, .id_table = cs35l33_id, - .probe_new = cs35l33_i2c_probe, + .probe = cs35l33_i2c_probe, .remove = cs35l33_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index b3f98023e6a7..6974dd461410 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -799,7 +799,7 @@ static struct regmap_config cs35l34_regmap = { .volatile_reg = cs35l34_volatile_register, .readable_reg = cs35l34_readable_register, .precious_reg = cs35l34_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -1213,7 +1213,7 @@ static struct i2c_driver cs35l34_i2c_driver = { }, .id_table = cs35l34_id, - .probe_new = cs35l34_i2c_probe, + .probe = cs35l34_i2c_probe, .remove = cs35l34_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 947a440a3a47..0a4b5aa78185 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1099,7 +1099,7 @@ static struct regmap_config cs35l35_regmap = { .volatile_reg = cs35l35_volatile_register, .readable_reg = cs35l35_readable_register, .precious_reg = cs35l35_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1654,7 +1654,7 @@ static struct i2c_driver cs35l35_i2c_driver = { .of_match_table = cs35l35_of_match, }, .id_table = cs35l35_id, - .probe_new = cs35l35_i2c_probe, + .probe = cs35l35_i2c_probe, .remove = cs35l35_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index a078dd422ea1..04ba7f25012e 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1944,7 +1944,7 @@ static struct i2c_driver cs35l36_i2c_driver = { .of_match_table = cs35l36_of_match, }, .id_table = cs35l36_id, - .probe_new = cs35l36_i2c_probe, + .probe = cs35l36_i2c_probe, .remove = cs35l36_i2c_remove, }; module_i2c_driver(cs35l36_i2c_driver); diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index 3676b596f60b..7ea890d7d387 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -88,7 +88,7 @@ static struct i2c_driver cs35l41_i2c_driver = { .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, .id_table = cs35l41_id_i2c, - .probe_new = cs35l41_i2c_probe, + .probe = cs35l41_i2c_probe, .remove = cs35l41_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c index 562f73df7afa..77e0f8750f37 100644 --- a/sound/soc/codecs/cs35l45-i2c.c +++ b/sound/soc/codecs/cs35l45-i2c.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-i2c.c -- CS35L45 I2C driver // @@ -65,12 +65,12 @@ static struct i2c_driver cs35l45_i2c_driver = { .pm = &cs35l45_pm_ops, }, .id_table = cs35l45_id_i2c, - .probe_new = cs35l45_i2c_probe, + .probe = cs35l45_i2c_probe, .remove = cs35l45_i2c_remove, }; module_i2c_driver(cs35l45_i2c_driver); MODULE_DESCRIPTION("I2C CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c index a00b23b4180c..5efb77530cc3 100644 --- a/sound/soc/codecs/cs35l45-spi.c +++ b/sound/soc/codecs/cs35l45-spi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-spi.c -- CS35L45 SPI driver // @@ -74,5 +74,5 @@ module_spi_driver(cs35l45_spi_driver); MODULE_DESCRIPTION("SPI CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index 46610e64e818..066f83c0c7ac 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-tables.c -- CS35L45 ALSA SoC audio driver // diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index c31597f6bfae..d1edb9876c10 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45.c - CS35L45 ALSA SoC audio driver // @@ -1296,4 +1296,4 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45); MODULE_DESCRIPTION("ASoC CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h index 0da28439f628..61135a316df3 100644 --- a/sound/soc/codecs/cs35l45.h +++ b/sound/soc/codecs/cs35l45.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * cs35l45.h - CS35L45 ALSA SoC audio driver * diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 295caad26224..ed2a41943d97 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -68,7 +68,7 @@ static struct i2c_driver cs35l56_i2c_driver = { .pm = &cs35l56_pm_ops_i2c_spi, }, .id_table = cs35l56_id_i2c, - .probe_new = cs35l56_i2c_probe, + .probe = cs35l56_i2c_probe, .remove = cs35l56_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index e0d2b9bb2326..c03f9d3c9a13 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -822,25 +822,23 @@ static void cs35l56_system_reset(struct cs35l56_private *cs35l56) regcache_cache_only(cs35l56->regmap, false); } -static void cs35l56_dsp_work(struct work_struct *work) +static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) { - struct cs35l56_private *cs35l56 = container_of(work, - struct cs35l56_private, - dsp_work); - unsigned int reg; - unsigned int val; - int ret = 0; - - if (!cs35l56->init_done) - return; - - cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", - cs35l56->secured ? "s" : "", cs35l56->rev); + int ret; - if (!cs35l56->dsp.part) - return; + /* Use wm_adsp to load and apply the firmware patch and coefficient files */ + ret = wm_adsp_power_up(&cs35l56->dsp); + if (ret) + dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + else + cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_REINIT); +} - pm_runtime_get_sync(cs35l56->dev); +static void cs35l56_patch(struct cs35l56_private *cs35l56) +{ + unsigned int reg; + unsigned int val; + int ret; /* * Disable SoundWire interrupts to prevent race with IRQ work. @@ -907,9 +905,6 @@ static void cs35l56_dsp_work(struct work_struct *work) err_unlock: mutex_unlock(&cs35l56->irq_lock); err: - pm_runtime_mark_last_busy(cs35l56->dev); - pm_runtime_put_autosuspend(cs35l56->dev); - /* Re-enable SoundWire interrupts */ if (cs35l56->sdw_peripheral) { cs35l56->sdw_irq_no_unmask = false; @@ -918,6 +913,32 @@ err: } } +static void cs35l56_dsp_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + dsp_work); + + if (!cs35l56->init_done) + return; + + pm_runtime_get_sync(cs35l56->dev); + + /* + * When the device is running in secure mode the firmware files can + * only contain insecure tunings and therefore we do not need to + * shutdown the firmware to apply them and can use the lower cost + * reinit sequence instead. + */ + if (cs35l56->secured) + cs35l56_secure_patch(cs35l56); + else + cs35l56_patch(cs35l56); + + pm_runtime_mark_last_busy(cs35l56->dev); + pm_runtime_put_autosuspend(cs35l56->dev); +} + static int cs35l56_component_probe(struct snd_soc_component *component) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); @@ -1505,6 +1526,12 @@ int cs35l56_init(struct cs35l56_private *cs35l56) dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", cs35l56->secured ? "s" : "", cs35l56->rev, otpid); + /* Populate the DSP information with the revision and security state */ + cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", + cs35l56->secured ? "s" : "", cs35l56->rev); + if (!cs35l56->dsp.part) + return -ENOMEM; + /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1, diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c index dee1a6662c2e..69287ba7e955 100644 --- a/sound/soc/codecs/cs4234.c +++ b/sound/soc/codecs/cs4234.c @@ -675,7 +675,7 @@ static const struct regmap_config cs4234_regmap = { .writeable_reg = cs4234_writeable_register, .reg_defaults = cs4234_default_reg, .num_reg_defaults = ARRAY_SIZE(cs4234_default_reg), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -906,7 +906,7 @@ static struct i2c_driver cs4234_i2c_driver = { .pm = &cs4234_pm, .of_match_table = cs4234_of_match, }, - .probe_new = cs4234_i2c_probe, + .probe = cs4234_i2c_probe, .remove = cs4234_i2c_remove, }; module_i2c_driver(cs4234_i2c_driver); diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 3573363b7e31..0cfc5ab36a13 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -649,7 +649,7 @@ static struct i2c_driver cs4265_i2c_driver = { .of_match_table = cs4265_of_match, }, .id_table = cs4265_id, - .probe_new = cs4265_i2c_probe, + .probe = cs4265_i2c_probe, .remove = cs4265_i2c_remove, }; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 1b640d8232ba..ab32f15e3b44 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -751,7 +751,7 @@ static struct i2c_driver cs4270_i2c_driver = { .of_match_table = cs4270_of_match, }, .id_table = cs4270_id, - .probe_new = cs4270_i2c_probe, + .probe = cs4270_i2c_probe, .remove = cs4270_i2c_remove, }; diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c index 4033be1c3bc1..89fe7d1665df 100644 --- a/sound/soc/codecs/cs4271-i2c.c +++ b/sound/soc/codecs/cs4271-i2c.c @@ -33,7 +33,7 @@ static struct i2c_driver cs4271_i2c_driver = { .name = "cs4271", .of_match_table = of_match_ptr(cs4271_dt_ids), }, - .probe_new = cs4271_i2c_probe, + .probe = cs4271_i2c_probe, .id_table = cs4271_i2c_id, }; module_i2c_driver(cs4271_i2c_driver); diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c index 67b253287daf..2552a1e6b82f 100644 --- a/sound/soc/codecs/cs42l42-i2c.c +++ b/sound/soc/codecs/cs42l42-i2c.c @@ -92,7 +92,7 @@ static struct i2c_driver cs42l42_i2c_driver = { .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), }, .id_table = cs42l42_id, - .probe_new = cs42l42_i2c_probe, + .probe = cs42l42_i2c_probe, .remove = cs42l42_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index e3edaa1a2761..a0de0329406a 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -393,7 +393,7 @@ const struct regmap_config cs42l42_regmap = { .max_register = CS42L42_MAX_REGISTER, .reg_defaults = cs42l42_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -646,12 +646,19 @@ static const struct cs42l42_pll_params pll_ratio_table[] = { { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1}, { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, + { 6144000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1}, { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1}, { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1} diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 85238339fbca..b2106ff6a7cb 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -43,7 +43,7 @@ static struct i2c_driver cs42l51_i2c_driver = { .of_match_table = cs42l51_of_match, .pm = &cs42l51_pm_ops, }, - .probe_new = cs42l51_i2c_probe, + .probe = cs42l51_i2c_probe, .remove = cs42l51_i2c_remove, .id_table = cs42l51_i2c_id, }; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index e88d9ff95cdf..a67cd3ee84e0 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -724,12 +724,9 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) dev_set_drvdata(dev, cs42l51); cs42l51->regmap = regmap; - cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); - if (IS_ERR(cs42l51->mclk_handle)) { - if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) - return PTR_ERR(cs42l51->mclk_handle); - cs42l51->mclk_handle = NULL; - } + cs42l51->mclk_handle = devm_clk_get_optional(dev, "MCLK"); + if (IS_ERR(cs42l51->mclk_handle)) + return PTR_ERR(cs42l51->mclk_handle); for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) cs42l51->supplies[i].supply = cs42l51_supply_names[i]; diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 90bf535fc5a5..1f1ded0ff0ac 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1226,7 +1226,7 @@ static struct i2c_driver cs42l52_i2c_driver = { .of_match_table = cs42l52_of_match, }, .id_table = cs42l52_id, - .probe_new = cs42l52_i2c_probe, + .probe = cs42l52_i2c_probe, }; module_i2c_driver(cs42l52_i2c_driver); diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 3b0e715549c9..4c646e8d72aa 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1341,7 +1341,7 @@ static struct i2c_driver cs42l56_i2c_driver = { .of_match_table = cs42l56_of_match, }, .id_table = cs42l56_id, - .probe_new = cs42l56_i2c_probe, + .probe = cs42l56_i2c_probe, .remove = cs42l56_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 0a146319755a..6ab67d196d10 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1267,7 +1267,7 @@ static const struct regmap_config cs42l73_regmap = { .num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults), .volatile_reg = cs42l73_volatile_register, .readable_reg = cs42l73_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -1384,7 +1384,7 @@ static struct i2c_driver cs42l73_i2c_driver = { .of_match_table = cs42l73_of_match, }, .id_table = cs42l73_id, - .probe_new = cs42l73_i2c_probe, + .probe = cs42l73_i2c_probe, }; diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c index 37629ebd90e0..f482b6a4f5c3 100644 --- a/sound/soc/codecs/cs42l83-i2c.c +++ b/sound/soc/codecs/cs42l83-i2c.c @@ -158,7 +158,7 @@ static const struct regmap_config cs42l83_regmap = { .max_register = CS42L42_MAX_REGISTER, .reg_defaults = cs42l83_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -228,7 +228,7 @@ static struct i2c_driver cs42l83_i2c_driver = { .pm = &cs42l83_i2c_pm_ops, .of_match_table = of_match_ptr(cs42l83_of_match), }, - .probe_new = cs42l83_i2c_probe, + .probe = cs42l83_i2c_probe, .remove = cs42l83_i2c_remove, }; diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index 052ffb7dcfc6..a422472820fb 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -70,7 +70,7 @@ static struct i2c_driver cs42xx8_i2c_driver = { .pm = &cs42xx8_pm, .of_match_table = cs42xx8_of_match, }, - .probe_new = cs42xx8_i2c_probe, + .probe = cs42xx8_i2c_probe, .remove = cs42xx8_i2c_remove, .id_table = cs42xx8_i2c_id, }; diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index db39abb2a31b..3292405024bc 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -2357,7 +2357,7 @@ static const struct regmap_config cs43130_regmap = { .readable_reg = cs43130_readable_register, .precious_reg = cs43130_precious_register, .volatile_reg = cs43130_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, /* needed for regcache_sync */ .use_single_read = true, .use_single_write = true, @@ -2697,7 +2697,7 @@ static struct i2c_driver cs43130_i2c_driver = { .pm = &cs43130_runtime_pm, }, .id_table = cs43130_i2c_id, - .probe_new = cs43130_i2c_probe, + .probe = cs43130_i2c_probe, .remove = cs43130_i2c_remove, }; diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c index ac1696034846..2ceca5d0e5bf 100644 --- a/sound/soc/codecs/cs4341.c +++ b/sound/soc/codecs/cs4341.c @@ -258,7 +258,7 @@ static struct i2c_driver cs4341_i2c_driver = { .name = "cs4341-i2c", .of_match_table = of_match_ptr(cs4341_dt_ids), }, - .probe_new = cs4341_i2c_probe, + .probe = cs4341_i2c_probe, .id_table = cs4341_i2c_id, }; #endif diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index ba94ffd0a7e4..8365dd0ebe2a 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -375,7 +375,7 @@ static struct i2c_driver cs4349_i2c_driver = { .pm = &cs4349_runtime_pm, }, .id_table = cs4349_i2c_id, - .probe_new = cs4349_i2c_probe, + .probe = cs4349_i2c_probe, .remove = cs4349_i2c_remove, }; diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 69db0013d243..f4065555c36e 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -911,7 +911,7 @@ static struct regmap_config cs53l30_regmap = { .volatile_reg = cs53l30_volatile_register, .writeable_reg = cs53l30_writeable_register, .readable_reg = cs53l30_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -990,14 +990,10 @@ static int cs53l30_i2c_probe(struct i2c_client *client) } /* Check if MCLK provided */ - cs53l30->mclk = devm_clk_get(dev, "mclk"); + cs53l30->mclk = devm_clk_get_optional(dev, "mclk"); if (IS_ERR(cs53l30->mclk)) { - if (PTR_ERR(cs53l30->mclk) != -ENOENT) { - ret = PTR_ERR(cs53l30->mclk); - goto error; - } - /* Otherwise mark the mclk pointer to NULL */ - cs53l30->mclk = NULL; + ret = PTR_ERR(cs53l30->mclk); + goto error; } /* Fetch the MUTE control */ @@ -1121,7 +1117,7 @@ static struct i2c_driver cs53l30_i2c_driver = { .pm = &cs53l30_runtime_pm, }, .id_table = cs53l30_id, - .probe_new = cs53l30_i2c_probe, + .probe = cs53l30_i2c_probe, .remove = cs53l30_i2c_remove, }; diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 5deceaa89282..082231088a26 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1706,7 +1706,7 @@ static struct i2c_driver cx2072x_i2c_driver = { .acpi_match_table = ACPI_PTR(cx2072x_acpi_match), .pm = &cx2072x_runtime_pm, }, - .probe_new = cx2072x_i2c_probe, + .probe = cx2072x_i2c_probe, .remove = cx2072x_i2c_remove, .id_table = cx2072x_i2c_id, }; diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index f838466bfebf..1e232d01809c 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1248,7 +1248,7 @@ static struct i2c_driver da7210_i2c_driver = { .driver = { .name = "da7210", }, - .probe_new = da7210_i2c_probe, + .probe = da7210_i2c_probe, .id_table = da7210_i2c_id, }; #endif diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 1c1f211a8e2e..3a6449c44b23 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -2069,7 +2069,7 @@ static struct i2c_driver da7213_i2c_driver = { .acpi_match_table = ACPI_PTR(da7213_acpi_match), .pm = &da7213_pm, }, - .probe_new = da7213_i2c_probe, + .probe = da7213_i2c_probe, .remove = da7213_i2c_remove, .id_table = da7213_i2c_id, }; diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index d9c28e701613..3f456b08b809 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -3317,7 +3317,7 @@ static struct i2c_driver da7218_i2c_driver = { .name = "da7218", .of_match_table = da7218_of_match, }, - .probe_new = da7218_i2c_probe, + .probe = da7218_i2c_probe, .id_table = da7218_i2c_id, }; diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index 993a0d00bc48..c65256bd526d 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -571,16 +571,29 @@ static enum da7219_aad_jack_ins_deb } } +static enum da7219_aad_jack_ins_det_pty + da7219_aad_fw_jack_ins_det_pty(struct device *dev, const char *str) +{ + if (!strcmp(str, "low")) { + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } else if (!strcmp(str, "high")) { + return DA7219_AAD_JACK_INS_DET_PTY_HIGH; + } else { + dev_warn(dev, "Invalid jack insertion detection polarity"); + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } +} + static enum da7219_aad_jack_det_rate da7219_aad_fw_jack_det_rate(struct device *dev, const char *str) { - if (!strcmp(str, "32ms_64ms")) { + if (!strcmp(str, "32_64")) { return DA7219_AAD_JACK_DET_RATE_32_64MS; - } else if (!strcmp(str, "64ms_128ms")) { + } else if (!strcmp(str, "64_128")) { return DA7219_AAD_JACK_DET_RATE_64_128MS; - } else if (!strcmp(str, "128ms_256ms")) { + } else if (!strcmp(str, "128_256")) { return DA7219_AAD_JACK_DET_RATE_128_256MS; - } else if (!strcmp(str, "256ms_512ms")) { + } else if (!strcmp(str, "256_512")) { return DA7219_AAD_JACK_DET_RATE_256_512MS; } else { dev_warn(dev, "Invalid jack detect rate"); @@ -688,6 +701,12 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) else aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; + if (!fwnode_property_read_string(aad_np, "dlg,jack-ins-det-pty", &fw_str)) + aad_pdata->jack_ins_det_pty = + da7219_aad_fw_jack_ins_det_pty(dev, fw_str); + else + aad_pdata->jack_ins_det_pty = DA7219_AAD_JACK_INS_DET_PTY_LOW; + if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str)) aad_pdata->jack_det_rate = da7219_aad_fw_jack_det_rate(dev, fw_str); @@ -849,6 +868,21 @@ static void da7219_aad_handle_pdata(struct snd_soc_component *component) mask |= DA7219_ADC_1_BIT_REPEAT_MASK; } snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg); + + switch (aad_pdata->jack_ins_det_pty) { + case DA7219_AAD_JACK_INS_DET_PTY_LOW: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x80); + snd_soc_component_write(component, 0xF0, 0x00); + break; + case DA7219_AAD_JACK_INS_DET_PTY_HIGH: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x00); + snd_soc_component_write(component, 0xF0, 0x00); + break; + default: + break; + } } } diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 7468ee4af2ea..600c2db58756 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -2714,7 +2714,7 @@ static struct i2c_driver da7219_i2c_driver = { .of_match_table = of_match_ptr(da7219_of_match), .acpi_match_table = ACPI_PTR(da7219_acpi_match), }, - .probe_new = da7219_i2c_probe, + .probe = da7219_i2c_probe, .id_table = da7219_i2c_id, }; diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 2c5b0b74201c..f8ca1afa8af5 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1555,7 +1555,7 @@ static struct i2c_driver da732x_i2c_driver = { .driver = { .name = "da7320", }, - .probe_new = da732x_i2c_probe, + .probe = da732x_i2c_probe, .id_table = da732x_i2c_id, }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 28043b4530df..ae20086777b5 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1531,7 +1531,7 @@ static struct i2c_driver da9055_i2c_driver = { .name = "da9055-codec", .of_match_table = of_match_ptr(da9055_of_match), }, - .probe_new = da9055_i2c_probe, + .probe = da9055_i2c_probe, .id_table = da9055_i2c_id, }; diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index a27d80956459..34cf60769b62 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -52,7 +52,12 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0); -static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv, + 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0), + 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 0), +); + static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv, 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0), 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0), @@ -115,7 +120,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = { alc_max_gain_tlv), SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0, alc_min_gain_tlv), - SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 10, 0, + SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 11, 0, alc_target_tlv), SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0), SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0), @@ -364,13 +369,11 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, int count = 0; es8316->sysclk = freq; + es8316->sysclk_constraints.list = NULL; + es8316->sysclk_constraints.count = 0; - if (freq == 0) { - es8316->sysclk_constraints.list = NULL; - es8316->sysclk_constraints.count = 0; - + if (freq == 0) return 0; - } ret = clk_set_rate(es8316->mclk, freq); if (ret) @@ -386,8 +389,10 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, es8316->allowed_rates[count++] = freq / ratio; } - es8316->sysclk_constraints.list = es8316->allowed_rates; - es8316->sysclk_constraints.count = count; + if (count) { + es8316->sysclk_constraints.list = es8316->allowed_rates; + es8316->sysclk_constraints.count = count; + } return 0; } @@ -820,7 +825,7 @@ static const struct regmap_config es8316_regmap = { .use_single_write = true, .max_register = 0x53, .volatile_reg = es8316_volatile_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int es8316_i2c_probe(struct i2c_client *i2c_client) @@ -887,7 +892,7 @@ static struct i2c_driver es8316_i2c_driver = { .acpi_match_table = ACPI_PTR(es8316_acpi_match), .of_match_table = of_match_ptr(es8316_of_match), }, - .probe_new = es8316_i2c_probe, + .probe = es8316_i2c_probe, .id_table = es8316_i2c_id, }; module_i2c_driver(es8316_i2c_driver); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 28a0565c2a95..7cb5b57ae655 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -896,7 +896,7 @@ static struct i2c_driver es8326_i2c_driver = { .acpi_match_table = ACPI_PTR(es8326_acpi_match), .of_match_table = of_match_ptr(es8326_of_match), }, - .probe_new = es8326_i2c_probe, + .probe = es8326_i2c_probe, .id_table = es8326_i2c_id, }; module_i2c_driver(es8326_i2c_driver); diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c index 68072e99fcc7..3c4aaa0032a0 100644 --- a/sound/soc/codecs/es8328-i2c.c +++ b/sound/soc/codecs/es8328-i2c.c @@ -40,7 +40,7 @@ static struct i2c_driver es8328_i2c_driver = { .name = "es8328", .of_match_table = es8328_of_match, }, - .probe_new = es8328_i2c_probe, + .probe = es8328_i2c_probe, .id_table = es8328_id, }; diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 160adc706cc6..0bd9ba5a11b4 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -822,7 +822,7 @@ const struct regmap_config es8328_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = ES8328_REG_MAX, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c index 6d980fbc4207..d21f69f05342 100644 --- a/sound/soc/codecs/hdmi-codec.c +++ b/sound/soc/codecs/hdmi-codec.c @@ -495,31 +495,43 @@ static int hdmi_codec_fill_codec_params(struct snd_soc_dai *dai, struct hdmi_codec_params *hp) { struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai); - int idx; - - /* Select a channel allocation that matches with ELD and pcm channels */ - idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels); - if (idx < 0) { - dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", - idx); - hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; - return idx; + int idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + u8 ca_id = 0; + bool pcm_audio = !(hcp->iec_status[0] & IEC958_AES0_NONAUDIO); + + if (pcm_audio) { + /* Select a channel allocation that matches with ELD and pcm channels */ + idx = hdmi_codec_get_ch_alloc_table_idx(hcp, channels); + + if (idx < 0) { + dev_err(dai->dev, "Not able to map channels to speakers (%d)\n", + idx); + hcp->chmap_idx = HDMI_CODEC_CHMAP_IDX_UNKNOWN; + return idx; + } + + ca_id = hdmi_codec_channel_alloc[idx].ca_id; } memset(hp, 0, sizeof(*hp)); hdmi_audio_infoframe_init(&hp->cea); - hp->cea.channels = channels; + + if (pcm_audio) + hp->cea.channels = channels; + else + hp->cea.channels = 0; + hp->cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM; hp->cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM; hp->cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM; - hp->cea.channel_allocation = hdmi_codec_channel_alloc[idx].ca_id; + hp->cea.channel_allocation = ca_id; hp->sample_width = sample_width; hp->sample_rate = sample_rate; hp->channels = channels; - hcp->chmap_idx = hdmi_codec_channel_alloc[idx].ca_id; + hcp->chmap_idx = idx; return 0; } diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index 50105d72b2b7..f9456133a89a 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -1142,7 +1142,7 @@ static struct i2c_driver isabelle_i2c_driver = { .driver = { .name = "isabelle", }, - .probe_new = isabelle_i2c_probe, + .probe = isabelle_i2c_probe, .id_table = isabelle_i2c_id, }; diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index dba161305de8..e7542f71323d 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -137,7 +137,7 @@ static struct i2c_driver lm4857_i2c_driver = { .driver = { .name = "lm4857", }, - .probe_new = lm4857_i2c_probe, + .probe = lm4857_i2c_probe, .id_table = lm4857_i2c_id, }; diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index a2e782cc4276..a4094689b3dd 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1451,7 +1451,7 @@ static struct i2c_driver lm49453_i2c_driver = { .driver = { .name = "lm49453", }, - .probe_new = lm49453_i2c_probe, + .probe = lm49453_i2c_probe, .id_table = lm49453_i2c_id, }; diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index d711eb1da0a8..d22b4ba51ed8 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -214,7 +214,7 @@ static struct i2c_driver max9768_i2c_driver = { .driver = { .name = "max9768", }, - .probe_new = max9768_i2c_probe, + .probe = max9768_i2c_probe, .id_table = max9768_i2c_id, }; module_i2c_driver(max9768_i2c_driver); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 405ec16be2b6..8b56ee550c09 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -310,24 +310,24 @@ static const struct regmap_config max98088_regmap = { static void m98088_eq_band(struct snd_soc_component *component, unsigned int dai, unsigned int band, u16 *coefs) { - unsigned int eq_reg; - unsigned int i; + unsigned int eq_reg; + unsigned int i; if (WARN_ON(band > 4) || WARN_ON(dai > 1)) return; - /* Load the base register address */ - eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; + /* Load the base register address */ + eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; - /* Add the band address offset, note adjustment for word address */ - eq_reg += band * (M98088_COEFS_PER_BAND << 1); + /* Add the band address offset, note adjustment for word address */ + eq_reg += band * (M98088_COEFS_PER_BAND << 1); - /* Step through the registers and coefs */ - for (i = 0; i < M98088_COEFS_PER_BAND; i++) { - snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i])); - snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i])); - } + /* Step through the registers and coefs */ + for (i = 0; i < M98088_COEFS_PER_BAND; i++) { + snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i])); + snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i])); + } } /* @@ -1789,7 +1789,7 @@ static struct i2c_driver max98088_i2c_driver = { .name = "max98088", .of_match_table = of_match_ptr(max98088_of_match), }, - .probe_new = max98088_i2c_probe, + .probe = max98088_i2c_probe, .id_table = max98088_i2c_id, }; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index b419c49e1e08..2adf744c6526 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -10,7 +10,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/acpi.h> @@ -1582,7 +1581,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); struct max98090_cdata *cdata; - u8 regval; + u8 regval, tdm_regval; max98090->dai_fmt = fmt; cdata = &max98090->dai[0]; @@ -1591,6 +1590,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, cdata->fmt = fmt; regval = 0; + tdm_regval = 0; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: /* Set to consumer mode PLL - MAS mode off */ @@ -1636,7 +1636,8 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, regval |= M98090_RJ_MASK; break; case SND_SOC_DAIFMT_DSP_A: - /* Not supported mode */ + tdm_regval |= M98090_TDM_MASK; + break; default: dev_err(component->dev, "DAI format unsupported"); return -EINVAL; @@ -1665,11 +1666,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, * seen for the case of TDM mode. The remaining cases have * normal logic. */ - if (max98090->tdm_slots > 1) + if (tdm_regval) regval ^= M98090_BCI_MASK; snd_soc_component_write(component, M98090_REG_INTERFACE_FORMAT, regval); + + regval = 0; + if (tdm_regval) + regval = max98090->tdm_lslot << M98090_TDM_SLOTL_SHIFT | + max98090->tdm_rslot << M98090_TDM_SLOTR_SHIFT | + 0 << M98090_TDM_SLOTDLY_SHIFT; + + snd_soc_component_write(component, M98090_REG_TDM_FORMAT, regval); + snd_soc_component_write(component, M98090_REG_TDM_CONTROL, tdm_regval); } return 0; @@ -1680,33 +1690,22 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, { struct snd_soc_component *component = codec_dai->component; struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); - struct max98090_cdata *cdata; - cdata = &max98090->dai[0]; if (slots < 0 || slots > 4) return -EINVAL; - max98090->tdm_slots = slots; - max98090->tdm_width = slot_width; - - if (max98090->tdm_slots > 1) { - /* SLOTL SLOTR SLOTDLY */ - snd_soc_component_write(component, M98090_REG_TDM_FORMAT, - 0 << M98090_TDM_SLOTL_SHIFT | - 1 << M98090_TDM_SLOTR_SHIFT | - 0 << M98090_TDM_SLOTDLY_SHIFT); - - /* FSW TDM */ - snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL, - M98090_TDM_MASK, - M98090_TDM_MASK); - } + if (slot_width != 16) + return -EINVAL; - /* - * Normally advisable to set TDM first, but this permits either order - */ - cdata->fmt = 0; - max98090_dai_set_fmt(codec_dai, max98090->dai_fmt); + if (rx_mask != tx_mask) + return -EINVAL; + + if (!rx_mask) + return -EINVAL; + + max98090->tdm_slots = slots; + max98090->tdm_lslot = ffs(rx_mask) - 1; + max98090->tdm_rslot = fls(rx_mask) - 1; return 0; } @@ -2409,6 +2408,9 @@ static int max98090_probe(struct snd_soc_component *component) max98090->pa1en = 0; max98090->pa2en = 0; + max98090->tdm_lslot = 0; + max98090->tdm_rslot = 1; + ret = snd_soc_component_read(component, M98090_REG_REVISION_ID); if (ret < 0) { dev_err(component->dev, "Failed to read device revision: %d\n", @@ -2691,7 +2693,7 @@ static struct i2c_driver max98090_i2c_driver = { .of_match_table = of_match_ptr(max98090_of_match), .acpi_match_table = ACPI_PTR(max98090_acpi_match), }, - .probe_new = max98090_i2c_probe, + .probe = max98090_i2c_probe, .shutdown = max98090_i2c_shutdown, .remove = max98090_i2c_remove, .id_table = max98090_i2c_id, diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index a197114b0dad..6ce8dd176e48 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -1533,7 +1533,8 @@ struct max98090_priv { struct snd_soc_jack *jack; unsigned int dai_fmt; int tdm_slots; - int tdm_width; + int tdm_lslot; + int tdm_rslot; u8 lin_state; unsigned int pa1en; unsigned int pa2en; diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 44aa58fcc23f..7e525d49328d 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2155,7 +2155,7 @@ static struct i2c_driver max98095_i2c_driver = { .name = "max98095", .of_match_table = of_match_ptr(max98095_of_match), }, - .probe_new = max98095_i2c_probe, + .probe = max98095_i2c_probe, .id_table = max98095_i2c_id, }; diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index e6b84e222b50..b5c69bba0e48 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -15,11 +15,6 @@ #include "max98363.h" static struct reg_default max98363_reg[] = { - {MAX98363_R2001_INTR_RAW, 0x0}, - {MAX98363_R2003_INTR_STATE, 0x0}, - {MAX98363_R2005_INTR_FALG, 0x0}, - {MAX98363_R2007_INTR_EN, 0x0}, - {MAX98363_R2009_INTR_CLR, 0x0}, {MAX98363_R2021_ERR_MON_CTRL, 0x0}, {MAX98363_R2022_SPK_MON_THRESH, 0x0}, {MAX98363_R2023_SPK_MON_DURATION, 0x0}, @@ -28,7 +23,6 @@ static struct reg_default max98363_reg[] = { {MAX98363_R2040_AMP_VOL, 0x0}, {MAX98363_R2041_AMP_GAIN, 0x5}, {MAX98363_R2042_DSP_CFG, 0x0}, - {MAX98363_R21FF_REV_ID, 0x0}, }; static bool max98363_readable_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index bac9d1bcf60a..f0e49179c38f 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -419,7 +419,7 @@ static struct i2c_driver max98371_i2c_driver = { .name = "max98371", .of_match_table = of_match_ptr(max98371_of_match), }, - .probe_new = max98371_i2c_probe, + .probe = max98371_i2c_probe, .id_table = max98371_i2c_id, }; diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index ec0905df65d1..0fa5ceca62a2 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -9,7 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/of.h> #include <linux/of_gpio.h> -#include <linux/pm_runtime.h> +#include <linux/pm.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/cdev.h> @@ -624,7 +624,7 @@ static struct i2c_driver max98373_i2c_driver = { .acpi_match_table = ACPI_PTR(max98373_acpi_match), .pm = &max98373_pm, }, - .probe_new = max98373_i2c_probe, + .probe = max98373_i2c_probe, .id_table = max98373_i2c_id, }; diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c new file mode 100644 index 000000000000..cde5e85946cb --- /dev/null +++ b/sound/soc/codecs/max98388.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, Analog Devices Inc. + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max98388.h" + +static struct reg_default max98388_reg[] = { + {MAX98388_R2000_SW_RESET, 0x00}, + {MAX98388_R2001_INT_RAW1, 0x00}, + {MAX98388_R2002_INT_RAW2, 0x00}, + {MAX98388_R2004_INT_STATE1, 0x00}, + {MAX98388_R2005_INT_STATE2, 0x00}, + {MAX98388_R2020_THERM_WARN_THRESH, 0x0A}, + {MAX98388_R2031_SPK_MON_THRESH, 0x58}, + {MAX98388_R2032_SPK_MON_LD_SEL, 0x08}, + {MAX98388_R2033_SPK_MON_DURATION, 0x02}, + {MAX98388_R2037_ERR_MON_CTRL, 0x01}, + {MAX98388_R2040_PCM_MODE_CFG, 0xC0}, + {MAX98388_R2041_PCM_CLK_SETUP, 0x04}, + {MAX98388_R2042_PCM_SR_SETUP, 0x88}, + {MAX98388_R2044_PCM_TX_CTRL1, 0x00}, + {MAX98388_R2045_PCM_TX_CTRL2, 0x00}, + {MAX98388_R2050_PCM_TX_HIZ_CTRL1, 0xFF}, + {MAX98388_R2051_PCM_TX_HIZ_CTRL2, 0xFF}, + {MAX98388_R2052_PCM_TX_HIZ_CTRL3, 0xFF}, + {MAX98388_R2053_PCM_TX_HIZ_CTRL4, 0xFF}, + {MAX98388_R2054_PCM_TX_HIZ_CTRL5, 0xFF}, + {MAX98388_R2055_PCM_TX_HIZ_CTRL6, 0xFF}, + {MAX98388_R2056_PCM_TX_HIZ_CTRL7, 0xFF}, + {MAX98388_R2057_PCM_TX_HIZ_CTRL8, 0xFF}, + {MAX98388_R2058_PCM_RX_SRC1, 0x00}, + {MAX98388_R2059_PCM_RX_SRC2, 0x01}, + {MAX98388_R205C_PCM_TX_DRIVE_STRENGTH, 0x00}, + {MAX98388_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98388_R205E_PCM_RX_EN, 0x00}, + {MAX98388_R205F_PCM_TX_EN, 0x00}, + {MAX98388_R2090_SPK_CH_VOL_CTRL, 0x00}, + {MAX98388_R2091_SPK_CH_CFG, 0x02}, + {MAX98388_R2092_SPK_AMP_OUT_CFG, 0x03}, + {MAX98388_R2093_SPK_AMP_SSM_CFG, 0x01}, + {MAX98388_R2094_SPK_AMP_ER_CTRL, 0x00}, + {MAX98388_R209E_SPK_CH_PINK_NOISE_EN, 0x00}, + {MAX98388_R209F_SPK_CH_AMP_EN, 0x00}, + {MAX98388_R20A0_IV_DATA_DSP_CTRL, 0x10}, + {MAX98388_R20A7_IV_DATA_EN, 0x00}, + {MAX98388_R20E0_BP_ALC_THRESH, 0x04}, + {MAX98388_R20E1_BP_ALC_RATES, 0x20}, + {MAX98388_R20E2_BP_ALC_ATTEN, 0x06}, + {MAX98388_R20E3_BP_ALC_REL, 0x02}, + {MAX98388_R20E4_BP_ALC_MUTE, 0x33}, + {MAX98388_R20EE_BP_INF_HOLD_REL, 0x00}, + {MAX98388_R20EF_BP_ALC_EN, 0x00}, + {MAX98388_R210E_AUTO_RESTART, 0x00}, + {MAX98388_R210F_GLOBAL_EN, 0x00}, + {MAX98388_R22FF_REV_ID, 0x00}, +}; + +static int max98388_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + max98388->tdm_mode = false; + break; + default: + return 0; + } + return 0; +} + +static const char * const max98388_monomix_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98388_R2058_PCM_RX_SRC1, + MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT, + 3, max98388_monomix_switch_text); + +static const struct snd_kcontrol_new max98388_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98388_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98388_R205F_PCM_TX_EN, 0, 1, 0); + +static const struct snd_soc_dapm_widget max98388_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98388_R205E_PCM_RX_EN, 0, 0, max98388_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98388_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 1, 0), + SND_SOC_DAPM_ADC("ADC Voltage", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 0, 0), + SND_SOC_DAPM_ADC("ADC Current", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 1, 0), + SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98388_vi_control), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), +}; + +static DECLARE_TLV_DB_SCALE(max98388_digital_tlv, -6350, 50, 1); +static DECLARE_TLV_DB_SCALE(max98388_amp_gain_tlv, -300, 300, 0); + +static const char * const max98388_alc_max_atten_text[] = { + "0dBFS", "-1dBFS", "-2dBFS", "-3dBFS", "-4dBFS", "-5dBFS", + "-6dBFS", "-7dBFS", "-8dBFS", "-9dBFS", "-10dBFS", "-11dBFS", + "-12dBFS", "-13dBFS", "-14dBFS", "-15dBFS" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_max_atten_enum, + MAX98388_R20E2_BP_ALC_ATTEN, + MAX98388_ALC_MAX_ATTEN_SHIFT, + max98388_alc_max_atten_text); + +static const char * const max98388_thermal_warn_text[] = { + "95C", "105C", "115C", "125C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_warning_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_WARN_THRESH_SHIFT, + max98388_thermal_warn_text); + +static const char * const max98388_thermal_shutdown_text[] = { + "135C", "145C", "155C", "165C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_shutdown_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_SHDN_THRESH_SHIFT, + max98388_thermal_shutdown_text); + +static const char * const max98388_alc_thresh_single_text[] = { + "3.625V", "3.550V", "3.475V", "3.400V", "3.325V", "3.250V", + "3.175V", "3.100V", "3.025V", "2.950V", "2.875V", "2.800V", + "2.725V", "2.650V", "2.575V", "2.500V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_thresh_single_enum, + MAX98388_R20E0_BP_ALC_THRESH, + MAX98388_ALC_THRESH_SHIFT, + max98388_alc_thresh_single_text); + +static const char * const max98388_alc_attack_rate_text[] = { + "0", "10us", "20us", "40us", "80us", "160us", + "320us", "640us", "1.28ms", "2.56ms", "5.12ms", "10.24ms", + "20.48ms", "40.96ms", "81.92ms", "163.84ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_attack_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_ATTACK_RATE_SHIFT, + max98388_alc_attack_rate_text); + +static const char * const max98388_alc_release_rate_text[] = { + "20us", "40us", "80us", "160us", "320us", "640us", + "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms", + "81.92ms", "163.84ms", "327.68ms", "655.36ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_release_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_RELEASE_RATE_SHIFT, + max98388_alc_release_rate_text); + +static const char * const max98388_alc_debounce_text[] = { + "0.01ms", "0.1ms", "1ms", "10ms", "100ms", "250ms", "500ms", "hold" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_debouce_enum, + MAX98388_R20E3_BP_ALC_REL, + MAX98388_ALC_DEBOUNCE_TIME_SHIFT, + max98388_alc_debounce_text); + +static const char * const max98388_alc_mute_delay_text[] = { + "0.01ms", "0.05ms", "0.1ms", "0.5ms", "1ms", "5ms", "25ms", "250ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_mute_delay_enum, + MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_DELAY_SHIFT, + max98388_alc_mute_delay_text); + +static const char * const max98388_spkmon_duration_text[] = { + "10ms", "25ms", "50ms", "75ms", "100ms", "200ms", "300ms", "400ms", + "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms", "1100ms", "1200ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_duration_enum, + MAX98388_R2033_SPK_MON_DURATION, + MAX98388_SPKMON_DURATION_SHIFT, + max98388_spkmon_duration_text); + +static const char * const max98388_spkmon_thresh_text[] = { + "0.03V", "0.06V", "0.09V", "0.12V", "0.15V", "0.18V", "0.20V", "0.23V", + "0.26V", "0.29V", "0.32V", "0.35V", "0.38V", "0.41V", "0.44V", "0.47V", + "0.50V", "0.53V", "0.56V", "0.58V", "0.61V", "0.64V", "0.67V", "0.70V", + "0.73V", "0.76V", "0.79V", "0.82V", "0.85V", "0.88V", "0.91V", "0.94V", + "0.96V", "0.99V", "1.02V", "1.05V", "1.08V", "1.11V", "1.14V", "1.17V", + "1.20V", "1.23V", "1.26V", "1.29V", "1.32V", "1.35V", "1.37V", "1.40V", + "1.43V", "1.46V", "1.49V", "1.52V", "1.55V", "1.58V", "1.61V", "1.64V", + "1.67V", "1.70V", "1.73V", "1.75V", "1.78V", "1.81V", "1.84V", "1.87V", + "1.90V", "1.93V", "1.96V", "1.99V", "2.02V", "2.05V", "2.08V", "2.11V", + "2.13V", "2.16V", "2.19V", "2.22V", "2.25V", "2.28V", "2.31V", "2.34V", + "2.37V", "2.40V", "2.43V", "2.46V", "2.49V", "2.51V", "2.54V", "2.57V", + "2.60V", "2.63V", "2.66V", "2.69V", "2.72V", "2.75V", "2.78V", "2.81V", + "2.84V", "2.87V", "2.89V", "2.92V", "2.95V", "2.98V", "3.01V", "3.04V", + "3.07V", "3.10V", "3.13V", "3.16V", "3.19V", "3.22V", "3.25V", "3.27V", + "3.30V", "3.33V", "3.36V", "3.39V", "3.42V", "3.45V", "3.48V", "3.51V", + "3.54V", "3.57V", "3.60V", "3.63V", "3.66V", "3.68V", "3.71V", "3.74V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_thresh_enum, + MAX98388_R2031_SPK_MON_THRESH, + MAX98388_SPKMON_THRESH_SHIFT, + max98388_spkmon_thresh_text); + +static const char * const max98388_spkmon_load_text[] = { + "2.00ohm", "2.25ohm", "2.50ohm", "2.75ohm", "3.00ohm", "3.25ohm", + "3.50ohm", "3.75ohm", "4.00ohm", "4.25ohm", "4.50ohm", "4.75ohm", + "5.00ohm", "5.25ohm", "5.50ohm", "5.75ohm", "6.00ohm", "6.25ohm", + "6.50ohm", "6.75ohm", "7.00ohm", "7.25ohm", "7.50ohm", "7.75ohm", + "8.00ohm", "8.25ohm", "8.50ohm", "8.75ohm", "9.00ohm", "9.25ohm", + "9.50ohm", "9.75ohm", "10.00ohm", "10.25ohm", "10.50ohm", "10.75ohm", + "11.00ohm", "11.25ohm", "11.50ohm", "11.75ohm", "12.00ohm", "12.25ohm", + "12.50ohm", "12.75ohm", "13.00ohm", "13.25ohm", "13.50ohm", "13.75ohm", + "14.00ohm", "14.25ohm", "14.50ohm", "14.75ohm", "15.00ohm", "15.25ohm", + "15.50ohm", "15.75ohm", "16.00ohm", "16.25ohm", "16.50ohm", "16.75ohm", + "17.00ohm", "17.25ohm", "17.50ohm", "17.75ohm", "18.00ohm", "18.25ohm", + "18.50ohm", "18.75ohm", "19.00ohm", "19.25ohm", "19.50ohm", "19.75ohm", + "20.00ohm", "20.25ohm", "20.50ohm", "20.75ohm", "21.00ohm", "21.25ohm", + "21.50ohm", "21.75ohm", "22.00ohm", "22.25ohm", "22.50ohm", "22.75ohm", + "23.00ohm", "23.25ohm", "23.50ohm", "23.75ohm", "24.00ohm", "24.25ohm", + "24.50ohm", "24.75ohm", "25.00ohm", "25.25ohm", "25.50ohm", "25.75ohm", + "26.00ohm", "26.25ohm", "26.50ohm", "26.75ohm", "27.00ohm", "27.25ohm", + "27.50ohm", "27.75ohm", "28.00ohm", "28.25ohm", "28.50ohm", "28.75ohm", + "29.00ohm", "29.25ohm", "29.50ohm", "29.75ohm", "30.00ohm", "30.25ohm", + "30.50ohm", "30.75ohm", "31.00ohm", "31.25ohm", "31.50ohm", "31.75ohm", + "32.00ohm", "32.25ohm", "32.50ohm", "32.75ohm", "33.00ohm", "33.25ohm", + "33.50ohm", "33.75ohm" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_load_enum, + MAX98388_R2032_SPK_MON_LD_SEL, + MAX98388_SPKMON_LOAD_SHIFT, + max98388_spkmon_load_text); + +static const char * const max98388_edge_rate_text[] = { + "Normal", "Reduced", "Maximum", "Increased", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_falling_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_FALL_SHIFT, + max98388_edge_rate_text); + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_rising_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_RISE_SHIFT, + max98388_edge_rate_text); + +static const char * const max98388_ssm_mod_text[] = { + "1.5%", "3.0%", "4.5%", "6.0%", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_ssm_mod_enum, + MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_MOD_SHIFT, + max98388_ssm_mod_text); + +static const struct snd_kcontrol_new max98388_snd_controls[] = { + SOC_SINGLE("Ramp Up Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPDN_SHIFT, 1, 0), + /* Two Cell Mode Enable */ + SOC_SINGLE("OP Mode Switch", MAX98388_R2092_SPK_AMP_OUT_CFG, + MAX98388_SPK_AMP_OUT_MODE_SHIFT, 1, 0), + /* Speaker Amplifier Overcurrent Automatic Restart Enable */ + SOC_SINGLE("OVC Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_OVC_AUTORESTART_SHIFT, 1, 0), + /* Thermal Shutdown Automatic Restart Enable */ + SOC_SINGLE("THERM Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_THERM_AUTORESTART_SHIFT, 1, 0), + /* PVDD UVLO Auto Restart */ + SOC_SINGLE("UVLO Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_PVDD_UVLO_AUTORESTART_SHIFT, 1, 0), + /* Clock Monitor Automatic Restart Enable */ + SOC_SINGLE("CMON Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_CMON_AUTORESTART_SHIFT, 1, 0), + SOC_SINGLE("CLK Monitor Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_CLOCK_MON_SHIFT, 1, 0), + /* Pinknoise Generator Enable */ + SOC_SINGLE("Pinknoise Gen Switch", MAX98388_R209E_SPK_CH_PINK_NOISE_EN, + MAX98388_PINK_NOISE_GEN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("VI Dither Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_DITH_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Voltage DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Current DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT, 1, 0), + /* Digital Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98388_R2090_SPK_CH_VOL_CTRL, + 0, 0x7F, 1, max98388_digital_tlv), + /* Speaker Volume */ + SOC_SINGLE_TLV("Speaker Volume", MAX98388_R2092_SPK_AMP_OUT_CFG, + 0, 5, 0, max98388_amp_gain_tlv), + SOC_ENUM("Thermal Warn Thresh", max98388_thermal_warning_thresh_enum), + SOC_ENUM("Thermal SHDN Thresh", max98388_thermal_shutdown_thresh_enum), + /* Brownout Protection Automatic Level Control */ + SOC_SINGLE("ALC Switch", MAX98388_R20EF_BP_ALC_EN, 0, 1, 0), + SOC_ENUM("ALC Thresh", max98388_alc_thresh_single_enum), + SOC_ENUM("ALC Attack Rate", max98388_alc_attack_rate_enum), + SOC_ENUM("ALC Release Rate", max98388_alc_release_rate_enum), + SOC_ENUM("ALC Max Atten", max98388_alc_max_atten_enum), + SOC_ENUM("ALC Debounce Time", max98388_alc_debouce_enum), + SOC_SINGLE("ALC Unmute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_EN_SHIFT, 1, 0), + SOC_ENUM("ALC Mute Delay", max98388_alc_mute_delay_enum), + /* Speaker Monitor */ + SOC_SINGLE("SPKMON Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_SPK_MON_SHIFT, 1, 0), + SOC_ENUM("SPKMON Thresh", max98388_spkmon_thresh_enum), + SOC_ENUM("SPKMON Load", max98388_spkmon_load_enum), + SOC_ENUM("SPKMON Duration", max98388_spkmon_duration_enum), + /* General Parameters */ + SOC_ENUM("Fall Slew Rate", max98388_edge_rate_falling_enum), + SOC_ENUM("Rise Slew Rate", max98388_edge_rate_rising_enum), + SOC_SINGLE("AMP SSM Switch", MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_EN_SHIFT, 1, 0), + SOC_ENUM("AMP SSM Mod", max98388_ssm_mod_enum), +}; + +static const struct snd_soc_dapm_route max98388_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "ADC Voltage", NULL, "VMON"}, + { "ADC Current", NULL, "IMON"}, + { "VI Sense", "Switch", "ADC Voltage"}, + { "VI Sense", "Switch", "ADC Current"}, + { "Voltage Sense", NULL, "VI Sense"}, + { "Current Sense", NULL, "VI Sense"}, +}; + +static void max98388_reset(struct max98388_priv *max98388, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_update_bits(max98388->regmap, + MAX98388_R2000_SW_RESET, + MAX98388_SOFT_RESET, + MAX98388_SOFT_RESET); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(10000, 11000); + /* Software Reset Verification */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + +static int max98388_probe(struct snd_soc_component *component) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + /* Software Reset */ + max98388_reset(max98388, component->dev); + + /* General channel source configuration */ + regmap_write(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + 0x10); + + /* Enable DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R2091_SPK_CH_CFG, + 0x1); + /* Enable IMON VMON DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R20A0_IV_DATA_DSP_CTRL, + 0x3); + /* TX slot configuration */ + regmap_write(max98388->regmap, + MAX98388_R2044_PCM_TX_CTRL1, + max98388->v_slot); + + regmap_write(max98388->regmap, + MAX98388_R2045_PCM_TX_CTRL2, + max98388->i_slot); + /* Enable Auto-restart behavior by default */ + regmap_write(max98388->regmap, + MAX98388_R210E_AUTO_RESTART, 0xF); + /* Set interleave mode */ + if (max98388->interleave_mode) + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK); + + /* Speaker Amplifier Channel Enable */ + regmap_update_bits(max98388->regmap, + MAX98388_R209F_SPK_CH_AMP_EN, + MAX98388_SPK_EN_MASK, 1); + + return 0; +} + +static int max98388_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int format = 0; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98388_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98388_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98388_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98388_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98388_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98388_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98388_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98388->ch_size; + int value; + + if (!max98388->tdm_mode) { + /* BCLK configuration */ + value = max98388_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + value); + } + return 0; +} + +static int max98388_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + int ret, reg; + int status = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98388->ch_size = snd_pcm_format_width(params_format(params)); + + ret = regmap_read(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, ®); + if (ret < 0) + goto err; + + /* GLOBAL_EN OFF prior to the channel size re-configure */ + if (chan_sz != (reg & MAX98388_PCM_MODE_CFG_CHANSZ_MASK)) { + ret = regmap_read(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, &status); + if (ret < 0) + goto err; + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + } + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + } + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98388_PCM_SR_8000; + break; + case 11025: + sampling_rate = MAX98388_PCM_SR_11025; + break; + case 12000: + sampling_rate = MAX98388_PCM_SR_12000; + break; + case 16000: + sampling_rate = MAX98388_PCM_SR_16000; + break; + case 22050: + sampling_rate = MAX98388_PCM_SR_22050; + break; + case 24000: + sampling_rate = MAX98388_PCM_SR_24000; + break; + case 32000: + sampling_rate = MAX98388_PCM_SR_32000; + break; + case 44100: + sampling_rate = MAX98388_PCM_SR_44100; + break; + case 48000: + sampling_rate = MAX98388_PCM_SR_48000; + break; + case 88200: + sampling_rate = MAX98388_PCM_SR_88200; + break; + case 96000: + sampling_rate = MAX98388_PCM_SR_96000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_MASK, + sampling_rate); + + /* set sampling rate of IV */ + if (max98388->interleave_mode && + sampling_rate > MAX98388_PCM_SR_16000) + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + (sampling_rate - 3) << MAX98388_PCM_SR_IV_SHIFT); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + sampling_rate << MAX98388_PCM_SR_IV_SHIFT); + + ret = max98388_set_clock(component, params); + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + } + + return ret; + +err: + return -EINVAL; +} + +#define MAX_NUM_SLOTS 16 +#define MAX_NUM_CH 2 + +static int max98388_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + int bsel = 0; + unsigned int chan_sz = 0; + unsigned int mask; + int cnt, slot_found; + int addr, bits; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98388->tdm_mode = false; + else + max98388->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98388_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + slot_found = 0; + mask = rx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + if (slot_found == 0) + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH0_SHIFT, + cnt); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH1_SHIFT, + cnt); + slot_found++; + if (slot_found >= MAX_NUM_CH) + break; + } + } + + /* speaker feedback slot configuration */ + slot_found = 0; + mask = tx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + addr = MAX98388_R2044_PCM_TX_CTRL1 + (cnt / 8); + bits = cnt % 8; + regmap_update_bits(max98388->regmap, addr, bits, bits); + if (slot_found >= MAX_NUM_CH) + break; + } + } + + return 0; +} + +#define MAX98388_RATES SNDRV_PCM_RATE_8000_96000 + +#define MAX98388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98388_dai_ops = { + .set_fmt = max98388_dai_set_fmt, + .hw_params = max98388_dai_hw_params, + .set_tdm_slot = max98388_dai_tdm_slot, +}; + +static bool max98388_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2002_INT_RAW2: + case MAX98388_R2004_INT_STATE1... MAX98388_R2005_INT_STATE2: + case MAX98388_R2020_THERM_WARN_THRESH: + case MAX98388_R2031_SPK_MON_THRESH + ... MAX98388_R2033_SPK_MON_DURATION: + case MAX98388_R2037_ERR_MON_CTRL: + case MAX98388_R2040_PCM_MODE_CFG + ... MAX98388_R2042_PCM_SR_SETUP: + case MAX98388_R2044_PCM_TX_CTRL1 + ... MAX98388_R2045_PCM_TX_CTRL2: + case MAX98388_R2050_PCM_TX_HIZ_CTRL1 + ... MAX98388_R2059_PCM_RX_SRC2: + case MAX98388_R205C_PCM_TX_DRIVE_STRENGTH + ... MAX98388_R205F_PCM_TX_EN: + case MAX98388_R2090_SPK_CH_VOL_CTRL + ... MAX98388_R2094_SPK_AMP_ER_CTRL: + case MAX98388_R209E_SPK_CH_PINK_NOISE_EN + ... MAX98388_R209F_SPK_CH_AMP_EN: + case MAX98388_R20A0_IV_DATA_DSP_CTRL: + case MAX98388_R20A7_IV_DATA_EN: + case MAX98388_R20E0_BP_ALC_THRESH ... MAX98388_R20E4_BP_ALC_MUTE: + case MAX98388_R20EE_BP_INF_HOLD_REL ... MAX98388_R20EF_BP_ALC_EN: + case MAX98388_R210E_AUTO_RESTART: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98388_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2005_INT_STATE2: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +} + +static struct snd_soc_dai_driver max98388_dai[] = { + { + .name = "max98388-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .ops = &max98388_dai_ops, + } +}; + +static int max98388_suspend(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, true); + regcache_mark_dirty(max98388->regmap); + + return 0; +} + +static int max98388_resume(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, false); + max98388_reset(max98388, dev); + regcache_sync(max98388->regmap); + + return 0; +} + +static const struct dev_pm_ops max98388_pm = { + SYSTEM_SLEEP_PM_OPS(max98388_suspend, max98388_resume) +}; + +static const struct regmap_config max98388_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98388_R22FF_REV_ID, + .reg_defaults = max98388_reg, + .num_reg_defaults = ARRAY_SIZE(max98388_reg), + .readable_reg = max98388_readable_register, + .volatile_reg = max98388_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98388 = { + .probe = max98388_probe, + .controls = max98388_snd_controls, + .num_controls = ARRAY_SIZE(max98388_snd_controls), + .dapm_widgets = max98388_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98388_dapm_widgets), + .dapm_routes = max98388_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98388_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static void max98388_read_deveice_property(struct device *dev, + struct max98388_priv *max98388) +{ + int value; + + if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value)) + max98388->v_slot = value & 0xF; + else + max98388->v_slot = 0; + + if (!device_property_read_u32(dev, "adi,imon-slot-no", &value)) + max98388->i_slot = value & 0xF; + else + max98388->i_slot = 1; + + if (device_property_read_bool(dev, "adi,interleave-mode")) + max98388->interleave_mode = true; + else + max98388->interleave_mode = false; +} + +static int max98388_i2c_probe(struct i2c_client *i2c) +{ + int ret = 0; + int reg = 0; + + struct max98388_priv *max98388 = NULL; + + max98388 = devm_kzalloc(&i2c->dev, sizeof(*max98388), GFP_KERNEL); + if (!max98388) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98388); + + /* regmap initialization */ + max98388->regmap = devm_regmap_init_i2c(i2c, &max98388_regmap); + if (IS_ERR(max98388->regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->regmap), + "Failed to allocate register map.\n"); + + /* voltage/current slot & gpio configuration */ + max98388_read_deveice_property(&i2c->dev, max98388); + + /* Device Reset */ + max98388->reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(max98388->reset_gpio)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->reset_gpio), + "Unable to request GPIO\n"); + + if (max98388->reset_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(max98388->reset_gpio, 0); + /* Wait for the hw reset done */ + usleep_range(5000, 6000); + } + + /* Read Revision ID */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (ret < 0) + return dev_err_probe(&i2c->dev, ret, + "Failed to read the revision ID\n"); + + dev_info(&i2c->dev, "MAX98388 revisionID: 0x%02X\n", reg); + + /* codec registration */ + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98388, + max98388_dai, + ARRAY_SIZE(max98388_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98388_i2c_id[] = { + { "max98388", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98388_i2c_id); + +static const struct of_device_id max98388_of_match[] = { + { .compatible = "adi,max98388", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98388_of_match); + +static const struct acpi_device_id max98388_acpi_match[] = { + { "ADS8388", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98388_acpi_match); + +static struct i2c_driver max98388_i2c_driver = { + .driver = { + .name = "max98388", + .of_match_table = max98388_of_match, + .acpi_match_table = max98388_acpi_match, + .pm = pm_sleep_ptr(&max98388_pm), + }, + .probe = max98388_i2c_probe, + .id_table = max98388_i2c_id, +}; + +module_i2c_driver(max98388_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98388 driver"); +MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98388.h b/sound/soc/codecs/max98388.h new file mode 100644 index 000000000000..77833d181913 --- /dev/null +++ b/sound/soc/codecs/max98388.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * max98388.h -- MAX98388 ALSA SoC audio driver header + * + * Copyright(c) 2022, Analog Devices Inc. + */ + +#ifndef _MAX98388_H +#define _MAX98388_H + +/* Device Status Registers */ +#define MAX98388_R2000_SW_RESET 0x2000 +#define MAX98388_R2001_INT_RAW1 0x2001 +#define MAX98388_R2002_INT_RAW2 0x2002 +#define MAX98388_R2004_INT_STATE1 0x2004 +#define MAX98388_R2005_INT_STATE2 0x2005 +/* Thermal Protection Registers */ +#define MAX98388_R2020_THERM_WARN_THRESH 0x2020 +/* Error Monitor */ +#define MAX98388_R2031_SPK_MON_THRESH 0x2031 +#define MAX98388_R2032_SPK_MON_LD_SEL 0x2032 +#define MAX98388_R2033_SPK_MON_DURATION 0x2033 +#define MAX98388_R2037_ERR_MON_CTRL 0x2037 +/* PCM Registers */ +#define MAX98388_R2040_PCM_MODE_CFG 0x2040 +#define MAX98388_R2041_PCM_CLK_SETUP 0x2041 +#define MAX98388_R2042_PCM_SR_SETUP 0x2042 +#define MAX98388_R2044_PCM_TX_CTRL1 0x2044 +#define MAX98388_R2045_PCM_TX_CTRL2 0x2045 +#define MAX98388_R2050_PCM_TX_HIZ_CTRL1 0x2050 +#define MAX98388_R2051_PCM_TX_HIZ_CTRL2 0x2051 +#define MAX98388_R2052_PCM_TX_HIZ_CTRL3 0x2052 +#define MAX98388_R2053_PCM_TX_HIZ_CTRL4 0x2053 +#define MAX98388_R2054_PCM_TX_HIZ_CTRL5 0x2054 +#define MAX98388_R2055_PCM_TX_HIZ_CTRL6 0x2055 +#define MAX98388_R2056_PCM_TX_HIZ_CTRL7 0x2056 +#define MAX98388_R2057_PCM_TX_HIZ_CTRL8 0x2057 +#define MAX98388_R2058_PCM_RX_SRC1 0x2058 +#define MAX98388_R2059_PCM_RX_SRC2 0x2059 +#define MAX98388_R205C_PCM_TX_DRIVE_STRENGTH 0x205C +#define MAX98388_R205D_PCM_TX_SRC_EN 0x205D +#define MAX98388_R205E_PCM_RX_EN 0x205E +#define MAX98388_R205F_PCM_TX_EN 0x205F +/* Speaker Channel Control */ +#define MAX98388_R2090_SPK_CH_VOL_CTRL 0x2090 +#define MAX98388_R2091_SPK_CH_CFG 0x2091 +#define MAX98388_R2092_SPK_AMP_OUT_CFG 0x2092 +#define MAX98388_R2093_SPK_AMP_SSM_CFG 0x2093 +#define MAX98388_R2094_SPK_AMP_ER_CTRL 0x2094 +#define MAX98388_R209E_SPK_CH_PINK_NOISE_EN 0x209E +#define MAX98388_R209F_SPK_CH_AMP_EN 0x209F +#define MAX98388_R20A0_IV_DATA_DSP_CTRL 0x20A0 +#define MAX98388_R20A7_IV_DATA_EN 0x20A7 +#define MAX98388_R20E0_BP_ALC_THRESH 0x20E0 +#define MAX98388_R20E1_BP_ALC_RATES 0x20E1 +#define MAX98388_R20E2_BP_ALC_ATTEN 0x20E2 +#define MAX98388_R20E3_BP_ALC_REL 0x20E3 +#define MAX98388_R20E4_BP_ALC_MUTE 0x20E4 +#define MAX98388_R20EE_BP_INF_HOLD_REL 0x20EE +#define MAX98388_R20EF_BP_ALC_EN 0x20EF +#define MAX98388_R210E_AUTO_RESTART 0x210E +#define MAX98388_R210F_GLOBAL_EN 0x210F +#define MAX98388_R22FF_REV_ID 0x22FF + +/* MAX98388_R2000_SW_RESET */ +#define MAX98388_SOFT_RESET (0x1 << 0) + +/* MAX98388_R2020_THERM_WARN_THRESH */ +#define MAX98388_THERM_SHDN_THRESH_SHIFT (0) +#define MAX98388_THERM_WARN_THRESH_SHIFT (2) + +/* MAX98388_R2022_PCM_TX_SRC_1 */ +#define MAX98388_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98388_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98388_R2024_PCM_DATA_FMT_CFG */ +#define MAX98388_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98388_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98388_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98388_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98388_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98388_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98388_R2031_SPK_MON_THRESH */ +#define MAX98388_SPKMON_THRESH_SHIFT (0) + +/* MAX98388_R2032_SPK_MON_LD_SEL */ +#define MAX98388_SPKMON_LOAD_SHIFT (0) + +/* MAX98388_R2033_SPK_MON_DURATION */ +#define MAX98388_SPKMON_DURATION_SHIFT (0) + +/* MAX98388_R2037_ERR_MON_CTRL */ +#define MAX98388_CLOCK_MON_SHIFT (0) +#define MAX98388_SPK_MON_SHIFT (1) + +/* MAX98388_R203E_AMP_PATH_GAIN */ +#define MAX98388_SPK_DIGI_GAIN_MASK (0xF << 4) +#define MAX98388_SPK_DIGI_GAIN_SHIFT (4) +#define MAX98388_FS_GAIN_MAX_MASK (0xF << 0) +#define MAX98388_FS_GAIN_MAX_SHIFT (0) + +/* MAX98388_R2041_PCM_CLK_SETUP */ +#define MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4) +#define MAX98388_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98388_R2042_PCM_SR_SETUP */ +#define MAX98388_PCM_SR_MASK (0xF << 0) +#define MAX98388_PCM_SR_IV_MASK (0xF << 4) +#define MAX98388_PCM_SR_IV_SHIFT (4) +#define MAX98388_PCM_SR_8000 (0x0 << 0) +#define MAX98388_PCM_SR_11025 (0x1 << 0) +#define MAX98388_PCM_SR_12000 (0x2 << 0) +#define MAX98388_PCM_SR_16000 (0x3 << 0) +#define MAX98388_PCM_SR_22050 (0x4 << 0) +#define MAX98388_PCM_SR_24000 (0x5 << 0) +#define MAX98388_PCM_SR_32000 (0x6 << 0) +#define MAX98388_PCM_SR_44100 (0x7 << 0) +#define MAX98388_PCM_SR_48000 (0x8 << 0) +#define MAX98388_PCM_SR_88200 (0x9 << 0) +#define MAX98388_PCM_SR_96000 (0xA << 0) + +/* MAX98388_R2043_AMP_EN */ +#define MAX98388_SPK_EN_MASK (0x1 << 0) +#define MAX98388_SPKFB_EN_MASK (0x1 << 1) +#define MAX98388_SPKFB_EN_SHIFT (1) + +/* MAX98388_R2052_MEAS_ADC_PVDD_FLT_CFG */ +#define MAX98388_FLT_EN_SHIFT (4) + +/* MAX98388_R2058_PCM_RX_SRC1 */ +#define MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT (0) + +/* MAX98388_R2059_PCM_RX_SRC2 */ +#define MAX98388_RX_SRC_CH0_SHIFT (0) +#define MAX98388_RX_SRC_CH1_SHIFT (4) + +/* MAX98388_R2091_SPK_CH_CFG */ +#define MAX98388_SPK_CFG_DCBLK_SHIFT (0) +#define MAX98388_SPK_CFG_DITH_EN_SHIFT (1) +#define MAX98388_SPK_CFG_INV_SHIFT (2) +#define MAX98388_SPK_CFG_VOL_RMPUP_SHIFT (3) +#define MAX98388_SPK_CFG_VOL_RMPDN_SHIFT (4) + +/* MAX98388_R2092_SPK_AMP_OUT_CFG */ +#define MAX98388_SPK_AMP_OUT_GAIN_SHIFT (0) +#define MAX98388_SPK_AMP_OUT_MODE_SHIFT (3) + +/* MAX98388_R2093_SPK_AMP_SSM_CFG */ +#define MAX98388_SPK_AMP_SSM_EN_SHIFT (0) +#define MAX98388_SPK_AMP_SSM_MOD_SHIFT (1) + +/* MAX98388_R2094_SPK_AMP_ER_CTRL */ +#define MAX98388_EDGE_RATE_RISE_SHIFT (0) +#define MAX98388_EDGE_RATE_FALL_SHIFT (2) + +/* MAX98388_R209E_SPK_CH_PINK_NOISE_EN */ +#define MAX98388_PINK_NOISE_GEN_SHIFT (0) + +/* MAX98388_R20A0_IV_DATA_DSP_CTRL */ +#define MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT (0) +#define MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT (1) +#define MAX98388_AMP_DSP_CTRL_VOL_INV_SHIFT (2) +#define MAX98388_AMP_DSP_CTRL_CUR_INV_SHIFT (3) +#define MAX98388_AMP_DSP_CTRL_DITH_SHIFT (4) + +/* MAX98388_R20B2_BDE_L4_CFG_2 */ +#define MAX98388_LVL4_HOLD_EN_SHIFT (6) +#define MAX98388_LVL4_MUTE_EN_SHIFT (7) + +/* MAX98388_R20B5_BDE_EN */ +#define MAX98388_BDE_EN_SHIFT (0) + +/* MAX98388_R20D1_DHT_CFG */ +#define MAX98388_DHT_ROT_PNT_SHIFT (0) +#define MAX98388_DHT_SPK_GAIN_MIN_SHIFT (4) + +/* MAX98388_R20D2_DHT_ATTACK_CFG */ +#define MAX98388_DHT_ATTACK_RATE_SHIFT (0) +#define MAX98388_DHT_ATTACK_STEP_SHIFT (3) + +/* MAX98388_R20D3_DHT_RELEASE_CFG */ +#define MAX98388_DHT_RELEASE_RATE_SHIFT (0) +#define MAX98388_DHT_RELEASE_STEP_SHIFT (3) + +/* MAX98388_R20D4_DHT_EN */ +#define MAX98388_DHT_EN_SHIFT (0) + +/* MAX98388_R20E0_BP_ALC_THRESH */ +#define MAX98388_ALC_THRESH_SHIFT (0) + +/* MAX98388_R20E1_BP_ALC_RATES */ +#define MAX98388_ALC_RELEASE_RATE_SHIFT (0) +#define MAX98388_ALC_ATTACK_RATE_SHIFT (4) + +/* MAX98388_R20E2_BP_ALC_ATTEN */ +#define MAX98388_ALC_MAX_ATTEN_SHIFT (0) + +/* MAX98388_R20E3_BP_ALC_REL */ +#define MAX98388_ALC_DEBOUNCE_TIME_SHIFT (0) + +/* MAX98388_R20E4_BP_ALC_MUTE */ +#define MAX98388_ALC_MUTE_EN_SHIFT (0) +#define MAX98388_ALC_MUTE_DELAY_SHIFT (1) +#define MAX98388_ALC_MUTE_RAMP_EN_SHIFT (4) +#define MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT (5) + +/* MAX98388_R210E_AUTO_RESTART */ +#define MAX98388_PVDD_UVLO_AUTORESTART_SHIFT (0) +#define MAX98388_THERM_AUTORESTART_SHIFT (1) +#define MAX98388_OVC_AUTORESTART_SHIFT (2) +#define MAX98388_CMON_AUTORESTART_SHIFT (3) + +/* MAX98388_R210F_GLOBAL_EN */ +#define MAX98388_GLOBAL_EN_MASK (0x1 << 0) + +struct max98388_priv { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spkfb_slot; + bool interleave_mode; + unsigned int ch_size; + bool tdm_mode; +}; + +#endif diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 7a5260ff8d6b..5b8e78e51630 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1133,7 +1133,7 @@ static struct i2c_driver max98390_i2c_driver = { .acpi_match_table = ACPI_PTR(max98390_acpi_match), .pm = &max98390_pm, }, - .probe_new = max98390_i2c_probe, + .probe = max98390_i2c_probe, .id_table = max98390_i2c_id, }; diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index db3de41f10e0..3a1d8c211f3c 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1907,7 +1907,7 @@ static struct i2c_driver max98396_i2c_driver = { .acpi_match_table = ACPI_PTR(max98396_acpi_match), .pm = &max98396_pm, }, - .probe_new = max98396_i2c_probe, + .probe = max98396_i2c_probe, .id_table = max98396_i2c_id, }; diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index a6733396b0ca..8b012a85360a 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -329,7 +329,7 @@ static struct i2c_driver max9850_i2c_driver = { .driver = { .name = "max9850", }, - .probe_new = max9850_i2c_probe, + .probe = max9850_i2c_probe, .id_table = max9850_i2c_id, }; diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index 0daa2a3dd621..93412b966b33 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -371,7 +371,7 @@ static struct i2c_driver max98504_i2c_driver = { .name = "max98504", .of_match_table = of_match_ptr(max98504_of_match), }, - .probe_new = max98504_i2c_probe, + .probe = max98504_i2c_probe, .id_table = max98504_i2c_id, }; module_i2c_driver(max98504_i2c_driver); diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index 5edd6f90f8a7..8637fff307ad 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -756,7 +756,7 @@ static struct i2c_driver max98520_i2c_driver = { .of_match_table = of_match_ptr(max98520_of_match), .pm = &max98520_pm, }, - .probe_new = max98520_i2c_probe, + .probe = max98520_i2c_probe, .id_table = max98520_i2c_id, }; diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 9611ab1e79e5..4015ed2c47ec 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -723,7 +723,7 @@ static const struct of_device_id max9860_of_match[] = { MODULE_DEVICE_TABLE(of, max9860_of_match); static struct i2c_driver max9860_i2c_driver = { - .probe_new = max9860_probe, + .probe = max9860_probe, .remove = max9860_remove, .id_table = max9860_i2c_id, .driver = { diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index ae552d72beec..b616ad39858c 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -702,7 +702,7 @@ static struct i2c_driver max9867_i2c_driver = { .name = "max9867", .of_match_table = of_match_ptr(max9867_of_match), }, - .probe_new = max9867_i2c_probe, + .probe = max9867_i2c_probe, .id_table = max9867_i2c_id, }; diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 3bf86328d5cd..2ae64fcf29c7 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -160,7 +160,7 @@ static struct i2c_driver max9877_i2c_driver = { .driver = { .name = "max9877", }, - .probe_new = max9877_i2c_probe, + .probe = max9877_i2c_probe, .id_table = max9877_i2c_id, }; diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index c24d9f2c8874..a9c1d85cd0d5 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -635,7 +635,7 @@ static struct i2c_driver max98925_i2c_driver = { .name = "max98925", .of_match_table = of_match_ptr(max98925_of_match), }, - .probe_new = max98925_i2c_probe, + .probe = max98925_i2c_probe, .id_table = max98925_i2c_id, }; diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index bffd56e240e9..bdc508e23e59 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -582,7 +582,7 @@ static struct i2c_driver max98926_i2c_driver = { .name = "max98926", .of_match_table = of_match_ptr(max98926_of_match), }, - .probe_new = max98926_i2c_probe, + .probe = max98926_i2c_probe, .id_table = max98926_i2c_id, }; diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 331d3e1d735c..0aaf2e6ae78d 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -973,7 +973,7 @@ static struct i2c_driver max98927_i2c_driver = { .acpi_match_table = ACPI_PTR(max98927_acpi_match), .pm = &max98927_pm, }, - .probe_new = max98927_i2c_probe, + .probe = max98927_i2c_probe, .remove = max98927_i2c_remove, .id_table = max98927_i2c_id, }; diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 3c6ac77379cb..a45ef9d65703 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -581,7 +581,7 @@ static struct i2c_driver ml26124_i2c_driver = { .driver = { .name = "ml26124", }, - .probe_new = ml26124_i2c_probe, + .probe = ml26124_i2c_probe, .id_table = ml26124_i2c_id, }; diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index cb487e63615c..30690479ec17 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -18,6 +18,20 @@ #include "mt6359.h" +static void mt6359_set_gpio_smt(struct mt6359_priv *priv) +{ + /* set gpio SMT mode */ + regmap_update_bits(priv->regmap, MT6359_SMT_CON1, 0x3ff0, 0x3ff0); +} + +static void mt6359_set_gpio_driving(struct mt6359_priv *priv) +{ + /* 8:4mA(default), a:8mA, c:12mA, e:16mA */ + regmap_update_bits(priv->regmap, MT6359_DRV_CON2, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON3, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON4, 0x00ff, 0x88); +} + static void mt6359_set_playback_gpio(struct mt6359_priv *priv) { /* set gpio mosi mode, clk / data mosi */ @@ -360,8 +374,34 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = 0; int index = ucontrol->value.integer.value[0]; + int orig_gain[2], new_gain[2]; int ret; + switch (mc->reg) { + case MT6359_ZCD_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + case MT6359_AUDENC_ANA_CON0: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + break; + case MT6359_AUDENC_ANA_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + break; + case MT6359_AUDENC_ANA_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; + break; + default: + return -EINVAL; + } + ret = snd_soc_put_volsw(kcontrol, ucontrol); if (ret < 0) return ret; @@ -373,6 +413,8 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK; priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; break; case MT6359_ZCD_CON1: regmap_read(priv->regmap, MT6359_ZCD_CON1, ®); @@ -380,35 +422,82 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK; priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; break; case MT6359_ZCD_CON3: regmap_read(priv->regmap, MT6359_ZCD_CON3, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] = (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; break; case MT6359_AUDENC_ANA_CON0: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON0, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] = (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; break; case MT6359_AUDENC_ANA_CON1: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON1, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] = (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; break; case MT6359_AUDENC_ANA_CON2: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON2, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3] = (reg >> RG_AUDPREAMP3GAIN_SFT) & RG_AUDPREAMP3GAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; break; } + ret = 0; + if (orig_gain[0] != new_gain[0]) { + ret = 1; + } else if (snd_soc_volsw_is_stereo(mc)) { + if (orig_gain[1] != new_gain[1]) + ret = 1; + } + dev_dbg(priv->dev, "%s(), name %s, reg(0x%x) = 0x%x, set index = %x\n", __func__, kcontrol->id.name, mc->reg, reg, index); return ret; } +static int mt6359_get_playback_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + switch (mc->reg) { + case MT6359_ZCD_CON2: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + default: + return -EINVAL; + } + + return 0; +} + /* MUX */ /* LOL MUX */ @@ -1070,9 +1159,10 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", - __func__, event, dapm_kcontrol_get_value(w->kcontrols[0])); + __func__, event, mux); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1110,14 +1200,29 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, /* Enable AUD_CLK */ mt6359_set_decoder_clk(priv, true); - /* Enable Audio DAC (3rd DAC) */ - regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); - /* Enable low-noise mode of DAC */ - if (priv->dev_counter[DEVICE_HP] == 0) - regmap_write(priv->regmap, - MT6359_AUDDEC_ANA_CON9, 0x0001); - /* Switch LOL MUX to audio 3rd DAC */ - regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + /* Switch LOL MUX to audio DAC */ + if (mux == LO_MUX_L_DAC) { + if (priv->dev_counter[DEVICE_HP] > 0) { + dev_info(priv->dev, "%s(), can not enable DAC, hp count %d\n", + __func__, priv->dev_counter[DEVICE_HP]); + break; + } + /* Enable DACL and switch HP MUX to open*/ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3009); + /* Disable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200); + usleep_range(100, 120); + /* Switch LOL MUX to DACL */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0117); + } else if (mux == LO_MUX_3RD_DAC) { + /* Enable Audio DAC (3rd DAC) */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); + /* Enable low-noise mode of DAC */ + if (priv->dev_counter[DEVICE_HP] == 0) + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001); + /* Switch LOL MUX to audio 3rd DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + } break; case SND_SOC_DAPM_PRE_PMD: /* Switch LOL MUX to open */ @@ -1129,6 +1234,15 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x000f, 0x0000); + if (mux == LO_MUX_L_DAC) { + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + } + /* Disable AUD_CLK */ mt6359_set_decoder_clk(priv, false); @@ -2358,6 +2472,10 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { {"MISO2_MUX", "UL2_CH1", "UL2_SRC_MUX"}, {"MISO2_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + {"MISO0_MUX", NULL, "UL_SRC"}, + {"MISO1_MUX", NULL, "UL_SRC"}, + {"MISO2_MUX", NULL, "UL_SRC_34"}, + {"UL_SRC_MUX", "AMIC", "ADC_L"}, {"UL_SRC_MUX", "AMIC", "ADC_R"}, {"UL_SRC_MUX", "DMIC", "DMIC0_MUX"}, @@ -2497,6 +2615,7 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { /* Lineout Path */ {"LOL Mux", "Playback", "DAC_3RD"}, + {"LOL Mux", "Playback_L_DAC", "DACL"}, {"LINEOUT L", NULL, "LOL Mux"}, /* Headphone Path */ @@ -2666,6 +2785,8 @@ static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt) 0x1 << RG_AUDLOLSCDISABLE_VAUDP32_SFT); /* set gpio */ + mt6359_set_gpio_smt(priv); + mt6359_set_gpio_driving(priv); mt6359_reset_playback_gpio(priv); mt6359_reset_capture_gpio(priv); @@ -2697,22 +2818,23 @@ static void mt6359_codec_remove(struct snd_soc_component *cmpnt) cmpnt->regmap = NULL; } -static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0); static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0); static const struct snd_kcontrol_new mt6359_snd_controls[] = { /* dl pga gain */ SOC_DOUBLE_EXT_TLV("Headset Volume", - MT6359_ZCD_CON2, 0, 7, 0x1E, 0, - snd_soc_get_volsw, mt6359_put_volsw, - hp_playback_tlv), + MT6359_ZCD_CON2, 0, 7, 0x12, 0, + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), SOC_DOUBLE_EXT_TLV("Lineout Volume", MT6359_ZCD_CON1, 0, 7, 0x12, 0, - snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), SOC_SINGLE_EXT_TLV("Handset Volume", MT6359_ZCD_CON3, 0, 0x12, 0, - snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), /* ul pga gain */ SOC_SINGLE_EXT_TLV("PGA1 Volume", diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index cc2df5f7ea19..5c50c7de26cd 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -570,7 +570,7 @@ static struct i2c_driver mt6660_i2c_driver = { .of_match_table = of_match_ptr(mt6660_of_id), .pm = &mt6660_dev_pm_ops, }, - .probe_new = mt6660_i2c_probe, + .probe = mt6660_i2c_probe, .remove = mt6660_i2c_remove, .id_table = mt6660_i2c_id, }; diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index 0626d5694c22..2174a89772fc 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -890,7 +890,7 @@ static struct i2c_driver nau8540_i2c_driver = { .name = "nau8540", .of_match_table = of_match_ptr(nau8540_of_ids), }, - .probe_new = nau8540_i2c_probe, + .probe = nau8540_i2c_probe, .id_table = nau8540_i2c_ids, }; module_i2c_driver(nau8540_i2c_driver); diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index ccb512c21d74..47f000cd4d99 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -914,7 +914,7 @@ static struct i2c_driver nau8810_i2c_driver = { .name = "nau8810", .of_match_table = of_match_ptr(nau8810_of_match), }, - .probe_new = nau8810_i2c_probe, + .probe = nau8810_i2c_probe, .id_table = nau8810_i2c_id, }; diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index fee970427a24..96d75882b33a 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1859,7 +1859,7 @@ static struct i2c_driver nau8821_driver = { .of_match_table = of_match_ptr(nau8821_of_ids), .acpi_match_table = ACPI_PTR(nau8821_acpi_match), }, - .probe_new = nau8821_i2c_probe, + .probe = nau8821_i2c_probe, .id_table = nau8821_i2c_ids, }; module_i2c_driver(nau8821_driver); diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index d5006d8de639..ff3024899f45 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -1166,7 +1166,7 @@ static struct i2c_driver nau8822_i2c_driver = { .name = "nau8822", .of_match_table = of_match_ptr(nau8822_of_match), }, - .probe_new = nau8822_i2c_probe, + .probe = nau8822_i2c_probe, .id_table = nau8822_i2c_id, }; module_i2c_driver(nau8822_i2c_driver); diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 5a4db8944d06..704af1cf8cbf 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -2030,7 +2030,7 @@ static struct i2c_driver nau8824_i2c_driver = { .of_match_table = of_match_ptr(nau8824_of_ids), .acpi_match_table = ACPI_PTR(nau8824_acpi_match), }, - .probe_new = nau8824_i2c_probe, + .probe = nau8824_i2c_probe, .id_table = nau8824_i2c_ids, }; module_i2c_driver(nau8824_i2c_driver); diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index f4eb999761a4..9e0e4ddf128e 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -53,6 +53,7 @@ struct nau8825_fll { int mclk_src; int ratio; int fll_frac; + int fll_frac_num; int fll_int; int clk_ref_div; }; @@ -178,6 +179,8 @@ static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_CLASSG_CTRL, 0x0 }, { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 }, { NAU8825_REG_MISC_CTRL, 0x0 }, + { NAU8825_REG_FLL2_LOWER, 0x0 }, + { NAU8825_REG_FLL2_UPPER, 0x0 }, { NAU8825_REG_BIAS_ADJ, 0x0 }, { NAU8825_REG_TRIM_SETTINGS, 0x0 }, { NAU8825_REG_ANALOG_CONTROL_1, 0x0 }, @@ -200,6 +203,23 @@ static struct reg_default nau8825_xtalk_baktab[] = { { NAU8825_REG_DACR_CTRL, 0x02cf }, }; +/* The regmap patch for Rev C */ +static const struct reg_sequence nau8825_regmap_patch[] = { + { NAU8825_REG_FLL2, 0x0000 }, + { NAU8825_REG_FLL4, 0x8010 }, + { NAU8825_REG_FLL_VCO_RSV, 0x0bc0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0800 }, + { NAU8825_REG_DACL_CTRL, 0x00cf }, + { NAU8825_REG_DACR_CTRL, 0x02cf }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0400 }, + { NAU8825_REG_FLL2_LOWER, 0x26e9 }, + { NAU8825_REG_FLL2_UPPER, 0x0031 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0020 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0220 }, + { NAU8825_REG_MIC_BIAS, 0x0046 }, +}; + + static const unsigned short logtable[256] = { 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, @@ -608,8 +628,13 @@ static void nau8825_xtalk_prepare(struct nau8825 *nau8825) regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0); /* Power up left and right DAC */ - regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); } static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825) @@ -622,9 +647,14 @@ static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825) NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); /* Power down left and right DAC */ - regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + /* Enable the TESTDAC and disable L/R HP impedance */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP | @@ -855,7 +885,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg) case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R: case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: case NAU8825_REG_MISC_CTRL: - case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_FLL2_UPPER: case NAU8825_REG_BIAS_ADJ: case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: @@ -881,6 +911,7 @@ static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) case NAU8825_REG_IMM_MODE_CTRL: case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_FLL2_LOWER ... NAU8825_REG_FLL2_UPPER: case NAU8825_REG_BIAS_ADJ: case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: @@ -911,6 +942,32 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) } } +static int nau8825_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, + NAU8825_ACDC_VREF_MICP | NAU8825_ACDC_VREF_MICN); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, NAU8825_DISCHRG_EN); + msleep(40); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, 0); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, 0); + break; + default: + break; + } + + return 0; +} + static int nau8825_adc_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -970,10 +1027,25 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w, /* Disables the TESTDAC to let DAC signal pass through. */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, 0); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); break; case SND_SOC_DAPM_POST_PMD: regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + break; default: return -EINVAL; @@ -1127,8 +1199,8 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC"), SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), - SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, - NULL, 0), + SND_SOC_DAPM_PGA_E("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, + NULL, 0, nau8825_fepga_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, nau8825_adc_event, SND_SOC_DAPM_POST_PMU | @@ -1181,12 +1253,13 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), SND_SOC_DAPM_PGA_S("Output DACL", 7, - NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_S("Output DACR", 7, - NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8, NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0), @@ -1678,6 +1751,10 @@ static void nau8825_setup_auto_irq(struct nau8825 *nau8825) { struct regmap *regmap = nau8825->regmap; + /* Enable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE, NAU8825_HSD_AUTO_MODE); + /* Enable headset jack type detection complete interruption and * jack ejection interruption. */ @@ -1929,6 +2006,9 @@ static int nau8825_jack_insert(struct nau8825 *nau8825) regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_MCLK_SRC_MASK, 0xf); + /* Disable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, NAU8825_HSD_AUTO_MODE, 0); + /* Leaving HPOL/R grounded after jack insert by default. They will be * ungrounded as part of the widget power up sequence at the beginning * of playback to reduce pop. @@ -2173,9 +2253,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825) regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64); /* Disable DACR/L power */ - regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input * signal to avoid any glitches due to power up transients in both * the analog and digital DAC circuit. @@ -2307,9 +2388,12 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional * input based on FDCO, FREF and FLL ratio. */ - fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); - fll_param->fll_int = (fvco >> 16) & 0x3FF; - fll_param->fll_frac = fvco & 0xFFFF; + fvco = div_u64(fvco_max << fll_param->fll_frac_num, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> fll_param->fll_frac_num) & 0x3FF; + if (fll_param->fll_frac_num == 16) + fll_param->fll_frac = fvco & 0xFFFF; + else + fll_param->fll_frac = fvco & 0xFFFFFF; return 0; } @@ -2323,8 +2407,16 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK, fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT)); - /* FLL 16-bit fractional input */ - regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); + /* FLL 16/24 bit fractional input */ + if (fll_param->fll_frac_num == 16) + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, + fll_param->fll_frac); + else { + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_LOWER, + fll_param->fll_frac & 0xffff); + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_UPPER, + (fll_param->fll_frac >> 16) & 0xff); + } /* FLL 10-bit integer input */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); @@ -2366,6 +2458,11 @@ static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int struct nau8825_fll fll_param; int ret, fs; + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + fll_param.fll_frac_num = 16; + else + fll_param.fll_frac_num = 24; + fs = freq_out / 256; ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); if (ret < 0) { @@ -2897,8 +2994,19 @@ static int nau8825_i2c_probe(struct i2c_client *i2c) ret); return ret; } - if ((value & NAU8825_SOFTWARE_ID_MASK) != - NAU8825_SOFTWARE_ID_NAU8825) { + nau8825->sw_id = value & NAU8825_SOFTWARE_ID_MASK; + switch (nau8825->sw_id) { + case NAU8825_SOFTWARE_ID_NAU8825: + break; + case NAU8825_SOFTWARE_ID_NAU8825C: + ret = regmap_register_patch(nau8825->regmap, nau8825_regmap_patch, + ARRAY_SIZE(nau8825_regmap_patch)); + if (ret) { + dev_err(dev, "Failed to register Rev C patch: %d\n", ret); + return ret; + } + break; + default: dev_err(dev, "Not a NAU8825 chip\n"); return -ENODEV; } @@ -2944,7 +3052,7 @@ static struct i2c_driver nau8825_driver = { .of_match_table = of_match_ptr(nau8825_of_ids), .acpi_match_table = ACPI_PTR(nau8825_acpi_match), }, - .probe_new = nau8825_i2c_probe, + .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, .id_table = nau8825_i2c_ids, }; diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 44b62bc3880f..2abfbb5184da 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -75,6 +75,8 @@ #define NAU8825_REG_MISC_CTRL 0x55 #define NAU8825_REG_I2C_DEVICE_ID 0x58 #define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 +#define NAU8825_REG_FLL2_LOWER 0x5a +#define NAU8825_REG_FLL2_UPPER 0x5b #define NAU8825_REG_BIAS_ADJ 0x66 #define NAU8825_REG_TRIM_SETTINGS 0x68 #define NAU8825_REG_ANALOG_CONTROL_1 0x69 @@ -386,6 +388,7 @@ #define NAU8825_GPIO2JD1 (1 << 7) #define NAU8825_SOFTWARE_ID_MASK 0x3 #define NAU8825_SOFTWARE_ID_NAU8825 0x0 +#define NAU8825_SOFTWARE_ID_NAU8825C 0x1 /* BIAS_ADJ (0x66) */ #define NAU8825_BIAS_HPR_IMP (1 << 15) @@ -442,10 +445,17 @@ /* BOOST (0x76) */ #define NAU8825_PRECHARGE_DIS (1 << 13) #define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_DISCHRG_EN (1 << 11) #define NAU8825_HP_BOOST_DIS (1 << 9) #define NAU8825_HP_BOOST_G_DIS (1 << 8) #define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) +/* FEPGA (0x77) */ +#define NAU8825_ACDC_CTRL_SFT 14 +#define NAU8825_ACDC_CTRL_MASK (0x3 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICP (0x1 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICN (0x2 << NAU8825_ACDC_CTRL_SFT) + /* POWER_UP_CONTROL (0x7f) */ #define NAU8825_POWERUP_INTEGR_R (1 << 5) #define NAU8825_POWERUP_INTEGR_L (1 << 4) @@ -490,6 +500,7 @@ struct nau8825 { struct clk *mclk; struct work_struct xtalk_work; struct semaphore xtalk_sem; + int sw_id; int irq; int mclk_freq; /* 0 - mclk is disabled */ int button_pressed; diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 3591f6f53901..735e1942b530 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -327,7 +327,7 @@ static struct i2c_driver pcm1681_i2c_driver = { .of_match_table = of_match_ptr(pcm1681_dt_ids), }, .id_table = pcm1681_i2c_id, - .probe_new = pcm1681_i2c_probe, + .probe = pcm1681_i2c_probe, }; module_i2c_driver(pcm1681_i2c_driver); diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c index fafe0dcbe4ea..f2d0b4d21e41 100644 --- a/sound/soc/codecs/pcm1789-i2c.c +++ b/sound/soc/codecs/pcm1789-i2c.c @@ -52,7 +52,7 @@ static struct i2c_driver pcm1789_i2c_driver = { .of_match_table = of_match_ptr(pcm1789_of_match), }, .id_table = pcm1789_i2c_ids, - .probe_new = pcm1789_i2c_probe, + .probe = pcm1789_i2c_probe, .remove = pcm1789_i2c_remove, }; diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c index e20fe3a85af8..10579681f44b 100644 --- a/sound/soc/codecs/pcm179x-i2c.c +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver pcm179x_i2c_driver = { .of_match_table = of_match_ptr(pcm179x_of_match), }, .id_table = pcm179x_i2c_ids, - .probe_new = pcm179x_i2c_probe, + .probe = pcm179x_i2c_probe, }; module_i2c_driver(pcm179x_i2c_driver); diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c index 932c8d41c3ea..a514ebd1b68a 100644 --- a/sound/soc/codecs/pcm186x-i2c.c +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -46,7 +46,7 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c) } static struct i2c_driver pcm186x_i2c_driver = { - .probe_new = pcm186x_i2c_probe, + .probe = pcm186x_i2c_probe, .id_table = pcm186x_i2c_id, .driver = { .name = "pcm186x", diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index dd21803ba13c..451a8fd8fac5 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -12,7 +12,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c index 2388098eeca3..5330cf46b127 100644 --- a/sound/soc/codecs/pcm3060-i2c.c +++ b/sound/soc/codecs/pcm3060-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver pcm3060_i2c_driver = { #endif /* CONFIG_OF */ }, .id_table = pcm3060_i2c_id, - .probe_new = pcm3060_i2c_probe, + .probe = pcm3060_i2c_probe, }; module_i2c_driver(pcm3060_i2c_driver); diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c index a0eec82e9872..7052cc0c97d1 100644 --- a/sound/soc/codecs/pcm3168a-i2c.c +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -44,7 +44,7 @@ static const struct of_device_id pcm3168a_of_match[] = { MODULE_DEVICE_TABLE(of, pcm3168a_of_match); static struct i2c_driver pcm3168a_i2c_driver = { - .probe_new = pcm3168a_i2c_probe, + .probe = pcm3168a_i2c_probe, .remove = pcm3168a_i2c_remove, .id_table = pcm3168a_i2c_id, .driver = { diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 9dfbbe8f4a0b..5cd2b64b9337 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); #endif static struct i2c_driver pcm512x_i2c_driver = { - .probe_new = pcm512x_i2c_probe, + .probe = pcm512x_i2c_probe, .remove = pcm512x_i2c_remove, .id_table = pcm512x_i2c_id, .driver = { diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index 1d523bfd9d84..9697aefc6e03 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <sound/dmaengine_pcm.h> diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index c1568216126e..42bac8150f62 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -2184,7 +2184,7 @@ static const struct regmap_config rt1011_regmap = { .max_register = RT1011_MAX_REG + 1, .volatile_reg = rt1011_volatile_register, .readable_reg = rt1011_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1011_reg, .num_reg_defaults = ARRAY_SIZE(rt1011_reg), .use_single_read = true, @@ -2483,7 +2483,7 @@ static struct i2c_driver rt1011_i2c_driver = { .of_match_table = of_match_ptr(rt1011_of_match), .acpi_match_table = ACPI_PTR(rt1011_acpi_match) }, - .probe_new = rt1011_i2c_probe, + .probe = rt1011_i2c_probe, .shutdown = rt1011_i2c_shutdown, .id_table = rt1011_i2c_id, }; diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 57d0f1c69e46..38d9f69b08d6 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -1170,7 +1170,7 @@ static struct i2c_driver rt1015_i2c_driver = { .of_match_table = of_match_ptr(rt1015_of_match), .acpi_match_table = ACPI_PTR(rt1015_acpi_match), }, - .probe_new = rt1015_i2c_probe, + .probe = rt1015_i2c_probe, .shutdown = rt1015_i2c_shutdown, .id_table = rt1015_i2c_id, }; diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index 37eeec650f03..b1e69fa290b2 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -683,7 +683,7 @@ static struct i2c_driver rt1016_i2c_driver = { .of_match_table = of_match_ptr(rt1016_of_match), .acpi_match_table = ACPI_PTR(rt1016_acpi_match), }, - .probe_new = rt1016_i2c_probe, + .probe = rt1016_i2c_probe, .shutdown = rt1016_i2c_shutdown, .id_table = rt1016_i2c_id, }; diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index dff2596c81eb..fd55049920c1 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -535,7 +535,7 @@ static const struct regmap_config rt1019_regmap = { .max_register = RT1019_BEEP_2, .volatile_reg = rt1019_volatile_register, .readable_reg = rt1019_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1019_reg, .num_reg_defaults = ARRAY_SIZE(rt1019_reg), }; @@ -600,7 +600,7 @@ static struct i2c_driver rt1019_i2c_driver = { .of_match_table = of_match_ptr(rt1019_of_match), .acpi_match_table = ACPI_PTR(rt1019_acpi_match), }, - .probe_new = rt1019_i2c_probe, + .probe = rt1019_i2c_probe, .id_table = rt1019_i2c_id, }; module_i2c_driver(rt1019_i2c_driver); diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index 5b39a440b6dc..59895131e6e0 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -955,7 +955,7 @@ static const struct regmap_config rt1305_regmap = { RT1305_PR_SPACING), .volatile_reg = rt1305_volatile_register, .readable_reg = rt1305_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1305_reg, .num_reg_defaults = ARRAY_SIZE(rt1305_reg), .ranges = rt1305_ranges, @@ -1170,7 +1170,7 @@ static struct i2c_driver rt1305_i2c_driver = { .acpi_match_table = ACPI_PTR(rt1305_acpi_match) #endif }, - .probe_new = rt1305_i2c_probe, + .probe = rt1305_i2c_probe, .shutdown = rt1305_i2c_shutdown, .id_table = rt1305_i2c_id, }; diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 1797af824f60..f43520ca3187 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -68,7 +68,7 @@ static const struct regmap_config rt1308_sdw_regmap = { .max_register = 0xcfff, .reg_defaults = rt1308_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -304,9 +304,6 @@ static int rt1308_update_status(struct sdw_slave *slave, { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1308->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1308->hw_init = false; @@ -314,7 +311,7 @@ static int rt1308_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED) + if (rt1308->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h index 04ff18fa18e2..f816c73e247e 100644 --- a/sound/soc/codecs/rt1308-sdw.h +++ b/sound/soc/codecs/rt1308-sdw.h @@ -159,7 +159,6 @@ struct rt1308_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index d2a8e9fe3e23..9d07756cda40 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -773,7 +773,7 @@ static const struct regmap_config rt1308_regmap = { .max_register = RT1308_MAX_REG, .volatile_reg = rt1308_volatile_register, .readable_reg = rt1308_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1308_reg, .num_reg_defaults = ARRAY_SIZE(rt1308_reg), .use_single_read = true, @@ -862,7 +862,7 @@ static struct i2c_driver rt1308_i2c_driver = { .of_match_table = of_match_ptr(rt1308_of_match), .acpi_match_table = ACPI_PTR(rt1308_acpi_match), }, - .probe_new = rt1308_i2c_probe, + .probe = rt1308_i2c_probe, .shutdown = rt1308_i2c_shutdown, .id_table = rt1308_i2c_id, }; diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 2ee5e763e345..721821d9e9af 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -188,7 +188,7 @@ static const struct regmap_config rt1316_sdw_regmap = { .max_register = 0x4108ffff, .reg_defaults = rt1316_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -323,9 +323,6 @@ static int rt1316_update_status(struct sdw_slave *slave, { struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1316->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1316->hw_init = false; @@ -333,7 +330,7 @@ static int rt1316_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1316->hw_init || rt1316->status != SDW_SLAVE_ATTACHED) + if (rt1316->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h index e37121655bc1..dc1bfe40edd3 100644 --- a/sound/soc/codecs/rt1316-sdw.h +++ b/sound/soc/codecs/rt1316-sdw.h @@ -42,7 +42,6 @@ struct rt1316_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 795accedc22c..16d750102c8c 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -337,7 +337,7 @@ static const struct regmap_config rt1318_sdw_regmap = { .max_register = 0x41081488, .reg_defaults = rt1318_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1318_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -456,9 +456,6 @@ static int rt1318_update_status(struct sdw_slave *slave, { struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1318->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1318->hw_init = false; @@ -466,7 +463,7 @@ static int rt1318_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1318->hw_init || rt1318->status != SDW_SLAVE_ATTACHED) + if (rt1318->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h index 85918c184f16..86e83d63a017 100644 --- a/sound/soc/codecs/rt1318-sdw.h +++ b/sound/soc/codecs/rt1318-sdw.h @@ -88,7 +88,6 @@ struct rt1318_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 4667bf7561b1..9a33e3776b55 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1221,7 +1221,7 @@ static struct i2c_driver rt274_i2c_driver = { .of_match_table = of_match_ptr(rt274_of_match), #endif }, - .probe_new = rt274_i2c_probe, + .probe = rt274_i2c_probe, .remove = rt274_i2c_remove, .id_table = rt274_i2c_id, }; diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index ceb56647e369..981155b046fd 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1263,7 +1263,7 @@ static struct i2c_driver rt286_i2c_driver = { .name = "rt286", .acpi_match_table = ACPI_PTR(rt286_acpi_match), }, - .probe_new = rt286_i2c_probe, + .probe = rt286_i2c_probe, .remove = rt286_i2c_remove, .id_table = rt286_i2c_id, }; diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index cea26f3a02b6..8fbd25ad9b47 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1311,7 +1311,7 @@ static struct i2c_driver rt298_i2c_driver = { .name = "rt298", .acpi_match_table = ACPI_PTR(rt298_acpi_match), }, - .probe_new = rt298_i2c_probe, + .probe = rt298_i2c_probe, .remove = rt298_i2c_remove, .id_table = rt298_i2c_id, }; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index b9bcf04d4dc9..b3aac2373357 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1195,7 +1195,7 @@ static const struct regmap_config rt5514_regmap = { .reg_read = rt5514_i2c_read, .reg_write = rt5514_i2c_write, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5514_reg, .num_reg_defaults = ARRAY_SIZE(rt5514_reg), .use_single_read = true, @@ -1328,7 +1328,7 @@ static struct i2c_driver rt5514_i2c_driver = { .of_match_table = of_match_ptr(rt5514_of_match), .pm = &rt5514_i2_pm_ops, }, - .probe_new = rt5514_i2c_probe, + .probe = rt5514_i2c_probe, .id_table = rt5514_i2c_id, }; module_i2c_driver(rt5514_i2c_driver); diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 948abde10463..c13108b51eaf 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1315,7 +1315,7 @@ static const struct regmap_config rt5616_regmap = { RT5616_PR_SPACING), .volatile_reg = rt5616_volatile_register, .readable_reg = rt5616_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5616_reg, .num_reg_defaults = ARRAY_SIZE(rt5616_reg), .ranges = rt5616_ranges, @@ -1404,7 +1404,7 @@ static struct i2c_driver rt5616_i2c_driver = { .name = "rt5616", .of_match_table = of_match_ptr(rt5616_of_match), }, - .probe_new = rt5616_i2c_probe, + .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 55c232413e2b..a64e66c2d3c4 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1693,7 +1693,7 @@ static const struct regmap_config rt5631_regmap_config = { .max_register = RT5631_VENDOR_ID2, .reg_defaults = rt5631_reg, .num_reg_defaults = ARRAY_SIZE(rt5631_reg), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1728,7 +1728,7 @@ static struct i2c_driver rt5631_i2c_driver = { .name = "rt5631", .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), }, - .probe_new = rt5631_i2c_probe, + .probe = rt5631_i2c_probe, .remove = rt5631_i2c_remove, .id_table = rt5631_i2c_id, }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 139257055507..0ed4fa261abf 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2949,7 +2949,7 @@ static const struct regmap_config rt5640_regmap = { .volatile_reg = rt5640_volatile_register, .readable_reg = rt5640_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5640_reg, .num_reg_defaults = ARRAY_SIZE(rt5640_reg), .ranges = rt5640_ranges, @@ -3079,7 +3079,7 @@ static struct i2c_driver rt5640_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5640_acpi_match), .of_match_table = of_match_ptr(rt5640_of_match), }, - .probe_new = rt5640_i2c_probe, + .probe = rt5640_i2c_probe, .id_table = rt5640_i2c_id, }; module_i2c_driver(rt5640_i2c_driver); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 7c7cbb6362ea..acc7fb1581b2 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3546,7 +3546,7 @@ static const struct regmap_config rt5645_regmap = { .volatile_reg = rt5645_volatile_register, .readable_reg = rt5645_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5645_reg, .num_reg_defaults = ARRAY_SIZE(rt5645_reg), .ranges = rt5645_ranges, @@ -3563,7 +3563,7 @@ static const struct regmap_config rt5650_regmap = { .volatile_reg = rt5645_volatile_register, .readable_reg = rt5645_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5650_reg, .num_reg_defaults = ARRAY_SIZE(rt5650_reg), .ranges = rt5645_ranges, @@ -4184,7 +4184,7 @@ static struct i2c_driver rt5645_i2c_driver = { .of_match_table = of_match_ptr(rt5645_of_match), .acpi_match_table = ACPI_PTR(rt5645_acpi_match), }, - .probe_new = rt5645_i2c_probe, + .probe = rt5645_i2c_probe, .remove = rt5645_i2c_remove, .shutdown = rt5645_i2c_shutdown, .id_table = rt5645_i2c_id, diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index df90af906563..0cee4fd1c84b 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2172,7 +2172,7 @@ static const struct regmap_config rt5651_regmap = { .volatile_reg = rt5651_volatile_register, .readable_reg = rt5651_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5651_reg, .num_reg_defaults = ARRAY_SIZE(rt5651_reg), .ranges = rt5651_ranges, @@ -2279,7 +2279,7 @@ static struct i2c_driver rt5651_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5651_acpi_match), .of_match_table = of_match_ptr(rt5651_of_match), }, - .probe_new = rt5651_i2c_probe, + .probe = rt5651_i2c_probe, .id_table = rt5651_i2c_id, }; module_i2c_driver(rt5651_i2c_driver); diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 5e21e3c37ab5..df6f0d769bbd 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4141,13 +4141,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c) regmap_write(rt5659->regmap, RT5659_RESET, 0); /* Check if MCLK provided */ - rt5659->mclk = devm_clk_get(&i2c->dev, "mclk"); - if (IS_ERR(rt5659->mclk)) { - if (PTR_ERR(rt5659->mclk) != -ENOENT) - return PTR_ERR(rt5659->mclk); - /* Otherwise mark the mclk pointer to NULL */ - rt5659->mclk = NULL; - } + rt5659->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5659->mclk)) + return PTR_ERR(rt5659->mclk); rt5659_calibrate(rt5659); @@ -4340,7 +4336,7 @@ static struct i2c_driver rt5659_i2c_driver = { .of_match_table = of_match_ptr(rt5659_of_match), .acpi_match_table = ACPI_PTR(rt5659_acpi_match), }, - .probe_new = rt5659_i2c_probe, + .probe = rt5659_i2c_probe, .shutdown = rt5659_i2c_shutdown, .id_table = rt5659_i2c_id, }; diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 341baa29fdb1..eade087b2d8b 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1221,7 +1221,7 @@ static const struct regmap_config rt5660_regmap = { .volatile_reg = rt5660_volatile_register, .readable_reg = rt5660_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5660_reg, .num_reg_defaults = ARRAY_SIZE(rt5660_reg), .ranges = rt5660_ranges, @@ -1341,7 +1341,7 @@ static struct i2c_driver rt5660_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5660_acpi_match), .of_match_table = of_match_ptr(rt5660_of_match), }, - .probe_new = rt5660_i2c_probe, + .probe = rt5660_i2c_probe, .id_table = rt5660_i2c_id, }; module_i2c_driver(rt5660_i2c_driver); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index f73751dbde30..77246f84de29 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3268,7 +3268,7 @@ static const struct regmap_config rt5663_v2_regmap = { .max_register = 0x07fa, .volatile_reg = rt5663_v2_volatile_register, .readable_reg = rt5663_v2_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5663_v2_reg, .num_reg_defaults = ARRAY_SIZE(rt5663_v2_reg), }; @@ -3281,7 +3281,7 @@ static const struct regmap_config rt5663_regmap = { .max_register = 0x03f3, .volatile_reg = rt5663_volatile_register, .readable_reg = rt5663_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5663_reg, .num_reg_defaults = ARRAY_SIZE(rt5663_reg), }; @@ -3733,7 +3733,7 @@ static struct i2c_driver rt5663_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5663_acpi_match), .of_match_table = of_match_ptr(rt5663_of_match), }, - .probe_new = rt5663_i2c_probe, + .probe = rt5663_i2c_probe, .remove = rt5663_i2c_remove, .shutdown = rt5663_i2c_shutdown, .id_table = rt5663_i2c_id, diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 17afaef85c77..83c367af91da 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4626,7 +4626,7 @@ static const struct regmap_config rt5665_regmap = { .max_register = 0x0400, .volatile_reg = rt5665_volatile_register, .readable_reg = rt5665_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5665_reg, .num_reg_defaults = ARRAY_SIZE(rt5665_reg), .use_single_read = true, @@ -4968,7 +4968,7 @@ static struct i2c_driver rt5665_i2c_driver = { .of_match_table = of_match_ptr(rt5665_of_match), .acpi_match_table = ACPI_PTR(rt5665_acpi_match), }, - .probe_new = rt5665_i2c_probe, + .probe = rt5665_i2c_probe, .shutdown = rt5665_i2c_shutdown, .id_table = rt5665_i2c_id, }; diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index ecf3b0527dbe..f04c810fd710 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2370,7 +2370,7 @@ static const struct regmap_config rt5668_regmap = { .max_register = RT5668_I2C_MODE, .volatile_reg = rt5668_volatile_register, .readable_reg = rt5668_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5668_reg, .num_reg_defaults = ARRAY_SIZE(rt5668_reg), .use_single_read = true, @@ -2618,7 +2618,7 @@ static struct i2c_driver rt5668_i2c_driver = { .of_match_table = of_match_ptr(rt5668_of_match), .acpi_match_table = ACPI_PTR(rt5668_acpi_match), }, - .probe_new = rt5668_i2c_probe, + .probe = rt5668_i2c_probe, .shutdown = rt5668_i2c_shutdown, .id_table = rt5668_i2c_id, }; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index a230f441559a..0e34293f3395 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2863,7 +2863,7 @@ static const struct regmap_config rt5670_regmap = { RT5670_PR_SPACING), .volatile_reg = rt5670_volatile_register, .readable_reg = rt5670_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5670_reg, .num_reg_defaults = ARRAY_SIZE(rt5670_reg), .ranges = rt5670_ranges, @@ -3328,7 +3328,7 @@ static struct i2c_driver rt5670_i2c_driver = { .name = "rt5670", .acpi_match_table = ACPI_PTR(rt5670_acpi_match), }, - .probe_new = rt5670_i2c_probe, + .probe = rt5670_i2c_probe, .remove = rt5670_i2c_remove, .id_table = rt5670_i2c_id, }; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 3bf019b3f700..ad14d18860fc 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -5704,7 +5704,7 @@ static struct i2c_driver rt5677_i2c_driver = { .of_match_table = rt5677_of_match, .acpi_match_table = ACPI_PTR(rt5677_acpi_match), }, - .probe_new = rt5677_i2c_probe, + .probe = rt5677_i2c_probe, .remove = rt5677_i2c_remove, }; module_i2c_driver(rt5677_i2c_driver); @@ -5712,3 +5712,5 @@ module_i2c_driver(rt5677_i2c_driver); MODULE_DESCRIPTION("ASoC RT5677 driver"); MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); MODULE_LICENSE("GPL v2"); + +MODULE_FIRMWARE("rt5677_elf_vad"); diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 5bc46b041786..fb8ffb5b2ff6 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> @@ -47,7 +46,7 @@ static const struct regmap_config rt5682_regmap = { .max_register = RT5682_I2C_MODE, .volatile_reg = rt5682_volatile_register, .readable_reg = rt5682_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682_reg, .num_reg_defaults = RT5682_REG_NUM, .use_single_read = true, @@ -334,7 +333,7 @@ static struct i2c_driver rt5682_i2c_driver = { .acpi_match_table = rt5682_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe_new = rt5682_i2c_probe, + .probe = rt5682_i2c_probe, .remove = rt5682_i2c_remove, .shutdown = rt5682_i2c_shutdown, .id_table = rt5682_i2c_id, diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 23f17f70d7e9..67404f45389f 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -79,7 +79,7 @@ static const struct regmap_config rt5682_sdw_indirect_regmap = { .max_register = RT5682_I2C_MODE, .volatile_reg = rt5682_volatile_register, .readable_reg = rt5682_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682_reg, .num_reg_defaults = RT5682_REG_NUM, .use_single_read = true, @@ -500,9 +500,6 @@ static int rt5682_update_status(struct sdw_slave *slave, { struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt5682->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt5682->hw_init = false; @@ -510,7 +507,7 @@ static int rt5682_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt5682->hw_init || rt5682->status != SDW_SLAVE_ATTACHED) + if (rt5682->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index e8efd8a84a6c..1a43d595f341 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1440,7 +1440,6 @@ struct rt5682_priv { bool disable_irq; struct mutex calibrate_mutex; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 9c34dca58f54..c77c675bd5f5 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> @@ -2848,14 +2847,9 @@ static int rt5682s_dai_probe_clks(struct snd_soc_component *component) int ret; /* Check if MCLK provided */ - rt5682s->mclk = devm_clk_get(component->dev, "mclk"); - if (IS_ERR(rt5682s->mclk)) { - if (PTR_ERR(rt5682s->mclk) != -ENOENT) { - ret = PTR_ERR(rt5682s->mclk); - return ret; - } - rt5682s->mclk = NULL; - } + rt5682s->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5682s->mclk)) + return PTR_ERR(rt5682s->mclk); /* Register CCF DAI clock control */ ret = rt5682s_register_dai_clks(component); @@ -3046,7 +3040,7 @@ static const struct regmap_config rt5682s_regmap = { .max_register = RT5682S_MAX_REG, .volatile_reg = rt5682s_volatile_register, .readable_reg = rt5682s_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682s_reg, .num_reg_defaults = ARRAY_SIZE(rt5682s_reg), .use_single_read = true, @@ -3316,7 +3310,7 @@ static struct i2c_driver rt5682s_i2c_driver = { .acpi_match_table = rt5682s_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe_new = rt5682s_i2c_probe, + .probe = rt5682s_i2c_probe, .remove = rt5682s_i2c_remove, .shutdown = rt5682s_i2c_shutdown, .id_table = rt5682s_i2c_id, diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 96fc5f36d0d0..8b28e47775cc 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -292,7 +292,7 @@ static const struct regmap_config rt700_regmap = { .max_register = 0x755800, .reg_defaults = rt700_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt700_sdw_read, @@ -315,9 +315,6 @@ static int rt700_update_status(struct sdw_slave *slave, { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt700->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt700->hw_init = false; @@ -325,7 +322,7 @@ static int rt700_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt700->hw_init || rt700->status != SDW_SLAVE_ATTACHED) + if (rt700->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h index 93c44005d38c..491774d207de 100644 --- a/sound/soc/codecs/rt700.h +++ b/sound/soc/codecs/rt700.h @@ -15,7 +15,6 @@ struct rt700_priv { struct regmap *regmap; struct regmap *sdw_regmap; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 51f3335343e0..119e1f9605d7 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -119,7 +119,7 @@ static const struct regmap_config rt711_sdca_regmap = { .max_register = 0x44ffffff, .reg_defaults = rt711_sdca_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -133,7 +133,7 @@ static const struct regmap_config rt711_sdca_mbq_regmap = { .max_register = 0x40800f12, .reg_defaults = rt711_sdca_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -143,9 +143,6 @@ static int rt711_sdca_update_status(struct sdw_slave *slave, { struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt711->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt711->hw_init = false; @@ -168,7 +165,7 @@ static int rt711_sdca_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 22076f268577..11d421e8ab2b 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -19,7 +19,6 @@ struct rt711_sdca_priv { struct regmap *regmap, *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 4fe68bcf2a7c..87dafcb4545d 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -296,7 +296,7 @@ static const struct regmap_config rt711_regmap = { .max_register = 0x755800, .reg_defaults = rt711_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt711_sdw_read, @@ -319,9 +319,6 @@ static int rt711_update_status(struct sdw_slave *slave, { struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt711->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt711->hw_init = false; @@ -329,7 +326,7 @@ static int rt711_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h index b31351f11df9..491e357191f9 100644 --- a/sound/soc/codecs/rt711.h +++ b/sound/soc/codecs/rt711.h @@ -15,7 +15,6 @@ struct rt711_priv { struct regmap *sdw_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 09807b6d6353..869cc7bfd178 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -110,7 +110,7 @@ static const struct regmap_config rt712_sdca_dmic_regmap = { .max_register = 0x40981300, .reg_defaults = rt712_sdca_dmic_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -124,7 +124,7 @@ static const struct regmap_config rt712_sdca_dmic_mbq_regmap = { .max_register = 0x40800f14, .reg_defaults = rt712_sdca_dmic_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -803,9 +803,6 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave, { struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt712->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt712->hw_init = false; @@ -813,7 +810,7 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED) + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h index 74c29677c251..110154e74efe 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.h +++ b/sound/soc/codecs/rt712-sdca-dmic.h @@ -16,7 +16,6 @@ struct rt712_sdca_dmic_priv { struct regmap *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 3f319459dfec..ad06267b0ea0 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -116,7 +116,7 @@ static const struct regmap_config rt712_sdca_regmap = { .max_register = 0x44ffffff, .reg_defaults = rt712_sdca_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -130,7 +130,7 @@ static const struct regmap_config rt712_sdca_mbq_regmap = { .max_register = 0x41000312, .reg_defaults = rt712_sdca_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -140,9 +140,6 @@ static int rt712_sdca_update_status(struct sdw_slave *slave, { struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt712->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt712->hw_init = false; @@ -165,7 +162,7 @@ static int rt712_sdca_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED) + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index c6a94a23f46e..ff79e03118ce 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -20,7 +20,6 @@ struct rt712_sdca_priv { struct regmap *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index 38a82e4e2f95..df10916bab46 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -97,7 +97,7 @@ static const struct regmap_config rt715_sdca_regmap = { .max_register = 0x43ffffff, .reg_defaults = rt715_reg_defaults_sdca, .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -111,7 +111,7 @@ static const struct regmap_config rt715_sdca_mbq_regmap = { .max_register = 0x43ffffff, .reg_defaults = rt715_mbq_reg_defaults_sdca, .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -121,14 +121,11 @@ static int rt715_sdca_update_status(struct sdw_slave *slave, { struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt715->status = status; - /* * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h index 7577f3151934..e5d6928ecaba 100644 --- a/sound/soc/codecs/rt715-sdca.h +++ b/sound/soc/codecs/rt715-sdca.h @@ -24,7 +24,6 @@ struct rt715_sdca_priv { int dbg_nid; int dbg_vid; int dbg_payload; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 4e61e16470ed..6db87442b783 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -354,7 +354,7 @@ static const struct regmap_config rt715_regmap = { .max_register = 0x752039, /* Maximum number of register */ .reg_defaults = rt715_reg_defaults, /* Defaults */ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt715_sdw_read, @@ -417,13 +417,11 @@ static int rt715_update_status(struct sdw_slave *slave, { struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt715->status = status; /* * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 17a8d041c1c3..12a0ae656d09 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -18,7 +18,6 @@ struct rt715_priv { int dbg_nid; int dbg_vid; int dbg_payload; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c new file mode 100644 index 000000000000..cc57e4e27805 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca-sdw.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#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> + +#include "rt722-sdca.h" +#include "rt722-sdca-sdw.h" + +static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01: + case 0x2f54: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x2000024: + case 0x2000029 ... 0x200004a: + case 0x2000051 ... 0x2000052: + case 0x200005a ... 0x200005b: + case 0x2000061 ... 0x2000069: + case 0x200006b: + case 0x2000070: + case 0x200007f: + case 0x2000082 ... 0x200008e: + case 0x2000090 ... 0x2000094: + case 0x5300000 ... 0x5300002: + case 0x5400002: + case 0x5600000 ... 0x5600007: + case 0x5700000 ... 0x5700004: + case 0x5800000 ... 0x5800004: + case 0x5b00003: + case 0x5c00011: + case 0x5d00006: + case 0x5f00000 ... 0x5f0000d: + case 0x5f00030: + case 0x6100000 ... 0x6100051: + case 0x6100055 ... 0x6100057: + case 0x6100062: + case 0x6100064 ... 0x6100065: + case 0x6100067: + case 0x6100070 ... 0x610007c: + case 0x6100080: + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_R): + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200000d: + case 0x2000019: + case 0x2000020: + case 0x2000030: + case 0x2000046: + case 0x2000067: + case 0x2000084: + case 0x2000086: + return true; + default: + return false; + } +} + +static const struct regmap_config rt722_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt722_sdca_readable_register, + .volatile_reg = rt722_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt722_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt722_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt722_sdca_mbq_readable_register, + .volatile_reg = rt722_sdca_mbq_volatile_register, + .max_register = 0x41000312, + .reg_defaults = rt722_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt722_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt722->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt722->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt722_sdca_jack_init set. + */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt722->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt722_sdca_io_init(&slave->dev, slave); +} + +static int rt722_sdca_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->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* + * port = 1 for headphone playback + * port = 2 for headset-mic capture + * port = 3 for speaker playback + * port = 6 for digital-mic capture + */ + prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */ + prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* 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; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 200; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt722_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + if (cancel_delayed_work_sync(&rt722->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt722->scp_sdca_stat2) + scp_sdca_stat2 = rt722->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt722->disable_irq_lock); + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt722->scp_sdca_stat2 |= scp_sdca_stat2; + do { + /* clear flag */ + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0); + if (ret < 0) + goto io_error; + } else if (ret & SDW_SCP_SDCA_INTMASK_SDCA_6) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_6, SDW_SCP_SDCA_INT_SDCA_6); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt722->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + if (status->sdca_cascade && !rt722->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_detect_work, msecs_to_jiffies(30)); + + mutex_unlock(&rt722->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt722->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static struct sdw_slave_ops rt722_sdca_slave_ops = { + .read_prop = rt722_sdca_read_prop, + .interrupt_callback = rt722_sdca_interrupt_callback, + .update_status = rt722_sdca_update_status, +}; + +static int rt722_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt722_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt722_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt722_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt722_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (rt722->hw_init) { + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + } + + if (rt722->first_hw_init) + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt722->calibrate_mutex); + mutex_destroy(&rt722->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt722_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x722, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt722_sdca_id); + +static int __maybe_unused rt722_sdca_dev_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + if (!rt722->hw_init) + return 0; + + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + + regcache_cache_only(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt722_sdca_dev_system_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt722_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt722_sdca->disable_irq_lock); + rt722_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt722_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt722_sdca_dev_suspend(dev); +} + +#define RT722_PROBE_TIMEOUT 5000 + +static int __maybe_unused 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; + + if (!rt722->first_hw_init) + return 0; + + if (!slave->unattach_request) + 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"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt722->regmap, false); + regcache_sync(rt722->regmap); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_sync(rt722->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt722_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt722_sdca_sdw_driver = { + .driver = { + .name = "rt722-sdca", + .owner = THIS_MODULE, + .pm = &rt722_sdca_pm, + }, + .probe = rt722_sdca_sdw_probe, + .remove = rt722_sdca_sdw_remove, + .ops = &rt722_sdca_slave_ops, + .id_table = rt722_sdca_id, +}; +module_sdw_driver(rt722_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h new file mode 100644 index 000000000000..5b43e86f75d1 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca-sdw.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_SDW_H__ +#define __RT722_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw_registers.h> + +static const struct reg_default rt722_sdca_reg_defaults[] = { + { 0x202d, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f35, 0x00 }, + { 0x2f36, 0x00 }, + { 0x2f50, 0xf0 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x07 }, + { 0x2f5a, 0x07 }, + { 0x2f5b, 0x07 }, + { 0x2f5c, 0x27 }, + { 0x2f5d, 0x07 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0), + 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, +}; + +static const struct reg_default rt722_sdca_mbq_defaults[] = { + { 0x200003c, 0xc214 }, + { 0x2000046, 0x8004 }, + { 0x6100006, 0x0005 }, + { 0x6100010, 0x2630 }, + { 0x6100011, 0x152f }, + { 0x6100013, 0x0102 }, + { 0x6100015, 0x2200 }, + { 0x6100017, 0x0102 }, + { 0x6100025, 0x2a29 }, + { 0x6100026, 0x2a00 }, + { 0x6100028, 0x2a2a }, + { 0x6100029, 0x4141 }, + { 0x6100055, 0x0000 }, + { 0x5810000, 0x702d }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_03), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R), + 0x0000 }, +}; + +#endif /* __RT722_SDW_H__ */ diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c new file mode 100644 index 000000000000..9c0d34366c9e --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.c @@ -0,0 +1,1555 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include <linux/bitops.h> +#include <sound/core.h> +#include <linux/delay.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 <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "rt722-sdca.h" + +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value) +{ + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + int ret; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt722_sdca_index_update_bits(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt722_sdca_index_read(rt722, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt722_sdca_index_write(rt722, nid, reg, tmp); +} + +static int rt722_sdca_btn_type(unsigned char *buffer) +{ + if ((*buffer & 0xf0) == 0x10 || (*buffer & 0x0f) == 0x01 || (*(buffer + 1) == 0x01) || + (*(buffer + 1) == 0x10)) + return SND_JACK_BTN_2; + else if ((*buffer & 0xf0) == 0x20 || (*buffer & 0x0f) == 0x02 || (*(buffer + 1) == 0x02) || + (*(buffer + 1) == 0x20)) + return SND_JACK_BTN_3; + else if ((*buffer & 0xf0) == 0x40 || (*buffer & 0x0f) == 0x04 || (*(buffer + 1) == 0x04) || + (*(buffer + 1) == 0x40)) + return SND_JACK_BTN_0; + else if ((*buffer & 0xf0) == 0x80 || (*buffer & 0x0f) == 0x08 || (*(buffer + 1) == 0x08) || + (*(buffer + 1) == 0x80)) + return SND_JACK_BTN_1; + + return 0; +} + +static unsigned int rt722_sdca_button_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return btn_type; +} + +static int rt722_sdca_headset_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt722->jack_type = 0; + break; + case 0x03: + rt722->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt722->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_SELECTED_MODE, 0), det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt722->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt722_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt722->hs_jack) + return; + + if (!rt722->component->card || !rt722->component->card->instantiated) + return; + + /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */ + if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6 || + rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + ret = rt722_sdca_headset_detect(rt722); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt722->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt722_sdca_button_detect(rt722); + + if (rt722->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt722->slave->dev, + "in %s, jack_type=%d\n", __func__, rt722->jack_type); + dev_dbg(&rt722->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + dev_dbg(&rt722->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt722_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + } else + rt722->jack_type = 0; + + dev_dbg(&rt722->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) +{ + mutex_lock(&rt722->calibrate_mutex); + if (rt722->hs_jack) { + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt722->slave->dev, "in %s enable\n", __func__); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_UNSOL_CTL, 0x016E); + /* set XU(et03h) & XU(et0Dh) to Not bypassed */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + /* trigger GE interrupt */ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_GE_RELATED_CTL2, 0x4000, 0x4000); + } + mutex_unlock(&rt722->calibrate_mutex); +} + +static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722->hs_jack = hs_jack; + + 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; + } + + rt722_sdca_jack_init(rt722); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + 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) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int adc_vol_flag = 0, changed = 0; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt722->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_l_val = gain_l_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset); + else + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_r_val = gain_r_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset); + else + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + } + + if (lvalue != gain_l_val || rvalue != gain_r_val) + changed = 1; + else + return 0; + + /* Lch*/ + regmap_write(rt722->mbq_regmap, mc->reg, gain_l_val); + + /* Rch */ + regmap_write(rt722->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return changed; + + return -EIO; +} + +static int rt722_sdca_set_gain_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); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + + if (mc->shift == 8) /* boost gain */ + ctl_l = read_l / tendB; + else { + if (adc_vol_flag) + ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); + else + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + } + + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = read_r / tendB; + else { /* ADC/DAC gain */ + if (adc_vol_flag) + ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); + else + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + } + } else { + ctl_r = ctl_l; + } + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt722_sdca_set_fu1e_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt722->fu1e_mixer_mute); i++) { + ch_mute = rt722->fu1e_dapm_mute || rt722->fu1e_mixer_mute[i]; + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt722_sdca_fu1e_capture_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); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt722->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt722_sdca_fu1e_capture_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 rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt722->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt722->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt722_sdca_set_fu1e_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt722_sdca_fu0f_capture_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->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt722->fu0f_mixer_r_mute; + return 0; +} + +static int rt722_sdca_fu0f_capture_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->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt722->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt722->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt722->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt722_sdca_set_fu0f_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +static int rt722_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / boost_step; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * boost_step; + else { /* ADC gain */ + gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt722->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt722->slave->dev, "%#08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +#define RT722_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt722_sdca_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT722_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt722_sdca_fu_info, \ + .get = rt722_sdca_fu1e_capture_get, \ + .put = rt722_sdca_fu1e_capture_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT722_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt722_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt722_sdca_controls[] = { + /* Headphone playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* Headset mic capture settings */ + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt722_sdca_fu0f_capture_get, rt722_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + 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_R_EXT_TLV("FU06 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* DMIC capture settings */ + RT722_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4), + RT722_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_VOLUME, CH_01), + rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, + 4, 0x3f, mic_vol_tlv), + RT722_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, + 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), +}; + +static int rt722_sdca_adc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7; + + return 0; +} + +static int rt722_sdca_adc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val2); + val2 = (0x7 << mask_sft) & val2; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc22_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", +}; + +static const char * const adc07_10_mux_text[] = { + "DMIC1", + "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt722_adc22_enum, SND_SOC_NOPM, 0, adc22_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc24_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc25_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static const struct snd_kcontrol_new rt722_sdca_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt722_adc22_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt722_adc24_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt722_adc25_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static int rt722_sdca_fu42_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct 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_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct 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); + 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); + break; + } + return 0; +} + +static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu1e_dapm_mute = false; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu1e_dapm_mute = true; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_fu36_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu0f_dapm_mute = false; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu0f_dapm_mute = true; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt722_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_INPUT("DMIC1_2"), + SND_SOC_DAPM_INPUT("DMIC3_4"), + + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde47_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde12_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu21_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu42_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu36_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu113_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc22_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc25_mux), + + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt722_sdca_audio_map[] = { + {"FU 42", NULL, "DP1RX"}, + {"FU 21", NULL, "DP3RX"}, + + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 24 Mux", "DMIC2", "DMIC3_4"}, + {"ADC 25 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 25 Mux", "DMIC2", "DMIC3_4"}, + {"FU 36", NULL, "PDE 12"}, + {"FU 36", NULL, "ADC 22 Mux"}, + {"FU 113", NULL, "PDE 11"}, + {"FU 113", NULL, "ADC 24 Mux"}, + {"FU 113", NULL, "ADC 25 Mux"}, + {"DP2TX", NULL, "FU 36"}, + {"DP6TX", NULL, "FU 113"}, + + {"HP", NULL, "PDE 47"}, + {"HP", NULL, "FU 42"}, + {"SPK", NULL, "PDE 23"}, + {"SPK", NULL, "FU 21"}, +}; + +static int rt722_sdca_parse_dt(struct rt722_sdca_priv *rt722, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt722->jd_src); + + return 0; +} + +static int rt722_sdca_probe(struct snd_soc_component *component) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722_sdca_parse_dt(rt722, &rt722->slave->dev); + rt722->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt722 = { + .probe = rt722_sdca_probe, + .controls = rt722_sdca_controls, + .num_controls = ARRAY_SIZE(rt722_sdca_controls), + .dapm_widgets = rt722_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt722_sdca_dapm_widgets), + .dapm_routes = rt722_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt722_sdca_audio_map), + .set_jack = rt722_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt722_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt722_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt722->slave) + return -EINVAL; + + /* + * RT722_AIF1 with port = 1 for headphone playback + * RT722_AIF1 with port = 2 for headset-mic capture + * RT722_AIF2 with port = 3 for speaker playback + * RT722_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT722_AIF1) + port = 1; + else if (dai->id == RT722_AIF2) + port = 3; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT722_AIF1) + port = 2; + else if (dai->id == RT722_AIF3) + port = 6; + else + return -EINVAL; + } + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt722->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT722_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT722_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT722_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT722_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + if (dai->id == RT722_AIF1) { + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + } + + if (dai->id == RT722_AIF2) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + if (dai->id == RT722_AIF3) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + return 0; +} + +static int rt722_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt722->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt722->slave, sdw_stream); + return 0; +} + +#define RT722_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT722_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt722_sdca_ops = { + .hw_params = rt722_sdca_pcm_hw_params, + .hw_free = rt722_sdca_pcm_hw_free, + .set_stream = rt722_sdca_set_sdw_stream, + .shutdown = rt722_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt722_sdca_dai[] = { + { + .name = "rt722-sdca-aif1", + .id = RT722_AIF1, + .playback = { + .stream_name = "DP1 Headphone Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .capture = { + .stream_name = "DP2 Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif2", + .id = RT722_AIF2, + .playback = { + .stream_name = "DP3 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif3", + .id = RT722_AIF3, + .capture = { + .stream_name = "DP6 DMic Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + } +}; + +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722; + + rt722 = devm_kzalloc(dev, sizeof(*rt722), GFP_KERNEL); + if (!rt722) + return -ENOMEM; + + dev_set_drvdata(dev, rt722); + rt722->slave = slave; + rt722->regmap = regmap; + rt722->mbq_regmap = mbq_regmap; + + mutex_init(&rt722->calibrate_mutex); + mutex_init(&rt722->disable_irq_lock); + + INIT_DELAYED_WORK(&rt722->jack_detect_work, rt722_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt722->jack_btn_check_work, rt722_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt722->hw_init = false; + rt722->first_hw_init = false; + rt722->fu1e_dapm_mute = true; + rt722->fu0f_dapm_mute = true; + 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; + + return devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); +} + +static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722) +{ + /* Set AD07 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29); + /* Set AD10 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC10_PDE_FLOAT_CTL, 0x2a00); + /* Set DMIC1/DMIC2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a); + /* Set DMIC2 IT entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_ENT_FLOAT_CTL, 0x2626); + /* Set AD10 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_ENT_FLOAT_CTL, 0x1e00); + /* Set DMIC2 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515); + /* Set AD10 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304); + /* Set DMIC2 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304); + /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000); + /* Enable vf707_r12_05/vf707_r13_05 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, + RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01); + /* Fine tune PDE2A latency */ + regmap_write(rt722->regmap, 0x2f5c, 0x25); +} + +static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722) +{ + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* Reset dc_cal_top */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0x702c); + /* W1C Trigger Calibration */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0xf02d); + /* Set DAC02/ClassD power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL, + 0x2323); + /* Set EAPD high */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL, + 0x0002); + /* Enable vf707_r14 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, + RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04); +} + +static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) +{ + int loop_check, chk_cnt = 100, ret; + unsigned int calib_status = 0; + + /* Read eFuse */ + rt722_sdca_index_write(rt722, RT722_VENDOR_SPK_EFUSE, RT722_DC_CALIB_CTRL, + 0x4808); + /* Button A, B, C, D bypass mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, + 0xcf00); + /* HID1 slot enable */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5, + 0x000f); + /* Report ID for HID1 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0, + 0x1100); + /* OSC/OOC for slot 2, 3 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7, + 0x0c12); + /* Set JD de-bounce clock control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1, + 0x7002); + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* FSM switch to calibration manual mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL, + 0x4100); + /* W1C Trigger DC calibration (HP) */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3, + 0x008d); + /* check HP calibration FSM status */ + for (loop_check = 0; loop_check < chk_cnt; loop_check++) { + ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI, + RT722_DAC_DC_CALI_CTL3, &calib_status); + if (ret < 0 || loop_check == chk_cnt) + dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret); + if ((calib_status & 0x0040) == 0x0) + break; + } + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, + 0x0010); + /* Set ADC09 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, + 0x2a12); + /* Set MIC2 and LINE1 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL, + 0x3429); + /* Set ET41h and LINE2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL, + 0x4112); + /* Set DAC03 and HP power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, + 0x4040); + /* Fine tune PDE40 latency */ + regmap_write(rt722->regmap, 0x2f58, 0x07); +} + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + rt722->disable_irq = false; + + if (rt722->hw_init) + return 0; + + if (rt722->first_hw_init) { + regcache_cache_only(rt722->regmap, false); + regcache_cache_bypass(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_cache_bypass(rt722->mbq_regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt722_sdca_dmic_preset(rt722); + rt722_sdca_amp_preset(rt722); + rt722_sdca_jack_preset(rt722); + + if (rt722->first_hw_init) { + regcache_cache_bypass(rt722->regmap, false); + regcache_mark_dirty(rt722->regmap); + regcache_cache_bypass(rt722->mbq_regmap, false); + regcache_mark_dirty(rt722->mbq_regmap); + } else + rt722->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt722->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h new file mode 100644 index 000000000000..44af8901352e --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_H__ +#define __RT722_H__ + +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <linux/workqueue.h> + +struct rt722_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct mutex calibrate_mutex; + struct mutex disable_irq_lock; + bool disable_irq; + /* For Headset jack & Headphone */ + unsigned int scp_sdca_stat1; + unsigned int scp_sdca_stat2; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; + int jd_src; + bool fu0f_dapm_mute; + bool fu0f_mixer_l_mute; + bool fu0f_mixer_r_mute; + /* For DMIC */ + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; +}; + +struct rt722_sdca_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; +}; + +/* NID */ +#define RT722_VENDOR_REG 0x20 +#define RT722_VENDOR_CALI 0x58 +#define RT722_VENDOR_SPK_EFUSE 0x5c +#define RT722_VENDOR_IMS_DRE 0x5b +#define RT722_VENDOR_ANALOG_CTL 0x5f +#define RT722_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT722_JD_PRODUCT_NUM 0x00 +#define RT722_ANALOG_BIAS_CTL3 0x04 +#define RT722_JD_CTRL1 0x09 +#define RT722_LDO2_3_CTL1 0x0e +#define RT722_LDO1_CTL 0x1a +#define RT722_HP_JD_CTRL 0x24 +#define RT722_CLSD_CTRL6 0x3c +#define RT722_COMBO_JACK_AUTO_CTL1 0x45 +#define RT722_COMBO_JACK_AUTO_CTL2 0x46 +#define RT722_COMBO_JACK_AUTO_CTL3 0x47 +#define RT722_DIGITAL_MISC_CTRL4 0x4a +#define RT722_FSM_CTL 0x67 +#define RT722_SDCA_INTR_REC 0x82 +#define RT722_SW_CONFIG1 0x8a +#define RT722_SW_CONFIG2 0x8b + +/* Index (NID:58h) */ +#define RT722_DAC_DC_CALI_CTL0 0x00 +#define RT722_DAC_DC_CALI_CTL1 0x01 +#define RT722_DAC_DC_CALI_CTL2 0x02 +#define RT722_DAC_DC_CALI_CTL3 0x03 + +/* Index (NID:59h) */ +#define RT722_ULTRA_SOUND_DETECTOR6 0x1e + +/* Index (NID:5bh) */ +#define RT722_IMS_DIGITAL_CTL1 0x00 +#define RT722_IMS_DIGITAL_CTL5 0x05 +#define RT722_HP_DETECT_RLDET_CTL1 0x29 +#define RT722_HP_DETECT_RLDET_CTL2 0x2a + +/* Index (NID:5fh) */ +#define RT722_MISC_POWER_CTL0 0x00 +#define RT722_MISC_POWER_CTL7 0x08 + +/* Index (NID:61h) */ +#define RT722_HDA_LEGACY_MUX_CTL0 0x00 +#define RT722_HDA_LEGACY_UNSOL_CTL 0x03 +#define RT722_HDA_LEGACY_CONFIG_CTL0 0x06 +#define RT722_HDA_LEGACY_RESET_CTL 0x08 +#define RT722_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e +#define RT722_DMIC_ENT_FLOAT_CTL 0x10 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL0 0x11 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL2 0x13 +#define RT722_ADC_ENT_FLOAT_CTL 0x15 +#define RT722_ADC_VOL_CH_FLOAT_CTL 0x17 +#define RT722_ADC_SAMPLE_RATE_FLOAT 0x18 +#define RT722_DAC03_HP_PDE_FLOAT_CTL 0x22 +#define RT722_MIC2_LINE2_PDE_FLOAT_CTL 0x23 +#define RT722_ET41_LINE2_PDE_FLOAT_CTL 0x24 +#define RT722_ADC0A_08_PDE_FLOAT_CTL 0x25 +#define RT722_ADC10_PDE_FLOAT_CTL 0x26 +#define RT722_DMIC1_2_PDE_FLOAT_CTL 0x28 +#define RT722_AMP_PDE_FLOAT_CTL 0x29 +#define RT722_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f +#define RT722_GE_RELATED_CTL1 0x45 +#define RT722_GE_RELATED_CTL2 0x46 +#define RT722_MIXER_CTL0 0x52 +#define RT722_MIXER_CTL1 0x53 +#define RT722_EAPD_CTL 0x55 +#define RT722_UMP_HID_CTL0 0x60 +#define RT722_UMP_HID_CTL1 0x61 +#define RT722_UMP_HID_CTL2 0x62 +#define RT722_UMP_HID_CTL3 0x63 +#define RT722_UMP_HID_CTL4 0x64 +#define RT722_UMP_HID_CTL5 0x65 +#define RT722_UMP_HID_CTL6 0x66 +#define RT722_UMP_HID_CTL7 0x67 +#define RT722_UMP_HID_CTL8 0x68 + +/* Parameter & Verb control 01 (0x1a)(NID:20h) */ +#define RT722_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT722_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT722_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT722_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT722_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* DAC calibration control (0x00)(NID:58h) */ +#define RT722_DC_CALIB_CTRL (0x1 << 16) +/* DAC DC offset calibration control-1 (0x01)(NID:58h) */ +#define RT722_PDM_DC_CALIB_STATUS (0x1 << 15) + +#define RT722_EAPD_HIGH 0x2 +#define RT722_EAPD_LOW 0x0 + +/* Buffer address for HID */ +#define RT722_BUF_ADDR_HID1 0x44030000 +#define RT722_BUF_ADDR_HID2 0x44030020 + +/* RT722 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 +#define FUNC_NUM_AMP 0x04 + +/* RT722 SDCA entity */ +#define RT722_SDCA_ENT_HID01 0x01 +#define RT722_SDCA_ENT_GE49 0x49 +#define RT722_SDCA_ENT_USER_FU05 0x05 +#define RT722_SDCA_ENT_USER_FU06 0x06 +#define RT722_SDCA_ENT_USER_FU0F 0x0f +#define RT722_SDCA_ENT_USER_FU10 0x19 +#define RT722_SDCA_ENT_USER_FU1E 0x1e +#define RT722_SDCA_ENT_FU15 0x15 +#define RT722_SDCA_ENT_PDE23 0x23 +#define RT722_SDCA_ENT_PDE40 0x40 +#define RT722_SDCA_ENT_PDE11 0x11 +#define RT722_SDCA_ENT_PDE12 0x12 +#define RT722_SDCA_ENT_PDE2A 0x2a +#define RT722_SDCA_ENT_CS01 0x01 +#define RT722_SDCA_ENT_CS11 0x11 +#define RT722_SDCA_ENT_CS1F 0x1f +#define RT722_SDCA_ENT_CS1C 0x1c +#define RT722_SDCA_ENT_CS31 0x31 +#define RT722_SDCA_ENT_OT23 0x42 +#define RT722_SDCA_ENT_IT26 0x26 +#define RT722_SDCA_ENT_IT09 0x09 +#define RT722_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT722_SDCA_ENT_PLATFORM_FU44 0x44 +#define RT722_SDCA_ENT_XU03 0x03 +#define RT722_SDCA_ENT_XU0D 0x0d + +/* RT722 SDCA control */ +#define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT722_SDCA_CTL_FU_MUTE 0x01 +#define RT722_SDCA_CTL_FU_VOLUME 0x02 +#define RT722_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT722_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT722_SDCA_CTL_SELECTED_MODE 0x01 +#define RT722_SDCA_CTL_DETECTED_MODE 0x02 +#define RT722_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT722_SDCA_CTL_VENDOR_DEF 0x30 +#define RT722_SDCA_CTL_FU_CH_GAIN 0x0b + +/* RT722 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 +#define CH_08 0x08 + +/* sample frequency index */ +#define RT722_SDCA_RATE_16000HZ 0x04 +#define RT722_SDCA_RATE_32000HZ 0x07 +#define RT722_SDCA_RATE_44100HZ 0x08 +#define RT722_SDCA_RATE_48000HZ 0x09 +#define RT722_SDCA_RATE_96000HZ 0x0b +#define RT722_SDCA_RATE_192000HZ 0x0d + +enum { + RT722_AIF1, /* For headset mic and headphone */ + RT722_AIF2, /* For speaker */ + RT722_AIF3, /* For dmic */ + RT722_AIFS, +}; + +enum rt722_sdca_jd_src { + RT722_JD_NULL, + RT722_JD1, +}; + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value); +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value); + +int rt722_sdca_jack_detect(struct rt722_sdca_priv *rt722, bool *hp, bool *mic); +#endif /* __RT722_H__ */ diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c index fcf4fbaed3c7..733a7d130a95 100644 --- a/sound/soc/codecs/rt9120.c +++ b/sound/soc/codecs/rt9120.c @@ -633,7 +633,7 @@ static struct i2c_driver rt9120_driver = { .of_match_table = rt9120_device_table, .pm = &rt9120_pm_ops, }, - .probe_new = rt9120_probe, + .probe = rt9120_probe, .remove = rt9120_remove, }; module_i2c_driver(rt9120_driver); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index a916f4619ea3..b22ba95bd0c0 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1826,7 +1826,7 @@ static struct i2c_driver sgtl5000_i2c_driver = { .name = "sgtl5000", .of_match_table = sgtl5000_dt_ids, }, - .probe_new = sgtl5000_i2c_probe, + .probe = sgtl5000_i2c_probe, .remove = sgtl5000_i2c_remove, .shutdown = sgtl5000_i2c_shutdown, .id_table = sgtl5000_id, diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index b6c132edf3bd..7b9abbc1bd94 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -1807,7 +1807,7 @@ static struct i2c_driver sma1303_i2c_driver = { .name = "sma1303", .of_match_table = sma1303_of_match, }, - .probe_new = sma1303_i2c_probe, + .probe = sma1303_i2c_probe, .remove = sma1303_i2c_remove, .id_table = sma1303_i2c_id, }; diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c index a40fd20df984..93af8e209b05 100644 --- a/sound/soc/codecs/src4xxx-i2c.c +++ b/sound/soc/codecs/src4xxx-i2c.c @@ -36,7 +36,7 @@ static struct i2c_driver src4xxx_i2c_driver = { .name = "src4xxx", .of_match_table = of_match_ptr(src4xxx_of_match), }, - .probe_new = src4xxx_i2c_probe, + .probe = src4xxx_i2c_probe, .id_table = src4xxx_i2c_ids, }; module_i2c_driver(src4xxx_i2c_driver); diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 22cb3b7c8283..d20d897407eb 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -803,7 +803,7 @@ static struct i2c_driver ssm2518_driver = { .name = "ssm2518", .of_match_table = of_match_ptr(ssm2518_dt_ids), }, - .probe_new = ssm2518_i2c_probe, + .probe = ssm2518_i2c_probe, .id_table = ssm2518_i2c_ids, }; module_i2c_driver(ssm2518_driver); diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index 3c85772901f5..596096466cd4 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver ssm2602_i2c_driver = { .name = "ssm2602", .of_match_table = ssm2602_of_match, }, - .probe_new = ssm2602_i2c_probe, + .probe = ssm2602_i2c_probe, .id_table = ssm2602_i2c_id, }; module_i2c_driver(ssm2602_i2c_driver); diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c new file mode 100644 index 000000000000..008cb3eb5758 --- /dev/null +++ b/sound/soc/codecs/ssm3515.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +// +// Analog Devices' SSM3515 audio amp driver +// +// Copyright (C) The Asahi Linux Contributors + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + + +#define SSM3515_PWR 0x00 +#define SSM3515_PWR_APWDN_EN BIT(7) +#define SSM3515_PWR_BSNS_PWDN BIT(6) +#define SSM3515_PWR_S_RST BIT(1) +#define SSM3515_PWR_SPWDN BIT(0) + +#define SSM3515_GEC 0x01 +#define SSM3515_GEC_EDGE BIT(4) +#define SSM3515_GEC_EDGE_SHIFT 4 +#define SSM3515_GEC_ANA_GAIN GENMASK(1, 0) + +#define SSM3515_DAC 0x02 +#define SSM3515_DAC_HV BIT(7) +#define SSM3515_DAC_MUTE BIT(6) +#define SSM3515_DAC_HPF BIT(5) +#define SSM3515_DAC_LPM BIT(4) +#define SSM3515_DAC_FS GENMASK(2, 0) + +#define SSM3515_DAC_VOL 0x03 + +#define SSM3515_SAI1 0x04 +#define SSM3515_SAI1_DAC_POL BIT(7) +#define SSM3515_SAI1_BCLK_POL BIT(6) +#define SSM3515_SAI1_TDM_BCLKS GENMASK(5, 3) +#define SSM3515_SAI1_FSYNC_MODE BIT(2) +#define SSM3515_SAI1_SDATA_FMT BIT(1) +#define SSM3515_SAI1_SAI_MODE BIT(0) + +#define SSM3515_SAI2 0x05 +#define SSM3515_SAI2_DATA_WIDTH BIT(7) +#define SSM3515_SAI2_AUTO_SLOT BIT(4) +#define SSM3515_SAI2_TDM_SLOT GENMASK(3, 0) + +#define SSM3515_VBAT_OUT 0x06 + +#define SSM3515_STATUS 0x0a +#define SSM3515_STATUS_UVLO_REG BIT(6) +#define SSM3515_STATUS_LIM_EG BIT(5) +#define SSM3515_STATUS_CLIP BIT(4) +#define SSM3515_STATUS_AMP_OC BIT(3) +#define SSM3515_STATUS_OTF BIT(2) +#define SSM3515_STATUS_OTW BIT(1) +#define SSM3515_STATUS_BAT_WARN BIT(0) + +static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM3515_STATUS: + case SSM3515_VBAT_OUT: + return true; + + default: + return false; + } +} + +static const struct reg_default ssm3515_reg_defaults[] = { + { SSM3515_PWR, 0x81 }, + { SSM3515_GEC, 0x01 }, + { SSM3515_DAC, 0x32 }, + { SSM3515_DAC_VOL, 0x40 }, + { SSM3515_SAI1, 0x11 }, + { SSM3515_SAI2, 0x00 }, +}; + +static const struct regmap_config ssm3515_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = ssm3515_volatile_reg, + .max_register = 0xb, + .reg_defaults = ssm3515_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +struct ssm3515_data { + struct device *dev; + struct regmap *regmap; +}; + +// The specced range is -71.25...24.00 dB with step size of 0.375 dB, +// and a mute item below that. This is represented by -71.62...24.00 dB +// with the mute item mapped onto the low end. +static DECLARE_TLV_DB_MINMAX_MUTE(ssm3515_dac_volume, -7162, 2400); + +static const char * const ssm3515_ana_gain_text[] = { + "8.4 V Span", "12.6 V Span", "14 V Span", "15 V Span", +}; + +static SOC_ENUM_SINGLE_DECL(ssm3515_ana_gain_enum, SSM3515_GEC, + __bf_shf(SSM3515_GEC_ANA_GAIN), + ssm3515_ana_gain_text); + +static const struct snd_kcontrol_new ssm3515_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", SSM3515_DAC_VOL, + 0, 255, 1, ssm3515_dac_volume), + SOC_SINGLE("Low EMI Mode Switch", SSM3515_GEC, + __bf_shf(SSM3515_GEC_EDGE), 1, 0), + SOC_SINGLE("Soft Volume Ramping Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HV), 1, 1), + SOC_SINGLE("HPF Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HPF), 1, 0), + SOC_SINGLE("DAC Invert Switch", SSM3515_SAI1, + __bf_shf(SSM3515_SAI1_DAC_POL), 1, 0), + SOC_ENUM("DAC Analog Gain Select", ssm3515_ana_gain_enum), +}; + +static void ssm3515_read_faults(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_read(component, SSM3515_STATUS); + if (ret <= 0) { + /* + * If the read was erroneous, ASoC core has printed a message, + * and that's all that's appropriate in handling the error here. + */ + return; + } + + dev_err(component->dev, "device reports:%s%s%s%s%s%s%s\n", + FIELD_GET(SSM3515_STATUS_UVLO_REG, ret) ? " voltage regulator fault" : "", + FIELD_GET(SSM3515_STATUS_LIM_EG, ret) ? " limiter engaged" : "", + FIELD_GET(SSM3515_STATUS_CLIP, ret) ? " clipping detected" : "", + FIELD_GET(SSM3515_STATUS_AMP_OC, ret) ? " amp over-current fault" : "", + FIELD_GET(SSM3515_STATUS_OTF, ret) ? " overtemperature fault" : "", + FIELD_GET(SSM3515_STATUS_OTW, ret) ? " overtemperature warning" : "", + FIELD_GET(SSM3515_STATUS_BAT_WARN, ret) ? " bat voltage low warning" : ""); +} + +static int ssm3515_probe(struct snd_soc_component *component) +{ + int ret; + + /* Start out muted */ + ret = snd_soc_component_update_bits(component, SSM3515_DAC, + SSM3515_DAC_MUTE, SSM3515_DAC_MUTE); + if (ret < 0) + return ret; + + /* Disable the 'master power-down' */ + ret = snd_soc_component_update_bits(component, SSM3515_PWR, + SSM3515_PWR_SPWDN, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + int ret; + + ret = snd_soc_component_update_bits(dai->component, + SSM3515_DAC, + SSM3515_DAC_MUTE, + FIELD_PREP(SSM3515_DAC_MUTE, mute)); + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_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; + int ret, rateval; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + case SNDRV_PCM_FORMAT_S24: + ret = snd_soc_component_update_bits(component, + SSM3515_SAI2, SSM3515_SAI2_DATA_WIDTH, + FIELD_PREP(SSM3515_SAI2_DATA_WIDTH, + params_width(params) == 16)); + if (ret < 0) + return ret; + break; + + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000 ... 12000: + rateval = 0; + break; + case 16000 ... 24000: + rateval = 1; + break; + case 32000 ... 48000: + rateval = 2; + break; + case 64000 ... 96000: + rateval = 3; + break; + case 128000 ... 192000: + rateval = 4; + break; + case 48001 ... 63999: /* this is ...72000 but overlaps */ + rateval = 5; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, + SSM3515_DAC, SSM3515_DAC_FS, + FIELD_PREP(SSM3515_DAC_FS, rateval)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + bool fpol_inv = false; /* non-inverted: frame starts with low-to-high FSYNC */ + int ret; + u8 sai1 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + sai1 |= SSM3515_SAI1_BCLK_POL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + fpol_inv = 1; + sai1 &= ~SSM3515_SAI1_SDATA_FMT; /* 1 bit start delay */ + break; + case SND_SOC_DAIFMT_LEFT_J: + fpol_inv = 0; + sai1 |= SSM3515_SAI1_SDATA_FMT; /* no start delay */ + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + fpol_inv ^= 1; + break; + } + + /* Set the serial input to 'TDM mode' */ + sai1 |= SSM3515_SAI1_SAI_MODE; + + if (fpol_inv) { + /* + * We configure the codec in a 'TDM mode', in which the + * FSYNC_MODE bit of SAI1 is supposed to select between + * what the datasheet calls 'Pulsed FSYNC mode' and '50% + * FSYNC mode'. + * + * Experiments suggest that this bit in fact simply selects + * the FSYNC polarity, so go with that. + */ + sai1 |= SSM3515_SAI1_FSYNC_MODE; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_BCLK_POL | SSM3515_SAI1_SDATA_FMT | + SSM3515_SAI1_SAI_MODE | SSM3515_SAI1_FSYNC_MODE, sai1); + + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + int slot, tdm_bclks_val, ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + slot = __ffs(tx_mask); + + if (tx_mask & ~BIT(slot)) + return -EINVAL; + + switch (slot_width) { + case 16: + tdm_bclks_val = 0; + break; + case 24: + tdm_bclks_val = 1; + break; + case 32: + tdm_bclks_val = 2; + break; + case 48: + tdm_bclks_val = 3; + break; + case 64: + tdm_bclks_val = 4; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_TDM_BCLKS, + FIELD_PREP(SSM3515_SAI1_TDM_BCLKS, tdm_bclks_val)); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, SSM3515_SAI2, + SSM3515_SAI2_TDM_SLOT, + FIELD_PREP(SSM3515_SAI2_TDM_SLOT, slot)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * We don't get live notification of faults, so at least at + * this time, when playback is over, check if we have tripped + * over anything and if so, log it. + */ + ssm3515_read_faults(dai->component); + return 0; +} + +static const struct snd_soc_dai_ops ssm3515_dai_ops = { + .mute_stream = ssm3515_mute, + .hw_params = ssm3515_hw_params, + .set_fmt = ssm3515_set_fmt, + .set_tdm_slot = ssm3515_set_tdm_slot, + .hw_free = ssm3515_hw_free, +}; + +static struct snd_soc_dai_driver ssm3515_dai_driver = { + .name = "SSM3515 SAI", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ssm3515_dai_ops, +}; + +static const struct snd_soc_dapm_widget ssm3515_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route ssm3515_dapm_routes[] = { + {"OUT", NULL, "DAC"}, + {"DAC", NULL, "Playback"}, +}; + +static const struct snd_soc_component_driver ssm3515_asoc_component = { + .probe = ssm3515_probe, + .controls = ssm3515_snd_controls, + .num_controls = ARRAY_SIZE(ssm3515_snd_controls), + .dapm_widgets = ssm3515_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm3515_dapm_widgets), + .dapm_routes = ssm3515_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ssm3515_dapm_routes), + .endianness = 1, +}; + +static int ssm3515_i2c_probe(struct i2c_client *client) +{ + struct ssm3515_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &client->dev; + i2c_set_clientdata(client, data); + + data->regmap = devm_regmap_init_i2c(client, &ssm3515_i2c_regmap); + if (IS_ERR(data->regmap)) + return dev_err_probe(data->dev, PTR_ERR(data->regmap), + "initializing register map\n"); + + /* Perform a reset */ + ret = regmap_update_bits(data->regmap, SSM3515_PWR, + SSM3515_PWR_S_RST, SSM3515_PWR_S_RST); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "performing software reset\n"); + regmap_reinit_cache(data->regmap, &ssm3515_i2c_regmap); + + return devm_snd_soc_register_component(data->dev, + &ssm3515_asoc_component, + &ssm3515_dai_driver, 1); +} + +static const struct of_device_id ssm3515_of_match[] = { + { .compatible = "adi,ssm3515" }, + {} +}; +MODULE_DEVICE_TABLE(of, ssm3515_of_match); + +static struct i2c_driver ssm3515_i2c_driver = { + .driver = { + .name = "ssm3515", + .of_match_table = of_match_ptr(ssm3515_of_match), + }, + .probe = ssm3515_i2c_probe, +}; +module_i2c_driver(ssm3515_i2c_driver); + +MODULE_AUTHOR("Martin PoviÅ¡er <povik+lin@cutebit.org>"); +MODULE_DESCRIPTION("ASoC SSM3515 audio amp driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 4b0265617c7b..0a6f04d8f636 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -500,7 +500,7 @@ static struct i2c_driver ssm4567_driver = { .of_match_table = of_match_ptr(ssm4567_of_match), .acpi_match_table = ACPI_PTR(ssm4567_acpi_match), }, - .probe_new = ssm4567_i2c_probe, + .probe = ssm4567_i2c_probe, .id_table = ssm4567_i2c_ids, }; module_i2c_driver(ssm4567_driver); diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 29af9595dac1..4a694d0bfd68 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -1167,7 +1167,7 @@ static struct i2c_driver sta32x_i2c_driver = { .name = "sta32x", .of_match_table = of_match_ptr(st32x_dt_ids), }, - .probe_new = sta32x_i2c_probe, + .probe = sta32x_i2c_probe, .id_table = sta32x_i2c_id, }; diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index b033a5fcd6c0..d05f3fd57661 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1249,7 +1249,7 @@ static struct i2c_driver sta350_i2c_driver = { .name = "sta350", .of_match_table = of_match_ptr(st350_dt_ids), }, - .probe_new = sta350_i2c_probe, + .probe = sta350_i2c_probe, .remove = sta350_i2c_remove, .id_table = sta350_i2c_id, }; diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 313957099145..0ac08478ddac 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -379,7 +379,7 @@ static struct i2c_driver sta529_i2c_driver = { .name = "sta529", .of_match_table = sta529_of_match, }, - .probe_new = sta529_i2c_probe, + .probe = sta529_i2c_probe, .id_table = sta529_i2c_id, }; diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 59a4ea5f6e30..8c9dc318b0e8 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -761,7 +761,7 @@ static struct i2c_driver tas2552_i2c_driver = { .of_match_table = of_match_ptr(tas2552_of_match), .pm = &tas2552_pm, }, - .probe_new = tas2552_probe, + .probe = tas2552_probe, .remove = tas2552_i2c_remove, .id_table = tas2552_id, }; diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index b486d0bd86c9..962c2cdfa017 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -8,7 +8,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/gpio/consumer.h> @@ -784,7 +783,7 @@ static struct i2c_driver tas2562_i2c_driver = { .name = "tas2562", .of_match_table = of_match_ptr(tas2562_of_match), }, - .probe_new = tas2562_probe, + .probe = tas2562_probe, .id_table = tas2562_id, }; diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 2e0ed3e68fa5..a9838e0738cc 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -756,7 +756,7 @@ static struct i2c_driver tas2764_i2c_driver = { .name = "tas2764", .of_match_table = of_match_ptr(tas2764_of_match), }, - .probe_new = tas2764_i2c_probe, + .probe = tas2764_i2c_probe, .id_table = tas2764_i2c_id, }; module_i2c_driver(tas2764_i2c_driver); diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 8557759acb1f..99bf402eb566 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -720,7 +720,7 @@ static struct i2c_driver tas2770_i2c_driver = { .name = "tas2770", .of_match_table = of_match_ptr(tas2770_of_match), }, - .probe_new = tas2770_i2c_probe, + .probe = tas2770_i2c_probe, .id_table = tas2770_i2c_id, }; module_i2c_driver(tas2770_i2c_driver); diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index 09e7ada1bca4..86bd6c18a944 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -645,7 +645,7 @@ static struct i2c_driver tas2780_i2c_driver = { .name = "tas2780", .of_match_table = of_match_ptr(tas2780_of_match), }, - .probe_new = tas2780_i2c_probe, + .probe = tas2780_i2c_probe, .id_table = tas2780_i2c_id, }; module_i2c_driver(tas2780_i2c_driver); diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c new file mode 100644 index 000000000000..a88c6c28a394 --- /dev/null +++ b/sound/soc/codecs/tas2781-comlib.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781.h> + +#define TASDEVICE_CRC8_POLYNOMIAL 0x4d + +static const struct regmap_range_cfg tasdevice_ranges[] = { + { + .range_min = 0, + .range_max = 256 * 128, + .selector_reg = TASDEVICE_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .ranges = tasdevice_ranges, + .num_ranges = ARRAY_SIZE(tasdevice_ranges), + .max_register = 256 * 128, +}; + +static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv, + unsigned short chn, int book) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + int ret = 0; + + if (chn < tas_priv->ndev) { + struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; + struct regmap *map = tas_priv->regmap; + + if (client->addr != tasdev->dev_addr) { + client->addr = tasdev->dev_addr; + if (tasdev->cur_book == book) { + ret = regmap_write(map, + TASDEVICE_PAGE_SELECT, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + } + goto out; + } + + if (tasdev->cur_book != book) { + ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + tasdev->cur_book = book; + } + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} + +int tasdevice_dev_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int *val) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_read(map, TASDEVICE_PGRG(reg), val); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_read); + +int tasdevice_dev_write(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_write(map, TASDEVICE_PGRG(reg), + value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_write); + +int tasdevice_dev_bulk_write( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg), + data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write); + +int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read); + +int tasdevice_dev_update_bits( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_update_bits(map, TASDEVICE_PGRG(reg), + mask, value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + ret = -EINVAL; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits); + +struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) +{ + struct tasdevice_priv *tas_priv; + + tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL); + if (!tas_priv) + return NULL; + tas_priv->dev = &i2c->dev; + tas_priv->client = (void *)i2c; + + return tas_priv; +} +EXPORT_SYMBOL_GPL(tasdevice_kzalloc); + +void tas2781_reset(struct tasdevice_priv *tas_dev) +{ + int ret, i; + + if (tas_dev->reset) { + gpiod_set_value_cansleep(tas_dev->reset, 0); + usleep_range(500, 1000); + gpiod_set_value_cansleep(tas_dev->reset, 1); + } else { + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_write(tas_dev, i, + TAS2781_REG_SWRESET, + TAS2781_REG_SWRESET_RESET); + if (ret < 0) + dev_err(tas_dev->dev, + "dev %d swreset fail, %d\n", + i, ret); + } + } + usleep_range(1000, 1050); +} +EXPORT_SYMBOL_GPL(tas2781_reset); + +int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + void (*cont)(const struct firmware *fw, void *context)) +{ + int ret = 0; + + /* Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ + mutex_lock(&tas_priv->codec_lock); + + scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->ndev); + crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); + tas_priv->codec = codec; + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, + cont); + if (ret) + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + return ret; +} +EXPORT_SYMBOL_GPL(tascodec_init); + +int tasdevice_init(struct tasdevice_priv *tas_priv) +{ + int ret = 0; + int i; + + tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client, + &tasdevice_regmap); + if (IS_ERR(tas_priv->regmap)) { + ret = PTR_ERR(tas_priv->regmap); + dev_err(tas_priv->dev, "Failed to allocate register map: %d\n", + ret); + goto out; + } + + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + for (i = 0; i < tas_priv->ndev; i++) { + tas_priv->tasdevice[i].cur_book = -1; + tas_priv->tasdevice[i].cur_prog = -1; + tas_priv->tasdevice[i].cur_conf = -1; + } + + dev_set_drvdata(tas_priv->dev, tas_priv); + + mutex_init(&tas_priv->codec_lock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_init); + +static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (!prog) + return; + + tas_dt = &(prog->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); +} + +static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_prog_blk_remove(&prog[i]); + kfree(prog); +} + +static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (cfg) { + tas_dt = &(cfg->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); + } +} + +static void tasdev_dsp_cfg_remove(struct tasdevice_config *config, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_cfg_blk_remove(&config[i]); + kfree(config); +} + +void tasdevice_dsp_remove(void *context) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_dev->fmw; + + if (!tas_dev->fmw) + return; + + if (tas_fmw->programs) + tasdev_dsp_prog_remove(tas_fmw->programs, + tas_fmw->nr_programs); + if (tas_fmw->configs) + tasdev_dsp_cfg_remove(tas_fmw->configs, + tas_fmw->nr_configurations); + kfree(tas_fmw); + tas_dev->fmw = NULL; +} +EXPORT_SYMBOL_GPL(tasdevice_dsp_remove); + +void tasdevice_remove(struct tasdevice_priv *tas_priv) +{ + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) + gpio_free(tas_priv->irq_info.irq_gpio); + kfree(tas_priv->acpi_subsystem_id); + mutex_destroy(&tas_priv->codec_lock); +} +EXPORT_SYMBOL_GPL(tasdevice_remove); + +static int tasdevice_clamp(int val, int max, unsigned int invert) +{ + if (val > max) + val = max; + if (invert) + val = max - val; + if (val < 0) + val = 0; + return val; +} + +int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask; + int max = mc->max; + int err_cnt = 0; + int val, i, ret; + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_update_bits(tas_priv, i, + mc->reg, mask, (unsigned int)(val << mc->shift)); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; +} +EXPORT_SYMBOL_GPL(tasdevice_amp_putvol); + +int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask = 0; + int max = mc->max; + int ret = 0; + int val; + + /* Read the primary device */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_amp_getvol); + +int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int err_cnt = 0; + int ret; + int val, i; + + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_write(tas_priv, i, mc->reg, + (unsigned int)val); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, + "set digital vol err in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_putvol); + +int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int ret, val; + + /* Read the primary device as the whole */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get digital vol error\n", + __func__); + goto out; + } + + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_getvol); + +MODULE_DESCRIPTION("TAS2781 common library"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c new file mode 100644 index 000000000000..eb55abae0d7b --- /dev/null +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -0,0 +1,2428 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tasdevice-fmw.c -- TASDEVICE firmware support +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/tas2781.h> + + +#define ERROR_PRAM_CRCCHK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define PPC_DRIVER_CRCCHK 0x00000200 + +#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c) +#define TAS2781_YRAM_BOOK1 140 +#define TAS2781_YRAM1_PAGE 42 +#define TAS2781_YRAM1_START_REG 88 + +#define TAS2781_YRAM2_START_PAGE 43 +#define TAS2781_YRAM2_END_PAGE 49 +#define TAS2781_YRAM2_START_REG 8 +#define TAS2781_YRAM2_END_REG 127 + +#define TAS2781_YRAM3_PAGE 50 +#define TAS2781_YRAM3_START_REG 8 +#define TAS2781_YRAM3_END_REG 27 + +/*should not include B0_P53_R44-R47 */ +#define TAS2781_YRAM_BOOK2 0 +#define TAS2781_YRAM4_START_PAGE 50 +#define TAS2781_YRAM4_END_PAGE 60 + +#define TAS2781_YRAM5_PAGE 61 +#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG +#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG + +#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10 +#define MAIN_ALL_DEVICES_1X 0x01 +#define MAIN_DEVICE_A_1X 0x02 +#define MAIN_DEVICE_B_1X 0x03 +#define MAIN_DEVICE_C_1X 0x04 +#define MAIN_DEVICE_D_1X 0x05 +#define COEFF_DEVICE_A_1X 0x12 +#define COEFF_DEVICE_B_1X 0x13 +#define COEFF_DEVICE_C_1X 0x14 +#define COEFF_DEVICE_D_1X 0x15 +#define PRE_DEVICE_A_1X 0x22 +#define PRE_DEVICE_B_1X 0x23 +#define PRE_DEVICE_C_1X 0x24 +#define PRE_DEVICE_D_1X 0x25 +#define PRE_SOFTWARE_RESET_DEVICE_A 0x41 +#define PRE_SOFTWARE_RESET_DEVICE_B 0x42 +#define PRE_SOFTWARE_RESET_DEVICE_C 0x43 +#define PRE_SOFTWARE_RESET_DEVICE_D 0x44 +#define POST_SOFTWARE_RESET_DEVICE_A 0x45 +#define POST_SOFTWARE_RESET_DEVICE_B 0x46 +#define POST_SOFTWARE_RESET_DEVICE_C 0x47 +#define POST_SOFTWARE_RESET_DEVICE_D 0x48 + +struct tas_crc { + unsigned char offset; + unsigned char len; +}; + +static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = { + 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4 +}; + +static struct tasdevice_config_info *tasdevice_add_config( + struct tasdevice_priv *tas_priv, unsigned char *config_data, + unsigned int config_size, int *status) +{ + struct tasdevice_config_info *cfg_info; + struct tasdev_blk_data **bk_da; + unsigned int config_offset = 0; + unsigned int i; + + /* In most projects are many audio cases, such as music, handfree, + * receiver, games, audio-to-haptics, PMIC record, bypass mode, + * portrait, landscape, etc. Even in multiple audios, one or + * two of the chips will work for the special case, such as + * ultrasonic application. In order to support these variable-numbers + * of audio cases, flexible configs have been introduced in the + * dsp firmware. + */ + cfg_info = kzalloc(sizeof(struct tasdevice_config_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + goto out; + } + + if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) { + if (config_offset + 64 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add conf: Out of boundary\n"); + goto out; + } + config_offset += 64; + } + + if (config_offset + 4 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add config: Out of boundary\n"); + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + cfg_info->nblocks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + /* Several kinds of dsp/algorithm firmwares can run on tas2781, + * the number and size of blk are not fixed and different among + * these firmwares. + */ + bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, + sizeof(struct tasdev_blk_data *), GFP_KERNEL); + if (!bk_da) { + *status = -ENOMEM; + goto out; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d nblocks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + bk_da[i] = kzalloc(sizeof(struct tasdev_blk_data), GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + break; + } + + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + if (bk_da[i]->block_type == TASDEVICE_BIN_BLK_PRE_POWER_UP) { + if (bk_da[i]->dev_idx == 0) + cfg_info->active_dev = + (1 << tas_priv->ndev) - 1; + else + cfg_info->active_dev |= 1 << + (bk_da[i]->dev_idx - 1); + + } + bk_da[i]->yram_checksum = + be16_to_cpup((__be16 *)&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + + config_offset += 4; + + if (config_offset + bk_da[i]->block_size > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d blks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + /* instead of kzalloc+memcpy */ + bk_da[i]->regdata = kmemdup(&config_data[config_offset], + bk_da[i]->block_size, GFP_KERNEL); + if (!bk_da[i]->regdata) { + *status = -ENOMEM; + goto out; + } + + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } + +out: + return cfg_info; +} + +int tasdevice_rca_parser(void *context, const struct firmware *fmw) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_config_info **cfg_info; + struct tasdevice_rca_hdr *fw_hdr; + struct tasdevice_rca *rca; + unsigned int total_config_sz = 0; + unsigned char *buf; + int offset = 0; + int ret = 0; + int i; + + rca = &(tas_priv->rcabin); + fw_hdr = &(rca->fw_hdr); + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "Failed to read %s\n", + tas_priv->rca_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + buf = (unsigned char *)fmw->data; + + fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_hdr->img_sz != fmw->size) { + dev_err(tas_priv->dev, + "File size not match, %d %u", (int)fmw->size, + fw_hdr->img_sz); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + + fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(tas_priv->dev, "File version 0x%04x is too low", + fw_hdr->binary_version_num); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset]; + offset += 1; + fw_hdr->dev_family = buf[offset]; + offset += 1; + fw_hdr->reserve = buf[offset]; + offset += 1; + fw_hdr->ndev = buf[offset]; + offset += 1; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "ndev(%u) in rcabin mismatch ndev(%u) in DTS\n", + fw_hdr->ndev, tas_priv->ndev); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(tas_priv->dev, "Bin file error!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + ret = -ENOMEM; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + rca->cfg_info = cfg_info; + rca->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + rca->ncfgs += 1; + cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset], + fw_hdr->config_size[i], &ret); + if (ret) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + offset += (int)fw_hdr->config_size[i]; + } +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_rca_parser, SND_SOC_TAS2781_FMWLIB); + +static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + + if (offset + 16 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + + block->blk_size = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (offset + block->blk_size > fmw->size) { + dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += block->blk_size; + +out: + return offset; +} + +static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = fmw->data; + struct tasdev_blk *blk; + unsigned int i; + + if (offset + 4 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + img_data->nr_blk = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data_kernel(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + break; + } + } + +out: + return offset; +} + +static int fw_parse_program_data_kernel( + struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + struct tasdevice_prog *program; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + if (offset + 72 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + /*skip 72 unused byts*/ + offset += 72; + + offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_configuration_data_kernel( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + struct tasdevice_config *config; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 80 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + /*skip extra 16 bytes*/ + offset += 80; + + offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_variable_header_kernel( + struct tasdevice_priv *tas_priv, const struct firmware *fmw, + int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_prog *program; + struct tasdevice_config *config; + const unsigned char *buf = fmw->data; + unsigned short max_confs; + unsigned int i; + + if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->device = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + goto out; + } + + tas_fmw->nr_programs = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs > + TASDEVICE_MAXPROGRAM_NUM_KERNEL) { + dev_err(tas_priv->dev, "mnPrograms is invalid\n"); + offset = -EINVAL; + goto out; + } + + tas_fmw->programs = kcalloc(tas_fmw->nr_programs, + sizeof(struct tasdevice_prog), GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + program->prog_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused prog_size */ + offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); + + tas_fmw->nr_configurations = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + /* The max number of config in firmware greater than 4 pieces of + * tas2781s is different from the one lower than 4 pieces of + * tas2781s. + */ + max_confs = (fw_hdr->ndev >= 4) ? + TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS : + TASDEVICE_MAXCONFIG_NUM_KERNEL; + if (tas_fmw->nr_configurations == 0 || + tas_fmw->nr_configurations > max_confs) { + dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__); + offset = -EINVAL; + goto out; + } + + if (offset + 4 * max_confs > fmw->size) { + dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__); + offset = -EINVAL; + goto out; + } + + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + config = &(tas_fmw->configs[i]); + config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused configs */ + offset += 4 * (max_confs - tas_fmw->nr_programs); + +out: + return offset; +} + +static int tasdevice_process_block(void *context, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + int subblk_offset, chn, chnend, rc; + unsigned char subblk_typ = data[1]; + int blktyp = dev_idx & 0xC0; + int idx = dev_idx & 0x3F; + bool is_err = false; + + if (idx) { + chn = idx - 1; + chnend = idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + + for (; chn < chnend; chn++) { + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + + is_err = false; + subblk_offset = 2; + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: { + int i; + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_priv->dev, + "process_block: Out of boundary\n"); + is_err = true; + break; + } + + for (i = 0; i < len; i++) { + rc = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "process_block: single write error\n"); + } + subblk_offset += 4; + } + } + break; + case TASDEVICE_CMD_BURST: { + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_priv->dev, + "%s: BST Out of boundary\n", + __func__); + is_err = true; + break; + } + if (len % 4) { + dev_err(tas_priv->dev, + "%s:Bst-len(%u)not div by 4\n", + __func__, len); + break; + } + + rc = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &(data[subblk_offset + 4]), len); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: bulk_write error = %d\n", + __func__, rc); + } + subblk_offset += (len + 4); + } + break; + case TASDEVICE_CMD_DELAY: { + unsigned int sleep_time = 0; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_priv->dev, + "%s: delay Out of boundary\n", + __func__); + is_err = true; + break; + } + sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + usleep_range(sleep_time, sleep_time + 50); + subblk_offset += 2; + } + break; + case TASDEVICE_CMD_FIELD_W: + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_priv->dev, + "%s: bit write Out of boundary\n", + __func__); + is_err = true; + break; + } + rc = tasdevice_dev_update_bits(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset + 2], + data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: update_bits error = %d\n", + __func__, rc); + } + subblk_offset += 6; + break; + default: + break; + } + if (is_err == true && blktyp != 0) { + if (blktyp == 0x80) { + tas_priv->tasdevice[chn].cur_prog = -1; + tas_priv->tasdevice[chn].cur_conf = -1; + } else + tas_priv->tasdevice[chn].cur_conf = -1; + } + } + + return subblk_offset; +} + +void tasdevice_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdev_blk_data **blk_data; + int j, k, chn, chnend; + + if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) { + dev_err(tas_priv->dev, "conf_no should be not more than %u\n", + rca->ncfgs); + return; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(tas_priv->dev, + "block_type should be in range from 2 to 5\n"); + break; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < (int)blk_data[j]->n_subblks; k++) { + if (blk_data[j]->dev_idx) { + chn = blk_data[j]->dev_idx - 1; + chnend = blk_data[j]->dev_idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + for (; chn < chnend; chn++) + tas_priv->tasdevice[chn].is_loading = true; + + rc = tasdevice_process_block(tas_priv, + blk_data[j]->regdata + length, + blk_data[j]->dev_idx, + blk_data[j]->block_size - length); + length += rc; + if (blk_data[j]->block_size < length) { + dev_err(tas_priv->dev, + "%s: %u %u out of boundary\n", + __func__, length, + blk_data[j]->block_size); + break; + } + } + if (length != blk_data[j]->block_size) + dev_err(tas_priv->dev, "%s: %u %u size is not same\n", + __func__, length, blk_data[j]->block_size); + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_cfg_blk, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_block_kernel( + struct tasdevice_priv *tasdevice, struct tasdev_blk *block) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned int blk_size = block->blk_size; + unsigned int i, length; + unsigned char *data = block->data; + unsigned char dev_idx = 0; + + if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + case PRE_SOFTWARE_RESET_DEVICE_A: + case POST_SOFTWARE_RESET_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + case PRE_SOFTWARE_RESET_DEVICE_B: + case POST_SOFTWARE_RESET_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + case PRE_SOFTWARE_RESET_DEVICE_C: + case POST_SOFTWARE_RESET_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + case PRE_SOFTWARE_RESET_DEVICE_D: + case POST_SOFTWARE_RESET_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else if (fw_fixed_hdr->ppcver >= + PPC3_VERSION) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else { + switch (block->type) { + case MAIN_ALL_DEVICES: + dev_idx = 0|0x80; + break; + case MAIN_DEVICE_A: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } + + for (i = 0, length = 0; i < block->nr_subblocks; i++) { + int rc = tasdevice_process_block(tasdevice, data + length, + dev_idx, blk_size - length); + if (rc < 0) { + dev_err(tasdevice->dev, + "%s: %u %u sublock write error\n", + __func__, length, blk_size); + break; + } + length += (unsigned int)rc; + if (blk_size < length) { + dev_err(tasdevice->dev, "%s: %u %u out of boundary\n", + __func__, length, blk_size); + break; + } + } + + return 0; +} + +static int fw_parse_variable_hdr(struct tasdevice_priv + *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, + const struct firmware *fmw, int offset) +{ + const unsigned char *buf = fmw->data; + int len = strlen((char *)&buf[offset]); + + len++; + + if (offset + len + 8 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + offset += len; + + fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + fw_hdr->device = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + +out: + return offset; +} + +static int fw_parse_variable_header_git(struct tasdevice_priv + *tas_priv, const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + int n; + + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Type error\n", __func__); + offset = -EINVAL; + goto out; + } + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "PChkSumPresent error\n"); + offset = -EINVAL; + goto out; + } + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + } else { + block->is_pchksum_present = 0; + block->is_ychksum_present = 0; + } + + block->nr_cmds = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + n = block->nr_cmds * 4; + if (offset + n > fmw->size) { + dev_err(tas_fmw->dev, + "%s: File Size(%lu) error offset = %d n = %d\n", + __func__, (unsigned long)fmw->size, offset, n); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], n, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += n; + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_data(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = (unsigned char *)fmw->data; + struct tasdev_blk *blk; + unsigned int i; + int n; + + if (offset + 64 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Name error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(img_data->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n++; + if (offset + n + 2 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += n; + img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + goto out; + } + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_program_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *buf = (unsigned char *)fmw->data; + struct tasdevice_prog *program; + int i; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_programs = be16_to_cpup((__be16 *)&buf[offset]); + offset += 2; + + if (tas_fmw->nr_programs == 0) { + /*Not error in calibration Data file, return directly*/ + dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n", + __func__); + goto out; + } + + tas_fmw->programs = + kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog), + GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_programs; i++) { + int n = 0; + + program = &(tas_fmw->programs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 64; + + n = strlen((char *)&buf[offset]); + /* skip '\0' and 5 unused bytes */ + n += 6; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_configuration_data( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + struct tasdevice_config *config; + unsigned int i; + int n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_configurations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_configurations == 0) { + dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__); + /*Not error for calibration Data file, return directly*/ + goto out; + } + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "File Size err\n"); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n += 15; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static bool check_inpage_yram_rg(struct tas_crc *cd, + unsigned char reg, unsigned char len) +{ + bool in = false; + + + if (reg <= TAS2781_YRAM5_END_REG && + reg >= TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_END_REG) + cd->len = TAS2781_YRAM5_END_REG - reg + 1; + else + cd->len = len; + cd->offset = reg; + in = true; + } else if (reg < TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_START_REG) { + cd->offset = TAS2781_YRAM5_START_REG; + cd->len = len - TAS2781_YRAM5_START_REG + reg; + in = true; + } + } + + return in; +} + +static bool check_inpage_yram_bk1(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (page == TAS2781_YRAM1_PAGE) { + if (reg >= TAS2781_YRAM1_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg + len > TAS2781_YRAM1_START_REG) { + cd->offset = TAS2781_YRAM1_START_REG; + cd->len = len - TAS2781_YRAM1_START_REG + reg; + in = true; + } + } else if (page == TAS2781_YRAM3_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + + return in; +} + +/* Return Code: + * true -- the registers are in the inpage yram + * false -- the registers are NOT in the inpage yram + */ +static bool check_inpage_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1) { + in = check_inpage_yram_bk1(cd, page, reg, len); + goto end; + } + if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + +end: + return in; +} + +static bool check_inblock_yram_bk(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if ((page >= TAS2781_YRAM4_START_PAGE && + page <= TAS2781_YRAM4_END_PAGE) || + (page >= TAS2781_YRAM2_START_PAGE && + page <= TAS2781_YRAM2_END_PAGE)) { + if (reg <= TAS2781_YRAM2_END_REG && + reg >= TAS2781_YRAM2_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg < TAS2781_YRAM2_START_REG) { + if (reg + len - 1 >= TAS2781_YRAM2_START_REG) { + cd->offset = TAS2781_YRAM2_START_REG; + cd->len = reg + len - TAS2781_YRAM2_START_REG; + in = true; + } + } + } + + return in; +} + +/* Return Code: + * true -- the registers are in the inblock yram + * false -- the registers are NOT in the inblock yram + */ +static bool check_inblock_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2) + in = check_inblock_yram_bk(cd, page, reg, len); + + return in; +} + +static bool check_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in; + + in = check_inpage_yram(cd, book, page, reg, len); + if (in) + goto end; + in = check_inblock_yram(cd, book, page, reg, len); + +end: + return in; +} + +static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, + unsigned short chn, unsigned char book, unsigned char page, + unsigned char reg, unsigned int len) +{ + struct tas_crc crc_data; + unsigned char crc_chksum = 0; + unsigned char nBuf1[128]; + int ret = 0; + int i; + bool in; + + if ((reg + len - 1) > 127) { + ret = -EINVAL; + dev_err(tasdevice->dev, "firmware error\n"); + goto end; + } + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (len == 4)) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, len); + if (!in) + goto end; + + if (len == 1) { + dev_err(tasdevice->dev, "firmware error\n"); + ret = -EINVAL; + goto end; + } + + ret = tasdevice_dev_bulk_read(tasdevice, chn, + TASDEVICE_REG(book, page, crc_data.offset), + nBuf1, crc_data.len); + if (ret < 0) + goto end; + + for (i = 0; i < crc_data.len; i++) { + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID( + TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + <= (TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG) + + 4))) + /*DSP swap command, bypass */ + continue; + else + crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i], + 1, 0); + } + + ret = crc_chksum; + +end: + return ret; +} + +static int do_singlereg_checksum(struct tasdevice_priv *tasdevice, + unsigned short chl, unsigned char book, unsigned char page, + unsigned char reg, unsigned char val) +{ + struct tas_crc crc_data; + unsigned int nData1; + int ret = 0; + bool in; + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (reg <= (TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG) + 4))) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, 1); + if (!in) + goto end; + ret = tasdevice_dev_read(tasdevice, chl, + TASDEVICE_REG(book, page, reg), &nData1); + if (ret < 0) + goto end; + + if (nData1 != val) { + dev_err(tasdevice->dev, + "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + book, page, reg, val, nData1); + tasdevice->tasdevice[chl].err_code |= ERROR_YRAM_CRCCHK; + ret = -EAGAIN; + goto end; + } + + ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0); + +end: + return ret; +} + +static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev) +{ + if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) + || (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) + || (type == MAIN_DEVICE_D)) + dev->cur_prog = -1; + else + dev->cur_conf = -1; +} + +static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned int len, + unsigned char val, unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) + ret = tasdev_multibytes_chksum(tas_priv, chn, book, page, reg, + len); + else + ret = do_singlereg_checksum(tas_priv, chn, book, page, reg, + val); + + if (ret > 0) { + *crc_chksum += (unsigned char)ret; + goto end; + } + + if (ret != -EAGAIN) + goto end; + + block->nr_retry--; + if (block->nr_retry > 0) + goto end; + + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + +end: + return ret; +} + +static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned char *data, + unsigned int len, unsigned int *nr_cmds, + unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) { + ret = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data + 3, len); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, + book, page, reg, len, 0, crc_chksum); + } else { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data[3]); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, book, + page, reg, 1, data[3], crc_chksum); + } + + if (!block->is_ychksum_present || ret >= 0) { + *nr_cmds += 1; + if (len >= 2) + *nr_cmds += ((len - 2) / 4) + 1; + } + +end: + return ret; +} + +static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int nr_value; + int ret; + + ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_I2CChecksum, + &nr_value); + if (ret < 0) { + dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn); + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + goto end; + } + + if ((nr_value & 0xff) != block->pchksum) { + dev_err(tas_priv->dev, "%s: Blk PChkSum Chn %d ", __func__, + chn); + dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n", + block->pchksum, (nr_value & 0xff)); + tas_priv->tasdevice[chn].err_code |= ERROR_PRAM_CRCCHK; + ret = -EAGAIN; + block->nr_retry--; + + if (block->nr_retry <= 0) + set_err_prg_cfg(block->type, + &tas_priv->tasdevice[chn]); + } else + tas_priv->tasdevice[chn].err_code &= ~ERROR_PRAM_CRCCHK; + +end: + return ret; +} + +static int tasdev_load_blk(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int sleep_time; + unsigned int len; + unsigned int nr_cmds; + unsigned char *data = block->data; + unsigned char crc_chksum = 0; + unsigned char offset; + unsigned char book; + unsigned char page; + unsigned char val; + int ret = 0; + + while (block->nr_retry > 0) { + if (block->is_pchksum_present) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_I2CChecksum, 0); + if (ret < 0) + break; + } + + if (block->is_ychksum_present) + crc_chksum = 0; + + nr_cmds = 0; + + while (nr_cmds < block->nr_cmds) { + data = block->data + nr_cmds * 4; + + book = data[0]; + page = data[1]; + offset = data[2]; + val = data[3]; + + nr_cmds++; + /*Single byte write*/ + if (offset <= 0x7F) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, offset), + val); + if (ret < 0) + goto end; + if (block->is_ychksum_present) { + ret = tasdev_bytes_chksum(tas_priv, + block, chn, book, page, offset, + 1, val, &crc_chksum); + if (ret < 0) + break; + } + continue; + } + /*sleep command*/ + if (offset == 0x81) { + /*book -- data[0] page -- data[1]*/ + sleep_time = ((book << 8) + page)*1000; + usleep_range(sleep_time, sleep_time + 50); + continue; + } + /*Multiple bytes write*/ + if (offset == 0x85) { + data += 4; + len = (book << 8) + page; + book = data[0]; + page = data[1]; + offset = data[2]; + ret = tasdev_multibytes_wr(tas_priv, + block, chn, book, page, offset, data, + len, &nr_cmds, &crc_chksum); + if (ret < 0) + break; + } + } + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + + if (block->is_pchksum_present) { + ret = tasdev_block_chksum(tas_priv, block, chn); + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + } + + if (block->is_ychksum_present) { + /* TBD, open it when FW ready */ + dev_err(tas_priv->dev, + "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n", + block->ychksum, crc_chksum); + + tas_priv->tasdevice[chn].err_code &= + ~ERROR_YRAM_CRCCHK; + ret = 0; + } + /*skip current blk*/ + break; + } + +end: + return ret; +} + +static int tasdevice_load_block(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + int chnend = 0; + int ret = 0; + int chn = 0; + int rc = 0; + + switch (block->type) { + case MAIN_ALL_DEVICES: + chn = 0; + chnend = tas_priv->ndev; + break; + case MAIN_DEVICE_A: + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + chn = 0; + chnend = 1; + break; + case MAIN_DEVICE_B: + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + chn = 1; + chnend = 2; + break; + case MAIN_DEVICE_C: + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + chn = 2; + chnend = 3; + break; + case MAIN_DEVICE_D: + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + chn = 3; + chnend = 4; + break; + default: + dev_dbg(tas_priv->dev, "load blk: Other Type = 0x%02x\n", + block->type); + break; + } + + for (; chn < chnend; chn++) { + block->nr_retry = 6; + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + ret = tasdev_load_blk(tas_priv, block, chn); + if (ret < 0) + dev_err(tas_priv->dev, "dev %d, Blk (%d) load error\n", + chn, block->type); + rc |= ret; + } + + return rc; +} + +static int dspfw_default_callback(struct tasdevice_priv *tas_priv, + unsigned int drv_ver, unsigned int ppcver) +{ + int rc = 0; + + if (drv_ver == 0x100) { + if (ppcver >= PPC3_VERSION) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + } else { + switch (ppcver) { + case 0x00: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + dev_err(tas_priv->dev, + "%s: PPCVer must be 0x0 or 0x%02x", + __func__, PPC3_VERSION); + dev_err(tas_priv->dev, " Current:0x%02x\n", + ppcver); + rc = -EINVAL; + break; + } + } + } else { + dev_err(tas_priv->dev, + "DrvVer must be 0x0, 0x230 or above 0x230 "); + dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver); + rc = -EINVAL; + } + + return rc; +} + +static int load_calib_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +static int fw_parse_header(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 }; + const unsigned char *buf = (unsigned char *)fmw->data; + + if (offset + 92 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + if (memcmp(&buf[offset], magic_number, 4)) { + dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + /* Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + fw_fixed_hdr->fwsize = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_fixed_hdr->fwsize != fmw->size) { + dev_err(tas_priv->dev, "File size not match, %lu %u", + (unsigned long)fmw->size, fw_fixed_hdr->fwsize); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_fixed_hdr->ppcver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_fixed_hdr->drv_ver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 72; + + out: + return offset; +} + +static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != 1) { + dev_err(tas_priv->dev, + "%s: calbin must be 1, but currently ndev(%u)\n", + __func__, fw_hdr->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +/* When calibrated data parsing error occurs, DSP can still work with default + * calibrated data, memory resource related to calibrated data will be + * released in the tasdevice_codec_remove. + */ +static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_calibration *calibration; + unsigned char *data = (unsigned char *)fmw->data; + unsigned int i, n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_calibrations != 1) { + dev_err(tas_priv->dev, + "%s: only supports one calibration (%d)!\n", + __func__, tas_fmw->nr_calibrations); + goto out; + } + + tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations, + sizeof(struct tasdevice_calibration), GFP_KERNEL); + if (!tas_fmw->calibrations) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "Calibrations error\n"); + offset = -EINVAL; + goto out; + } + calibration = &(tas_fmw->calibrations[i]); + offset += 64; + + n = strlen((char *)&data[offset]); + /* skip '\0' and 2 unused bytes */ + n += 3; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + offset += n; + + offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +int tas2781_load_calibration(void *context, char *file_name, + unsigned short i) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); + const struct firmware *fw_entry; + struct tasdevice_fw *tas_fmw; + struct firmware fmw; + int offset = 0; + int ret; + + ret = request_firmware(&fw_entry, file_name, tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", + __func__, file_name); + goto out; + } + + if (!fw_entry->size) { + dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", + __func__, (unsigned long)fw_entry->size); + ret = -EINVAL; + goto out; + } + fmw.size = fw_entry->size; + fmw.data = fw_entry->data; + + tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(struct tasdevice_fw), + GFP_KERNEL); + if (!tasdev->cali_data_fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); + ret = offset; + goto out; + } + offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); + ret = offset; + goto out; + } + +out: + if (fw_entry) + release_firmware(fw_entry); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_dspfw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; + struct tasdevice_fw *tas_fmw; + int offset = 0; + int ret = 0; + + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", + __func__, tas_priv->coef_binaryname); + ret = -EINVAL; + goto out; + } + + tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL); + if (!tas_priv->fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw = tas_priv->fmw; + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); + + if (offset == -EINVAL) { + ret = -EINVAL; + goto out; + } + fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr); + /* Support different versions of firmware */ + switch (fw_fixed_hdr->drv_ver) { + case 0x301: + case 0x302: + case 0x502: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + break; + case 0x202: + case 0x400: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + ret = dspfw_default_callback(tas_priv, + fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver); + if (ret) + goto out; + break; + } + + offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, + offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_configuration_data(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + ret = offset; + +out: + return ret; +} + +int tasdevice_dsp_parser(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + const struct firmware *fw_entry; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, + tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); + goto out; + } + + ret = tasdevice_dspfw_ready(fw_entry, tas_priv); + release_firmware(fw_entry); + fw_entry = NULL; + +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, SND_SOC_TAS2781_FMWLIB); + +static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw) +{ + struct tasdevice_calibration *calibration; + struct tasdev_blk *block; + struct tasdevice_data *im; + unsigned int blks; + int i; + + if (!tas_fmw->calibrations) + goto out; + + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + calibration = &(tas_fmw->calibrations[i]); + if (!calibration) + continue; + + im = &(calibration->dev_data); + + if (!im->dev_blks) + continue; + + for (blks = 0; blks < im->nr_blk; blks++) { + block = &(im->dev_blks[blks]); + if (!block) + continue; + kfree(block->data); + } + kfree(im->dev_blks); + } + kfree(tas_fmw->calibrations); +out: + kfree(tas_fmw); +} + +void tasdevice_calbin_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice *tasdev; + int i; + + if (!tas_priv) + return; + + for (i = 0; i < tas_priv->ndev; i++) { + tasdev = &(tas_priv->tasdevice[i]); + if (!tasdev->cali_data_fmw) + continue; + tas2781_clear_calfirmware(tasdev->cali_data_fmw); + tasdev->cali_data_fmw = NULL; + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_calbin_remove, SND_SOC_TAS2781_FMWLIB); + +void tasdevice_config_info_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **ci = rca->cfg_info; + int i, j; + + if (!ci) + return; + for (i = 0; i < rca->ncfgs; i++) { + if (!ci[i]) + continue; + if (ci[i]->blk_data) { + for (j = 0; j < (int)ci[i]->real_nblocks; j++) { + if (!ci[i]->blk_data[j]) + continue; + kfree(ci[i]->blk_data[j]->regdata); + kfree(ci[i]->blk_data[j]); + } + kfree(ci[i]->blk_data); + } + kfree(ci[i]); + } + kfree(ci); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_config_info_remove, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tas_priv->tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +int tasdevice_select_tuningprm_cfg(void *context, int prm_no, + int cfg_no, int rca_conf_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + struct tasdevice_config *conf; + int prog_status = 0; + int status, i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (cfg_no >= tas_fmw->nr_configurations) { + dev_err(tas_priv->dev, + "%s: cfg(%d) is not in range of conf %u\n", + __func__, cfg_no, tas_fmw->nr_configurations); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 || + !cfg_info) { + dev_err(tas_priv->dev, + "conf_no:%d should be in range from 0 to %u\n", + rca_conf_no, rca->ncfgs-1); + goto out; + } + + conf = &(tas_fmw->configs[cfg_no]); + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (cfg_info[rca_conf_no]->active_dev & (1 << i)) { + if (tas_priv->tasdevice[i].cur_prog != prm_no + || tas_priv->force_fwload_status) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } else + tas_priv->tasdevice[i].is_loading = false; + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration + *cal = cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + + for (i = 0, status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_conf != cfg_no + && (cfg_info[rca_conf_no]->active_dev & (1 << i)) + && (tas_priv->tasdevice[i].is_loaderr == false)) { + status++; + tas_priv->tasdevice[i].is_loading = true; + } else + tas_priv->tasdevice[i].is_loading = false; + } + + if (status) { + status = 0; + tasdevice_load_data(tas_priv, &(conf->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) { + status |= 1 << (i + 4); + continue; + } else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_conf = cfg_no; + } + } else + dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n", + __func__, cfg_no); + + status |= cfg_info[rca_conf_no]->active_dev; + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_tuningprm_cfg, + SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_calibdata_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration *cal = + cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_calibdata_load, + SND_SOC_TAS2781_FMWLIB); + +void tasdevice_tuning_switch(void *context, int state) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + return; + } + + if (state == 0) { + if (tas_priv->cur_prog < tas_fmw->nr_programs) { + /*dsp mode or tuning mode*/ + profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + tasdevice_select_tuningprm_cfg(tas_priv, + tas_priv->cur_prog, tas_priv->cur_conf, + profile_cfg_id); + } + + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + } else + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, + SND_SOC_TAS2781_FMWLIB); + +MODULE_DESCRIPTION("Texas Firmware Support"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c new file mode 100644 index 000000000000..4c59429a42b7 --- /dev/null +++ b/sound/soc/codecs/tas2781-i2c.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier +// +// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2781 driver implements a flexible and configurable +// algo coefficient setting for one, two, or even multiple +// TAS2781 chips. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> +// Author: Kevin Lu <kevin-lu@ti.com> +// + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781.h> +#include <sound/tlv.h> +#include <sound/tas2781-tlv.h> + +static const struct i2c_device_id tasdevice_id[] = { + { "tas2781", TAS2781 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tasdevice_id); + +#ifdef CONFIG_OF +static const struct of_device_id tasdevice_of_match[] = { + { .compatible = "ti,tas2781" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tasdevice_of_match); +#endif + +/** + * tas2781_digital_getvol - get the volum control + * @kcontrol: control pointer + * @ucontrol: User data + * Customer Kcontrol for tas2781 is primarily for regmap booking, paging + * depends on internal regmap mechanism. + * tas2781 contains book and page two-level register map, especially + * book switching will set the register BXXP00R7F, after switching to the + * correct book, then leverage the mechanism for paging to access the + * register. + */ +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return 0; +} + +static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + bool change, val = (bool)ucontrol->value.integer.value[0]; + + if (tas_priv->force_fwload_status == val) + change = false; + else { + change = true; + tas_priv->force_fwload_status = val; + } + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return change; +} + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->rcabin.profile_cfg_id != + ucontrol->value.integer.value[0]) { + tas_priv->rcabin.profile_cfg_id = + ucontrol->value.integer.value[0]; + ret = 1; + } + + return ret; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_programs; + + return 0; +} + +static int tasdevice_info_configurations( + struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_configurations - 1; + + return 0; +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; + + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + + return 0; +} + +static int tasdevice_create_control(struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *prof_ctrls; + int nr_controls = 1; + int mix_index = 0; + int ret; + char *name; + + prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(prof_ctrls[0]), GFP_KERNEL); + if (!prof_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "Speaker Profile Id"); + prof_ctrls[mix_index].name = name; + prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + prof_ctrls[mix_index].info = tasdevice_info_profile; + prof_ctrls[mix_index].get = tasdevice_get_profile_id; + prof_ctrls[mix_index].put = tasdevice_set_profile_id; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static int tasdevice_program_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_program = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_prog != nr_program) { + tas_priv->cur_prog = nr_program; + ret = 1; + } + + return ret; +} + +static int tasdevice_configuration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + + return 0; +} + +static int tasdevice_configuration_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_configuration = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_conf != nr_configuration) { + tas_priv->cur_conf = nr_configuration; + ret = 1; + } + + return ret; +} + +static int tasdevice_dsp_create_ctrls( + struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *dsp_ctrls; + char *prog_name, *conf_name; + int nr_controls = 2; + int mix_index = 0; + int ret; + + /* Alloc kcontrol via devm_kzalloc, which don't manually + * free the kcontrol + */ + dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(dsp_ctrls[0]), GFP_KERNEL); + if (!dsp_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + prog_name = devm_kzalloc(tas_priv->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + conf_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!prog_name || !conf_name) { + ret = -ENOMEM; + goto out; + } + + scnprintf(prog_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Program Id"); + dsp_ctrls[mix_index].name = prog_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_programs; + dsp_ctrls[mix_index].get = tasdevice_program_get; + dsp_ctrls[mix_index].put = tasdevice_program_put; + mix_index++; + + scnprintf(conf_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Config Id"); + dsp_ctrls[mix_index].name = conf_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_configurations; + dsp_ctrls[mix_index].get = tasdevice_configuration_get; + dsp_ctrls[mix_index].put = tasdevice_configuration_put; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, + nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static void tasdevice_fw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = context; + int ret = 0; + int i; + + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out; + tasdevice_create_control(tas_priv); + + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", + tas_priv->dev_name); + ret = tasdevice_dsp_parser(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dspfw load %s error\n", + tas_priv->coef_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + tasdevice_dsp_create_ctrls(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + + /* If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + for (i = 0; i < tas_priv->ndev; i++) { + scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", + tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr); + ret = tas2781_load_calibration(tas_priv, + tas_priv->cal_binaryname[i], i); + if (ret != 0) + dev_err(tas_priv->dev, + "%s: load %s error, default will effect\n", + __func__, tas_priv->cal_binaryname[i]); + } + + tasdevice_prmg_calibdata_load(tas_priv, 0); + tas_priv->cur_prog = 0; +out: + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + /*If DSP FW fail, kcontrol won't be created */ + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + } + mutex_unlock(&tas_priv->codec_lock); + if (fmw) + release_firmware(fmw); +} + +static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + + /* Codec Lock Hold */ + mutex_lock(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; + tasdevice_tuning_switch(tas_priv, state); + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + + return 0; +} + +static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, + 0, 0, tasdevice_dapm_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_INPUT("DMIC") +}; + +static const struct snd_soc_dapm_route tasdevice_audio_map[] = { + {"SPK", NULL, "ASI"}, + {"OUT", NULL, "SPK"}, + {"ASI OUT", NULL, "DMIC"} +}; + +static int tasdevice_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + ret = -EINVAL; + } + + return ret; +} + +static int tasdevice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(dai); + unsigned int slot_width; + unsigned int fsrate; + int bclk_rate; + int rc = 0; + + fsrate = params_rate(params); + switch (fsrate) { + case 48000: + case 44100: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n", + __func__, fsrate); + rc = -EINVAL; + goto out; + } + + slot_width = params_width(params); + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n", + __func__, slot_width); + rc = -EINVAL; + goto out; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) { + dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n", + __func__, bclk_rate); + rc = bclk_rate; + goto out; + } + +out: + return rc; +} + +static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(codec_dai); + + tas_priv->sysclk = freq; + + return 0; +} + +static const struct snd_soc_dai_ops tasdevice_dai_ops = { + .startup = tasdevice_startup, + .hw_params = tasdevice_hw_params, + .set_sysclk = tasdevice_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver tasdevice_dai_driver[] = { + { + .name = "tas2781_codec", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .ops = &tasdevice_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int tasdevice_codec_probe(struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + return tascodec_init(tas_priv, codec, tasdevice_fw_ready); +} + +static void tasdevice_deinit(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static void tasdevice_codec_remove( + struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + tasdevice_deinit(tas_priv); +} + +static const struct snd_soc_component_driver + soc_codec_driver_tasdevice = { + .probe = tasdevice_codec_probe, + .remove = tasdevice_codec_remove, + .controls = tas2781_snd_controls, + .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .dapm_widgets = tasdevice_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), + .dapm_routes = tasdevice_audio_map, + .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + unsigned int dev_addrs[TASDEVICE_MAX_CHANNELS]; + int rc, i, ndev = 0; + + if (tas_priv->isacpi) { + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", NULL, 0); + if (ndev <= 0) { + ndev = 1; + dev_addrs[0] = client->addr; + } else { + ndev = (ndev < ARRAY_SIZE(dev_addrs)) + ? ndev : ARRAY_SIZE(dev_addrs); + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", dev_addrs, ndev); + } + + tas_priv->irq_info.irq_gpio = + acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0); + } else { + struct device_node *np = tas_priv->dev->of_node; +#ifdef CONFIG_OF + const __be32 *reg, *reg_end; + int len, sw, aw; + + aw = of_n_addr_cells(np); + sw = of_n_size_cells(np); + if (sw == 0) { + reg = (const __be32 *)of_get_property(np, + "reg", &len); + reg_end = reg + len/sizeof(*reg); + ndev = 0; + do { + dev_addrs[ndev] = of_read_number(reg, aw); + reg += aw; + ndev++; + } while (reg < reg_end); + } else { + ndev = 1; + dev_addrs[0] = client->addr; + } +#else + ndev = 1; + dev_addrs[0] = client->addr; +#endif + tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); + } + tas_priv->ndev = ndev; + for (i = 0; i < ndev; i++) + tas_priv->tasdevice[i].dev_addr = dev_addrs[i]; + + tas_priv->reset = devm_gpiod_get_optional(&client->dev, + "reset-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(tas_priv->reset)) + dev_err(tas_priv->dev, "%s Can't get reset GPIO\n", + __func__); + + strcpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name); + + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) { + rc = gpio_request(tas_priv->irq_info.irq_gpio, + "AUDEV-IRQ"); + if (!rc) { + gpio_direction_input( + tas_priv->irq_info.irq_gpio); + + tas_priv->irq_info.irq = + gpio_to_irq(tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, "%s: GPIO %d request error\n", + __func__, tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, + "Looking up irq-gpio property failed %d\n", + tas_priv->irq_info.irq_gpio); +} + +static int tasdevice_i2c_probe(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c); + const struct acpi_device_id *acpi_id; + struct tasdevice_priv *tas_priv; + int ret; + + tas_priv = tasdevice_kzalloc(i2c); + if (!tas_priv) + return -ENOMEM; + + if (ACPI_HANDLE(&i2c->dev)) { + acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, + &i2c->dev); + if (!acpi_id) { + dev_err(&i2c->dev, "No driver data\n"); + ret = -EINVAL; + goto err; + } + tas_priv->chip_id = acpi_id->driver_data; + tas_priv->isacpi = true; + } else { + tas_priv->chip_id = id ? id->driver_data : 0; + tas_priv->isacpi = false; + } + + tasdevice_parse_dt(tas_priv); + + ret = tasdevice_init(tas_priv); + if (ret) + goto err; + + ret = devm_snd_soc_register_component(tas_priv->dev, + &soc_codec_driver_tasdevice, + tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); + if (ret) { + dev_err(tas_priv->dev, "%s: codec register error:0x%08x\n", + __func__, ret); + goto err; + } +err: + if (ret < 0) + tasdevice_remove(tas_priv); + return ret; +} + +static void tasdevice_i2c_remove(struct i2c_client *client) +{ + struct tasdevice_priv *tas_priv = i2c_get_clientdata(client); + + tasdevice_remove(tas_priv); +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id tasdevice_acpi_match[] = { + { "TAS2781", TAS2781 }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); +#endif + +static struct i2c_driver tasdevice_i2c_driver = { + .driver = { + .name = "tas2781-codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tasdevice_of_match), +#ifdef CONFIG_ACPI + .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), +#endif + }, + .probe = tasdevice_i2c_probe, + .remove = tasdevice_i2c_remove, + .id_table = tasdevice_id, +}; + +module_i2c_driver(tasdevice_i2c_driver); + +MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>"); +MODULE_AUTHOR("Kevin Lu <kevin-lu@ti.com>"); +MODULE_DESCRIPTION("ASoC TAS2781 Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB); diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index f9e7122894bd..60e59e993ba6 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -990,7 +990,7 @@ static struct i2c_driver tas5086_i2c_driver = { .of_match_table = of_match_ptr(tas5086_dt_ids), }, .id_table = tas5086_i2c_id, - .probe_new = tas5086_i2c_probe, + .probe = tas5086_i2c_probe, .remove = tas5086_i2c_remove, }; diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index f39c3273b2fd..1756edb35961 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -975,7 +975,7 @@ static struct i2c_driver tas571x_i2c_driver = { .name = "tas571x", .of_match_table = of_match_ptr(tas571x_of_match), }, - .probe_new = tas571x_i2c_probe, + .probe = tas571x_i2c_probe, .remove = tas571x_i2c_remove, .id_table = tas571x_i2c_id, }; diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index 4d27b60bd804..6dd6c0896eff 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> @@ -821,7 +820,7 @@ static struct i2c_driver tas5720_i2c_driver = { .name = "tas5720", .of_match_table = of_match_ptr(tas5720_of_match), }, - .probe_new = tas5720_probe, + .probe = tas5720_probe, .id_table = tas5720_id, }; diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index 4e38eb7acea1..aca3756ffab6 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -597,7 +597,7 @@ MODULE_DEVICE_TABLE(of, tas5805m_of_match); #endif static struct i2c_driver tas5805m_i2c_driver = { - .probe_new = tas5805m_i2c_probe, + .probe = tas5805m_i2c_probe, .remove = tas5805m_i2c_remove, .id_table = tas5805m_i2c_id, .driver = { diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index f8ff69fa2549..da89e8c681dd 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> @@ -803,7 +802,7 @@ static struct i2c_driver tas6424_i2c_driver = { .name = "tas6424", .of_match_table = of_match_ptr(tas6424_of_ids), }, - .probe_new = tas6424_i2c_probe, + .probe = tas6424_i2c_probe, .remove = tas6424_i2c_remove, .id_table = tas6424_i2c_ids, }; diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c index d964e5207569..e187d74a1737 100644 --- a/sound/soc/codecs/tda7419.c +++ b/sound/soc/codecs/tda7419.c @@ -629,7 +629,7 @@ static struct i2c_driver tda7419_driver = { .name = "tda7419", .of_match_table = tda7419_of_match, }, - .probe_new = tda7419_probe, + .probe = tda7419_probe, .id_table = tda7419_i2c_id, }; diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 9f7902ec40db..8cca2ceadd9e 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -312,7 +312,7 @@ static struct i2c_driver tfa9879_i2c_driver = { .name = "tfa9879", .of_match_table = tfa9879_of_match, }, - .probe_new = tfa9879_i2c_probe, + .probe = tfa9879_i2c_probe, .id_table = tfa9879_i2c_id, }; diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index b853507e65a8..79847a90ac46 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -416,7 +416,7 @@ static struct i2c_driver tfa989x_i2c_driver = { .name = "tfa989x", .of_match_table = tfa989x_of_match, }, - .probe_new = tfa989x_i2c_probe, + .probe = tfa989x_i2c_probe, }; module_i2c_driver(tfa989x_i2c_driver); diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 52bb55724724..b976c1946286 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1451,7 +1451,7 @@ static struct i2c_driver adc3xxx_i2c_driver = { .name = "tlv320adc3xxx-codec", .of_match_table = tlv320adc3xxx_of_match, }, - .probe_new = adc3xxx_i2c_probe, + .probe = adc3xxx_i2c_probe, .remove = __exit_p(adc3xxx_i2c_remove), .id_table = adc3xxx_i2c_id, }; diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 530f321d08e9..41342b340680 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -1208,7 +1208,7 @@ static struct i2c_driver adcx140_i2c_driver = { .name = "tlv320adcx140-codec", .of_match_table = of_match_ptr(tlv320adcx140_of_match), }, - .probe_new = adcx140_i2c_probe, + .probe = adcx140_i2c_probe, .id_table = adcx140_i2c_id, }; module_i2c_driver(adcx140_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index 1f97673a1cc0..9692ae007c91 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -47,7 +47,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = { .name = "tlv320aic23-codec", .of_match_table = of_match_ptr(tlv320aic23_of_match), }, - .probe_new = tlv320aic23_i2c_probe, + .probe = tlv320aic23_i2c_probe, .id_table = tlv320aic23_id, }; diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 0847302121f6..9611aa8acb0d 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1746,7 +1746,7 @@ static struct i2c_driver aic31xx_i2c_driver = { .of_match_table = of_match_ptr(tlv320aic31xx_of_match), .acpi_match_table = ACPI_PTR(aic31xx_acpi_match), }, - .probe_new = aic31xx_i2c_probe, + .probe = aic31xx_i2c_probe, .id_table = aic31xx_i2c_id, }; module_i2c_driver(aic31xx_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index d1e543ca3521..49b33a256cd7 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -71,7 +71,7 @@ static struct i2c_driver aic32x4_i2c_driver = { .name = "tlv320aic32x4", .of_match_table = aic32x4_of_id, }, - .probe_new = aic32x4_i2c_probe, + .probe = aic32x4_i2c_probe, .remove = aic32x4_i2c_remove, .id_table = aic32x4_i2c_id, }; diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c index d7e94d564dbf..bb33fd3dfb4f 100644 --- a/sound/soc/codecs/tlv320aic3x-i2c.c +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -61,7 +61,7 @@ static struct i2c_driver aic3x_i2c_driver = { .name = "tlv320aic3x", .of_match_table = aic3x_of_id, }, - .probe_new = aic3x_i2c_probe, + .probe = aic3x_i2c_probe, .remove = aic3x_i2c_remove, .id_table = aic3x_i2c_id, }; diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 16ce3ef1134b..fa46f51d4341 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1560,7 +1560,7 @@ static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { .name = "tlv320dac33-codec", }, - .probe_new = dac33_i2c_probe, + .probe = dac33_i2c_probe, .remove = dac33_i2c_remove, .id_table = tlv320dac33_i2c_id, }; diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 8c00db32996b..5bc486283fde 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -319,7 +319,7 @@ static struct i2c_driver tpa6130a2_i2c_driver = { .name = "tpa6130a2", .of_match_table = of_match_ptr(tpa6130a2_of_match), }, - .probe_new = tpa6130a2_probe, + .probe = tpa6130a2_probe, .id_table = tpa6130a2_id, }; diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 5282112c7d8d..6d9166d9116a 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -455,7 +455,7 @@ static struct i2c_driver ts3a227e_driver = { .of_match_table = of_match_ptr(ts3a227e_of_match), .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), }, - .probe_new = ts3a227e_i2c_probe, + .probe = ts3a227e_i2c_probe, .id_table = ts3a227e_i2c_ids, }; module_i2c_driver(ts3a227e_driver); diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index fa0c525189c2..1eefc1fe6ea8 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -1503,7 +1503,7 @@ static struct i2c_driver tscs42xx_i2c_driver = { .name = "tscs42xx", .of_match_table = tscs42xx_of_match, }, - .probe_new = tscs42xx_i2c_probe, + .probe = tscs42xx_i2c_probe, .id_table = tscs42xx_i2c_id, }; diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index 38622bc247fc..744aef32a21f 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -3473,7 +3473,7 @@ static struct i2c_driver tscs454_i2c_driver = { .name = "tscs454", .of_match_table = tscs454_of_match, }, - .probe_new = tscs454_i2c_probe, + .probe = tscs454_i2c_probe, .id_table = tscs454_i2c_id, }; diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index fdaaee845176..5c5751dc14e5 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -798,7 +798,7 @@ static struct i2c_driver uda1380_i2c_driver = { .name = "uda1380-codec", .of_match_table = uda1380_of_match, }, - .probe_new = uda1380_i2c_probe, + .probe = uda1380_i2c_probe, .id_table = uda1380_i2c_id, }; diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 9c10200ff34b..bd0e9fbc12eb 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1270,7 +1270,7 @@ static int wcd9380_probe(struct sdw_slave *pdev, /* Start in cache-only until device is enumerated */ regcache_cache_only(wcd->regmap, true); - }; + } pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 034a4e858c7e..1d4259433f47 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -994,3 +994,6 @@ module_spi_driver(wm0010_spi_driver); MODULE_DESCRIPTION("ASoC WM0010 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("wm0010.dfw"); +MODULE_FIRMWARE("wm0010_stage2.bin"); diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index 0064a607ec68..d7eeb41ba60f 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -243,7 +243,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = { .driver = { .name = "wm1250-ev1", }, - .probe_new = wm1250_ev1_probe, + .probe = wm1250_ev1_probe, .remove = wm1250_ev1_remove, .id_table = wm1250_ev1_i2c_id, }; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 14b4fd97488c..9571ea53cb3f 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -938,7 +938,7 @@ static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", }, - .probe_new = wm2000_i2c_probe, + .probe = wm2000_i2c_probe, .id_table = wm2000_i2c_id, }; diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 0a65afa44a59..277b8c468c78 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2485,7 +2485,7 @@ static struct i2c_driver wm2200_i2c_driver = { .name = "wm2200", .pm = &wm2200_pm, }, - .probe_new = wm2200_i2c_probe, + .probe = wm2200_i2c_probe, .remove = wm2200_i2c_remove, .id_table = wm2200_i2c_id, }; diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 3b09d4a1684f..a86eacb2a9bb 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2709,7 +2709,7 @@ static struct i2c_driver wm5100_i2c_driver = { .name = "wm5100", .pm = &wm5100_pm, }, - .probe_new = wm5100_i2c_probe, + .probe = wm5100_i2c_probe, .remove = wm5100_i2c_remove, .id_table = wm5100_i2c_id, }; diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index e13f9780a111..c0ed76d5b65f 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -678,7 +678,7 @@ static struct i2c_driver wm8510_i2c_driver = { .name = "wm8510", .of_match_table = wm8510_of_match, }, - .probe_new = wm8510_i2c_probe, + .probe = wm8510_i2c_probe, .id_table = wm8510_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 66f6371d8acf..55c72c5ac845 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -527,7 +527,7 @@ static struct i2c_driver wm8523_i2c_driver = { .name = "wm8523", .of_match_table = wm8523_of_match, }, - .probe_new = wm8523_i2c_probe, + .probe = wm8523_i2c_probe, .id_table = wm8523_i2c_id, }; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index ca796aa0aeb7..34ae7fe05398 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -1049,7 +1049,7 @@ static struct i2c_driver wm8580_i2c_driver = { .name = "wm8580", .of_match_table = wm8580_of_match, }, - .probe_new = wm8580_i2c_probe, + .probe = wm8580_i2c_probe, .id_table = wm8580_i2c_id, }; diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 383c6796e8a3..903a0147d584 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -464,7 +464,7 @@ static struct i2c_driver wm8711_i2c_driver = { .name = "wm8711", .of_match_table = wm8711_of_match, }, - .probe_new = wm8711_i2c_probe, + .probe = wm8711_i2c_probe, .id_table = wm8711_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index a3dbdbf40723..5ea6d8fd10f6 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -305,7 +305,7 @@ static struct i2c_driver wm8728_i2c_driver = { .name = "wm8728", .of_match_table = wm8728_of_match, }, - .probe_new = wm8728_i2c_probe, + .probe = wm8728_i2c_probe, .id_table = wm8728_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c index fdf03bf91606..c39e637d813d 100644 --- a/sound/soc/codecs/wm8731-i2c.c +++ b/sound/soc/codecs/wm8731-i2c.c @@ -57,7 +57,7 @@ static struct i2c_driver wm8731_i2c_driver = { .name = "wm8731", .of_match_table = wm8731_of_match, }, - .probe_new = wm8731_i2c_probe, + .probe = wm8731_i2c_probe, .id_table = wm8731_i2c_id, }; diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 90b54343370c..9f4e372e90ea 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -649,7 +649,7 @@ static struct i2c_driver wm8737_i2c_driver = { .name = "wm8737", .of_match_table = wm8737_of_match, }, - .probe_new = wm8737_i2c_probe, + .probe = wm8737_i2c_probe, .id_table = wm8737_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index c7afa4f2795d..787156b980a1 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -616,7 +616,7 @@ static struct i2c_driver wm8741_i2c_driver = { .name = "wm8741", .of_match_table = wm8741_of_match, }, - .probe_new = wm8741_i2c_probe, + .probe = wm8741_i2c_probe, .id_table = wm8741_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 2f6ee8d6639f..20dc9ff9fea9 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -813,7 +813,7 @@ static struct i2c_driver wm8750_i2c_driver = { .name = "wm8750", .of_match_table = wm8750_of_match, }, - .probe_new = wm8750_i2c_probe, + .probe = wm8750_i2c_probe, .id_table = wm8750_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index bb18f58dc670..5e8a8eb41b2b 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1590,7 +1590,7 @@ static struct i2c_driver wm8753_i2c_driver = { .name = "wm8753", .of_match_table = wm8753_of_match, }, - .probe_new = wm8753_i2c_probe, + .probe = wm8753_i2c_probe, .id_table = wm8753_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 936ea24621b0..212224a68006 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -523,7 +523,7 @@ static struct i2c_driver wm8776_i2c_driver = { .name = "wm8776", .of_match_table = wm8776_of_match, }, - .probe_new = wm8776_i2c_probe, + .probe = wm8776_i2c_probe, .id_table = wm8776_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index 3ce1a39d76eb..7062a8b2f8b5 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -60,7 +60,7 @@ static struct i2c_driver wm8804_i2c_driver = { .of_match_table = of_match_ptr(wm8804_of_match), .acpi_match_table = ACPI_PTR(wm8804_acpi_match), }, - .probe_new = wm8804_i2c_probe, + .probe = wm8804_i2c_probe, .remove = wm8804_i2c_remove, .id_table = wm8804_i2c_id }; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 03bbd85ebdf4..320ccd92f318 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1295,7 +1295,7 @@ static struct i2c_driver wm8900_i2c_driver = { .driver = { .name = "wm8900", }, - .probe_new = wm8900_i2c_probe, + .probe = wm8900_i2c_probe, .remove = wm8900_i2c_remove, .id_table = wm8900_i2c_id, }; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 1dc8e20bdace..901b65ef8de5 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2209,7 +2209,7 @@ static struct i2c_driver wm8903_i2c_driver = { .name = "wm8903", .of_match_table = wm8903_of_match, }, - .probe_new = wm8903_i2c_probe, + .probe = wm8903_i2c_probe, .remove = wm8903_i2c_remove, .id_table = wm8903_i2c_id, }; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 791d8738d1c0..068e610b1b4c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2337,7 +2337,7 @@ static struct i2c_driver wm8904_i2c_driver = { .name = "wm8904", .of_match_table = of_match_ptr(wm8904_of_match), }, - .probe_new = wm8904_i2c_probe, + .probe = wm8904_i2c_probe, .id_table = wm8904_i2c_id, }; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 8eb4782c9232..53c27986d216 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -860,7 +860,7 @@ static struct i2c_driver wm8940_i2c_driver = { .name = "wm8940", .of_match_table = wm8940_of_match, }, - .probe_new = wm8940_i2c_probe, + .probe = wm8940_i2c_probe, .id_table = wm8940_i2c_id, }; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 05ef45672ebc..78044f580a67 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -1003,7 +1003,7 @@ static struct i2c_driver wm8955_i2c_driver = { .driver = { .name = "wm8955", }, - .probe_new = wm8955_i2c_probe, + .probe = wm8955_i2c_probe, .id_table = wm8955_i2c_id, }; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0d167238a369..366f5d769d6d 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -1518,7 +1518,7 @@ static struct i2c_driver wm8960_i2c_driver = { .of_match_table = of_match_ptr(wm8960_of_match), .acpi_match_table = ACPI_PTR(wm8960_acpi_match), }, - .probe_new = wm8960_i2c_probe, + .probe = wm8960_i2c_probe, .remove = wm8960_i2c_remove, .id_table = wm8960_i2c_id, }; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index a4857024711d..c076f78d04ce 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -982,7 +982,7 @@ static struct i2c_driver wm8961_i2c_driver = { .name = "wm8961", .of_match_table = of_match_ptr(wm8961_of_match), }, - .probe_new = wm8961_i2c_probe, + .probe = wm8961_i2c_probe, .id_table = wm8961_i2c_id, }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b901e4c65e8a..68ea15be7330 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3946,7 +3946,7 @@ static struct i2c_driver wm8962_i2c_driver = { .of_match_table = wm8962_of_match, .pm = &wm8962_pm, }, - .probe_new = wm8962_i2c_probe, + .probe = wm8962_i2c_probe, .remove = wm8962_i2c_remove, .id_table = wm8962_i2c_id, }; diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 4db9248de54b..b22d8f0b59be 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -700,7 +700,7 @@ static struct i2c_driver wm8971_i2c_driver = { .driver = { .name = "wm8971", }, - .probe_new = wm8971_i2c_probe, + .probe = wm8971_i2c_probe, .id_table = wm8971_i2c_id, }; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 010a394c705c..044b6f604c09 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -723,7 +723,7 @@ static struct i2c_driver wm8974_i2c_driver = { .name = "wm8974", .of_match_table = wm8974_of_match, }, - .probe_new = wm8974_i2c_probe, + .probe = wm8974_i2c_probe, .id_table = wm8974_i2c_id, }; diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index aa2f55401a88..5c829301cf4c 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -1072,7 +1072,7 @@ static struct i2c_driver wm8978_i2c_driver = { .name = "wm8978", .of_match_table = wm8978_of_match, }, - .probe_new = wm8978_i2c_probe, + .probe = wm8978_i2c_probe, .id_table = wm8978_i2c_id, }; diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 50e6ac6ccbe0..2bd26e2478d9 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -1068,7 +1068,7 @@ static struct i2c_driver wm8983_i2c_driver = { .driver = { .name = "wm8983", }, - .probe_new = wm8983_i2c_probe, + .probe = wm8983_i2c_probe, .id_table = wm8983_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 751aa6730833..c0816bcfa294 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1206,7 +1206,7 @@ static struct i2c_driver wm8985_i2c_driver = { .driver = { .name = "wm8985", }, - .probe_new = wm8985_i2c_probe, + .probe = wm8985_i2c_probe, .id_table = wm8985_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 5dbdf647cd97..b440719cca7d 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -905,7 +905,7 @@ static struct i2c_driver wm8988_i2c_driver = { .driver = { .name = "wm8988", }, - .probe_new = wm8988_i2c_probe, + .probe = wm8988_i2c_probe, .id_table = wm8988_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 589af286f133..5a8e765090af 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1247,7 +1247,7 @@ static struct i2c_driver wm8990_i2c_driver = { .driver = { .name = "wm8990", }, - .probe_new = wm8990_i2c_probe, + .probe = wm8990_i2c_probe, .id_table = wm8990_i2c_id, }; diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 30121993b7b4..8cb2ae829699 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1323,7 +1323,7 @@ static struct i2c_driver wm8991_i2c_driver = { .driver = { .name = "wm8991", }, - .probe_new = wm8991_i2c_probe, + .probe = wm8991_i2c_probe, .id_table = wm8991_i2c_id, }; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 22a47acbc6d1..feb997c698e2 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1741,7 +1741,7 @@ static struct i2c_driver wm8993_i2c_driver = { .driver = { .name = "wm8993", }, - .probe_new = wm8993_i2c_probe, + .probe = wm8993_i2c_probe, .remove = wm8993_i2c_remove, .id_table = wm8993_i2c_id, }; diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index eed48bf339f2..90588614edcc 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2268,7 +2268,7 @@ static struct i2c_driver wm8995_i2c_driver = { .driver = { .name = "wm8995", }, - .probe_new = wm8995_i2c_probe, + .probe = wm8995_i2c_probe, .id_table = wm8995_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index b52ed89d631a..5d0eb0ae0475 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -3086,7 +3086,7 @@ static struct i2c_driver wm8996_i2c_driver = { .driver = { .name = "wm8996", }, - .probe_new = wm8996_i2c_probe, + .probe = wm8996_i2c_probe, .remove = wm8996_i2c_remove, .id_table = wm8996_i2c_id, }; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 513ec0ba81bb..34a07db7342a 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1369,7 +1369,7 @@ static struct i2c_driver wm9081_i2c_driver = { .driver = { .name = "wm9081", }, - .probe_new = wm9081_i2c_probe, + .probe = wm9081_i2c_probe, .remove = wm9081_i2c_remove, .id_table = wm9081_i2c_id, }; diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index ef3524c3f07f..432729c753dd 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -616,7 +616,7 @@ static struct i2c_driver wm9090_i2c_driver = { .driver = { .name = "wm9090", }, - .probe_new = wm9090_i2c_probe, + .probe = wm9090_i2c_probe, .id_table = wm9090_id, }; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 216120b68b64..5a89abfe8784 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,7 +15,6 @@ #include <linux/firmware.h> #include <linux/list.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -686,8 +685,6 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, { struct cs_dsp_coeff_ctl *cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); struct wm_coeff_ctl *ctl; - struct snd_kcontrol *kcontrol; - char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ret; ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); @@ -699,23 +696,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, ctl = cs_ctl->priv; - if (dsp->component->name_prefix) - snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", - dsp->component->name_prefix, ctl->name); - else - snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", - ctl->name); - - kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); - if (!kcontrol) { - adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); - return -EINVAL; - } - - snd_ctl_notify(dsp->component->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); - - return 0; + return snd_soc_component_notify_control(dsp->component, ctl->name); } EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index e80b53143569..e40d583a1ce6 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -1333,7 +1333,8 @@ static int wsa883x_digital_mute(struct snd_soc_dai *dai, int mute, int stream) WSA883X_DRE_GAIN_EN_MASK, WSA883X_DRE_GAIN_FROM_CSR); snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL, - WSA883X_GLOBAL_PA_EN_MASK, 1); + WSA883X_GLOBAL_PA_EN_MASK, + WSA883X_GLOBAL_PA_ENABLE); } diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c new file mode 100644 index 000000000000..993d76b18b53 --- /dev/null +++ b/sound/soc/codecs/wsa884x.c @@ -0,0 +1,1936 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define WSA884X_BASE 0x3000 +#define WSA884X_ANA_BG_TSADC_BASE (WSA884X_BASE + 0x0001) +#define WSA884X_BG_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x00) +#define WSA884X_ADC_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x01) +#define WSA884X_BOP1_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x02) +#define WSA884X_BOP2_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x03) +#define WSA884X_BOP2_PROG_BOP2_VTH_MASK 0xf0 +#define WSA884X_BOP2_PROG_BOP2_VTH_SHIFT 4 +#define WSA884X_BOP2_PROG_BOP2_HYST_MASK 0x0f +#define WSA884X_BOP2_PROG_BOP2_HYST_SHIFT 0 +#define WSA884X_UVLO_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x04) +#define WSA884X_UVLO_PROG1 (WSA884X_ANA_BG_TSADC_BASE + 0x05) +#define WSA884X_SPARE_CTRL_0 (WSA884X_ANA_BG_TSADC_BASE + 0x06) +#define WSA884X_SPARE_CTRL_1 (WSA884X_ANA_BG_TSADC_BASE + 0x07) +#define WSA884X_SPARE_CTRL_2 (WSA884X_ANA_BG_TSADC_BASE + 0x08) +#define WSA884X_SPARE_CTRL_3 (WSA884X_ANA_BG_TSADC_BASE + 0x09) +#define WSA884X_REF_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x0a) +#define WSA884X_REF_CTRL_BG_RDY_SEL_MASK 0x03 +#define WSA884X_REF_CTRL_BG_RDY_SEL_SHIFT 0 +#define WSA884X_BG_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0b) +#define WSA884X_BG_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x0c) +#define WSA884X_ADC_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x0d) +#define WSA884X_ADC_IREF_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0e) +#define WSA884X_ADC_ISENS_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0f) +#define WSA884X_ADC_CLK_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x10) +#define WSA884X_ADC_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x11) +#define WSA884X_ADC_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x12) +#define WSA884X_VBAT_SNS (WSA884X_ANA_BG_TSADC_BASE + 0x13) +#define WSA884X_DOUT_MSB (WSA884X_ANA_BG_TSADC_BASE + 0x14) +#define WSA884X_DOUT_LSB (WSA884X_ANA_BG_TSADC_BASE + 0x15) +#define WSA884X_BOP_ATEST_SEL (WSA884X_ANA_BG_TSADC_BASE + 0x16) +#define WSA884X_MISC0 (WSA884X_ANA_BG_TSADC_BASE + 0x17) +#define WSA884X_MISC1 (WSA884X_ANA_BG_TSADC_BASE + 0x18) +#define WSA884X_MISC2 (WSA884X_ANA_BG_TSADC_BASE + 0x19) +#define WSA884X_MISC3 (WSA884X_ANA_BG_TSADC_BASE + 0x1a) +#define WSA884X_SPARE_TSBG_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1b) +#define WSA884X_SPARE_TUNE_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1c) +#define WSA884X_SPARE_TUNE_1 (WSA884X_ANA_BG_TSADC_BASE + 0x1d) + +#define WSA884X_ANA_IVSENSE_BASE (WSA884X_BASE + 0x0020) +#define WSA884X_VSENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x00) +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK 0xe0 +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_SHIFT 5 +#define WSA884X_ISENSE2 (WSA884X_ANA_IVSENSE_BASE + 0x01) +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK 0xe0 +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_SHIFT 5 + +#define WSA884X_SPARE_CTL_1 (WSA884X_ANA_IVSENSE_BASE + 0x02) +#define WSA884X_SPARE_CTL_2 (WSA884X_ANA_IVSENSE_BASE + 0x03) +#define WSA884X_SPARE_CTL_3 (WSA884X_ANA_IVSENSE_BASE + 0x04) +#define WSA884X_SPARE_CTL_4 (WSA884X_ANA_IVSENSE_BASE + 0x05) +#define WSA884X_EN (WSA884X_ANA_IVSENSE_BASE + 0x06) +#define WSA884X_OVERRIDE1 (WSA884X_ANA_IVSENSE_BASE + 0x07) +#define WSA884X_OVERRIDE2 (WSA884X_ANA_IVSENSE_BASE + 0x08) +#define WSA884X_ISENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x09) +#define WSA884X_ISENSE_CAL (WSA884X_ANA_IVSENSE_BASE + 0x0a) +#define WSA884X_MISC (WSA884X_ANA_IVSENSE_BASE + 0x0b) +#define WSA884X_ADC_0 (WSA884X_ANA_IVSENSE_BASE + 0x0c) +#define WSA884X_ADC_1 (WSA884X_ANA_IVSENSE_BASE + 0x0d) +#define WSA884X_ADC_2 (WSA884X_ANA_IVSENSE_BASE + 0x0e) +#define WSA884X_ADC_3 (WSA884X_ANA_IVSENSE_BASE + 0x0f) +#define WSA884X_ADC_4 (WSA884X_ANA_IVSENSE_BASE + 0x10) +#define WSA884X_ADC_5 (WSA884X_ANA_IVSENSE_BASE + 0x11) +#define WSA884X_ADC_6 (WSA884X_ANA_IVSENSE_BASE + 0x12) +#define WSA884X_ADC_7 (WSA884X_ANA_IVSENSE_BASE + 0x13) +#define WSA884X_STATUS (WSA884X_ANA_IVSENSE_BASE + 0x14) +#define WSA884X_IVSENSE_SPARE_TUNE_1 (WSA884X_ANA_IVSENSE_BASE + 0x15) +#define WSA884X_SPARE_TUNE_2 (WSA884X_ANA_IVSENSE_BASE + 0x16) +#define WSA884X_SPARE_TUNE_3 (WSA884X_ANA_IVSENSE_BASE + 0x17) +#define WSA884X_SPARE_TUNE_4 (WSA884X_ANA_IVSENSE_BASE + 0x18) + +#define WSA884X_ANA_SPK_TOP_BASE (WSA884X_BASE + 0x0040) +#define WSA884X_TOP_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x00) +#define WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK 0x01 +#define WSA884X_CLIP_DET_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x01) +#define WSA884X_CLIP_DET_CTRL2 (WSA884X_ANA_SPK_TOP_BASE + 0x02) +#define WSA884X_DAC_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x03) +#define WSA884X_DAC_VCM_CTRL_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x04) +#define WSA884X_DAC_VCM_CTRL_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x05) +#define WSA884X_DAC_VCM_CTRL_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x06) +#define WSA884X_DAC_VCM_CTRL_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x07) +#define WSA884X_DAC_VCM_CTRL_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x08) +#define WSA884X_DAC_VCM_CTRL_REG6 (WSA884X_ANA_SPK_TOP_BASE + 0x09) +#define WSA884X_PWM_CLK_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0a) +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_MASK 0x80 +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_SHIFT 7 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_MASK 0x40 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_SHIFT 6 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_MASK 0x30 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_SHIFT 4 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK 0x08 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_SHIFT 3 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_MASK 0x06 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_SHIFT 1 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_MASK 0x01 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_SHIFT 0 +#define WSA884X_DRV_LF_LDO_SEL (WSA884X_ANA_SPK_TOP_BASE + 0x0b) +#define WSA884X_OCP_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0c) +#define WSA884X_PDRV_HS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0d) +#define WSA884X_PDRV_LS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0e) +#define WSA884X_SPK_TOP_SPARE_CTL_1 (WSA884X_ANA_SPK_TOP_BASE + 0x0f) +#define WSA884X_SPK_TOP_SPARE_CTL_2 (WSA884X_ANA_SPK_TOP_BASE + 0x10) +#define WSA884X_SPK_TOP_SPARE_CTL_3 (WSA884X_ANA_SPK_TOP_BASE + 0x11) +#define WSA884X_SPK_TOP_SPARE_CTL_4 (WSA884X_ANA_SPK_TOP_BASE + 0x12) +#define WSA884X_SPARE_CTL_5 (WSA884X_ANA_SPK_TOP_BASE + 0x13) +#define WSA884X_DAC_EN_DEBUG_REG (WSA884X_ANA_SPK_TOP_BASE + 0x14) +#define WSA884X_DAC_OPAMP_BIAS1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x15) +#define WSA884X_DAC_OPAMP_BIAS2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x16) +#define WSA884X_DAC_TUNE1 (WSA884X_ANA_SPK_TOP_BASE + 0x17) +#define WSA884X_DAC_VOLTAGE_CTRL_REG (WSA884X_ANA_SPK_TOP_BASE + 0x18) +#define WSA884X_ATEST1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x19) +#define WSA884X_ATEST2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x1a) +#define WSA884X_TOP_BIAS_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x1b) +#define WSA884X_TOP_BIAS_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1c) +#define WSA884X_TOP_BIAS_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x1d) +#define WSA884X_TOP_BIAS_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x1e) +#define WSA884X_PWRSTG_DBG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1f) +#define WSA884X_DRV_LF_BLK_EN (WSA884X_ANA_SPK_TOP_BASE + 0x20) +#define WSA884X_DRV_LF_EN (WSA884X_ANA_SPK_TOP_BASE + 0x21) +#define WSA884X_DRV_LF_MASK_DCC_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x22) +#define WSA884X_DRV_LF_MISC_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x23) +#define WSA884X_DRV_LF_REG_GAIN (WSA884X_ANA_SPK_TOP_BASE + 0x24) +#define WSA884X_DRV_OS_CAL_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x25) +#define WSA884X_DRV_OS_CAL_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x26) +#define WSA884X_PWRSTG_DBG (WSA884X_ANA_SPK_TOP_BASE + 0x27) +#define WSA884X_BBM_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x28) +#define WSA884X_TOP_MISC1 (WSA884X_ANA_SPK_TOP_BASE + 0x29) +#define WSA884X_DAC_VCM_CTRL_REG7 (WSA884X_ANA_SPK_TOP_BASE + 0x2a) +#define WSA884X_TOP_BIAS_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x2b) +#define WSA884X_DRV_LF_MISC_CTL2 (WSA884X_ANA_SPK_TOP_BASE + 0x2c) +#define WSA884X_SPK_TOP_SPARE_TUNE_2 (WSA884X_ANA_SPK_TOP_BASE + 0x2d) +#define WSA884X_SPK_TOP_SPARE_TUNE_3 (WSA884X_ANA_SPK_TOP_BASE + 0x2e) +#define WSA884X_SPK_TOP_SPARE_TUNE_4 (WSA884X_ANA_SPK_TOP_BASE + 0x2f) +#define WSA884X_SPARE_TUNE_5 (WSA884X_ANA_SPK_TOP_BASE + 0x30) +#define WSA884X_SPARE_TUNE_6 (WSA884X_ANA_SPK_TOP_BASE + 0x31) +#define WSA884X_SPARE_TUNE_7 (WSA884X_ANA_SPK_TOP_BASE + 0x32) +#define WSA884X_SPARE_TUNE_8 (WSA884X_ANA_SPK_TOP_BASE + 0x33) +#define WSA884X_SPARE_TUNE_9 (WSA884X_ANA_SPK_TOP_BASE + 0x34) +#define WSA884X_SPARE_TUNE_10 (WSA884X_ANA_SPK_TOP_BASE + 0x35) +#define WSA884X_PA_STATUS0 (WSA884X_ANA_SPK_TOP_BASE + 0x36) +#define WSA884X_PA_STATUS1 (WSA884X_ANA_SPK_TOP_BASE + 0x37) +#define WSA884X_PA_STATUS2 (WSA884X_ANA_SPK_TOP_BASE + 0x38) +#define WSA884X_PA_STATUS3 (WSA884X_ANA_SPK_TOP_BASE + 0x39) +#define WSA884X_PA_STATUS4 (WSA884X_ANA_SPK_TOP_BASE + 0x3a) +#define WSA884X_PA_STATUS5 (WSA884X_ANA_SPK_TOP_BASE + 0x3b) +#define WSA884X_SPARE_RO_1 (WSA884X_ANA_SPK_TOP_BASE + 0x3c) +#define WSA884X_SPARE_RO_2 (WSA884X_ANA_SPK_TOP_BASE + 0x3d) +#define WSA884X_SPARE_RO_3 (WSA884X_ANA_SPK_TOP_BASE + 0x3e) + +#define WSA884X_ANA_BOOST_BASE (WSA884X_BASE + 0x0090) +#define WSA884X_STB_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x00) +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK 0xf8 +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_SHIFT 3 +#define WSA884X_STB_CTRL1_VOUT_FS_MASK 0x07 +#define WSA884X_STB_CTRL1_VOUT_FS_SHIFT 0 +#define WSA884X_CURRENT_LIMIT (WSA884X_ANA_BOOST_BASE + 0x01) +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK 0x80 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_SHIFT 7 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK 0x7c +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_SHIFT 2 +#define WSA884X_CURRENT_LIMIT_CLK_PHASE_SHIFT 0 +#define WSA884X_BYP_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x02) +#define WSA884X_SPARE_CTL_0 (WSA884X_ANA_BOOST_BASE + 0x03) +#define WSA884X_BOOST_SPARE_CTL_1 (WSA884X_ANA_BOOST_BASE + 0x04) +#define WSA884X_SPARE_RO_0 (WSA884X_ANA_BOOST_BASE + 0x05) +#define WSA884X_BOOST_SPARE_RO_1 (WSA884X_ANA_BOOST_BASE + 0x06) +#define WSA884X_IBIAS1 (WSA884X_ANA_BOOST_BASE + 0x07) +#define WSA884X_IBIAS2 (WSA884X_ANA_BOOST_BASE + 0x08) +#define WSA884X_IBIAS3 (WSA884X_ANA_BOOST_BASE + 0x09) +#define WSA884X_EN_CTRL (WSA884X_ANA_BOOST_BASE + 0x0a) +#define WSA884X_STB_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0b) +#define WSA884X_STB_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0c) +#define WSA884X_STB_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x0d) +#define WSA884X_BYP_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0e) +#define WSA884X_BYP_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0f) +#define WSA884X_ZX_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x10) +#define WSA884X_ZX_CTRL1_ZX_DET_EN_MASK 0x80 +#define WSA884X_ZX_CTRL1_ZX_DET_EN_SHIFT 7 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_MASK 0x40 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_SHIFT 6 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_MASK 0x20 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_SHIFT 5 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK 0x18 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_SHIFT 3 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_MASK 0x04 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_SHIFT 2 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_MASK 0x02 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_SHIFT 1 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_MASK 0x01 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_SHIFT 0 +#define WSA884X_ZX_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x11) +#define WSA884X_BLEEDER_CTRL (WSA884X_ANA_BOOST_BASE + 0x12) +#define WSA884X_BOOST_MISC (WSA884X_ANA_BOOST_BASE + 0x13) +#define WSA884X_PWRSTAGE_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x14) +#define WSA884X_PWRSTAGE_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x15) +#define WSA884X_PWRSTAGE_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x16) +#define WSA884X_PWRSTAGE_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x17) +#define WSA884X_MAXD_REG1 (WSA884X_ANA_BOOST_BASE + 0x18) +#define WSA884X_MAXD_REG2 (WSA884X_ANA_BOOST_BASE + 0x19) +#define WSA884X_ILIM_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1a) +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_MASK 0x80 +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_SHIFT 0x07 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_MASK 0x40 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_SHIFT 0x06 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_MASK 0x38 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_SHIFT 0x03 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK 0x07 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_SHIFT 0x00 +#define WSA884X_ILIM_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1b) +#define WSA884X_TEST_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1c) +#define WSA884X_TEST_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1d) +#define WSA884X_SPARE1 (WSA884X_ANA_BOOST_BASE + 0x1e) +#define WSA884X_BOOT_CAP_CHECK (WSA884X_ANA_BOOST_BASE + 0x1f) + +#define WSA884X_ANA_PON_LDOL_BASE (WSA884X_BASE + 0x00b0) +#define WSA884X_PON_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x00) +#define WSA884X_PWRSAV_CTL (WSA884X_ANA_PON_LDOL_BASE + 0x01) +#define WSA884X_PON_LDOL_SPARE_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x02) +#define WSA884X_PON_LDOL_SPARE_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x03) +#define WSA884X_PON_LDOL_SPARE_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x04) +#define WSA884X_PON_LDOL_SPARE_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x05) +#define WSA884X_PON_CLT_1 (WSA884X_ANA_PON_LDOL_BASE + 0x06) +#define WSA884X_PON_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x07) +#define WSA884X_PON_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x08) +#define WSA884X_CKWD_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x09) +#define WSA884X_CKWD_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0a) +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK 0x20 +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_SHIFT 5 +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK 0x1f +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_SHIFT 0 +#define WSA884X_CKWD_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x0b) +#define WSA884X_CKSK_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0c) +#define WSA884X_PADSW_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0d) +#define WSA884X_TEST_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0e) +#define WSA884X_TEST_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0f) +#define WSA884X_STATUS_0 (WSA884X_ANA_PON_LDOL_BASE + 0x10) +#define WSA884X_STATUS_1 (WSA884X_ANA_PON_LDOL_BASE + 0x11) +#define WSA884X_PON_LDOL_SPARE_TUNE_0 (WSA884X_ANA_PON_LDOL_BASE + 0x12) +#define WSA884X_PON_LDOL_SPARE_TUNE_1 (WSA884X_ANA_PON_LDOL_BASE + 0x13) +#define WSA884X_PON_LDOL_SPARE_TUNE_2 (WSA884X_ANA_PON_LDOL_BASE + 0x14) +#define WSA884X_PON_LDOL_SPARE_TUNE_3 (WSA884X_ANA_PON_LDOL_BASE + 0x15) +#define WSA884X_PON_LDOL_SPARE_TUNE_4 (WSA884X_ANA_PON_LDOL_BASE + 0x16) + +#define WSA884X_DIG_CTRL0_BASE (WSA884X_BASE + 0x0400) +#define WSA884X_DIG_CTRL0_PAGE (WSA884X_DIG_CTRL0_BASE + 0x00) +#define WSA884X_CHIP_ID0 (WSA884X_DIG_CTRL0_BASE + 0x01) +#define WSA884X_CHIP_ID1 (WSA884X_DIG_CTRL0_BASE + 0x02) +#define WSA884X_CHIP_ID2 (WSA884X_DIG_CTRL0_BASE + 0x03) +#define WSA884X_CHIP_ID3 (WSA884X_DIG_CTRL0_BASE + 0x04) +#define WSA884X_BUS_ID (WSA884X_DIG_CTRL0_BASE + 0x05) +#define WSA884X_CDC_RST_CTL (WSA884X_DIG_CTRL0_BASE + 0x10) +#define WSA884X_SWR_RESET_EN (WSA884X_DIG_CTRL0_BASE + 0x14) +#define WSA884X_TOP_CLK_CFG (WSA884X_DIG_CTRL0_BASE + 0x18) +#define WSA884X_SWR_CLK_RATE (WSA884X_DIG_CTRL0_BASE + 0x19) +#define WSA884X_CDC_PATH_MODE (WSA884X_DIG_CTRL0_BASE + 0x1a) +#define WSA884X_CDC_PATH_MODE_RXD_MODE_MASK 0x02 +#define WSA884X_CDC_PATH_MODE_RXD_MODE_SHIFT 0 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_MASK 0x01 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_SHIFT 0 +#define WSA884X_CDC_CLK_CTL (WSA884X_DIG_CTRL0_BASE + 0x1c) +#define WSA884X_PA_FSM_EN (WSA884X_DIG_CTRL0_BASE + 0x30) +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK 0x01 +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_SHIFT 0 +#define WSA884X_PA_FSM_CTL0 (WSA884X_DIG_CTRL0_BASE + 0x31) +#define WSA884X_PA_FSM_CTL1 (WSA884X_DIG_CTRL0_BASE + 0x32) +#define WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK 0x38 +#define WSA884X_PA_FSM_TIMER0 (WSA884X_DIG_CTRL0_BASE + 0x33) +#define WSA884X_PA_FSM_TIMER1 (WSA884X_DIG_CTRL0_BASE + 0x34) +#define WSA884X_PA_FSM_STA0 (WSA884X_DIG_CTRL0_BASE + 0x35) +#define WSA884X_PA_FSM_STA1 (WSA884X_DIG_CTRL0_BASE + 0x36) +#define WSA884X_PA_FSM_ERR_CTL (WSA884X_DIG_CTRL0_BASE + 0x37) +#define WSA884X_PA_FSM_ERR_COND0 (WSA884X_DIG_CTRL0_BASE + 0x38) +#define WSA884X_PA_FSM_ERR_COND1 (WSA884X_DIG_CTRL0_BASE + 0x39) +#define WSA884X_PA_FSM_MSK0 (WSA884X_DIG_CTRL0_BASE + 0x3a) +#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b) +#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c) +#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d) +#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e) +#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50) +#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51) +#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52) +#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53) +#define WSA884X_TEMP_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x54) +#define WSA884X_TEMP_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x55) +#define WSA884X_TEMP_CONFIG0 (WSA884X_DIG_CTRL0_BASE + 0x56) +#define WSA884X_TEMP_CONFIG1 (WSA884X_DIG_CTRL0_BASE + 0x57) +#define WSA884X_VBAT_THRM_FLT_CTL (WSA884X_DIG_CTRL0_BASE + 0x58) +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_MASK 0xe0 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_SHIFT 5 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_FLT_EN_SHIFT 4 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK 0x0e +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_SHIFT 1 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_FLT_EN_SHIFT 0 +#define WSA884X_VBAT_CAL_CTL (WSA884X_DIG_CTRL0_BASE + 0x59) +#define WSA884X_VBAT_CAL_CTL_RESERVE_MASK 0x0e +#define WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK 0x01 +#define WSA884X_VBAT_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x5a) +#define WSA884X_VBAT_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x5b) +#define WSA884X_VBAT_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x5c) +#define WSA884X_VBAT_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x5d) +#define WSA884X_VBAT_CAL_MSB (WSA884X_DIG_CTRL0_BASE + 0x5e) +#define WSA884X_VBAT_CAL_LSB (WSA884X_DIG_CTRL0_BASE + 0x5f) +#define WSA884X_UVLO_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x60) +#define WSA884X_BOP_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x61) +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK 0x1e +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_SHIFT 1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK 0x1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_SHIFT 0 +#define WSA884X_VBAT_ZONE_DETC_CTL (WSA884X_DIG_CTRL0_BASE + 0x64) +#define WSA884X_CPS_CTL (WSA884X_DIG_CTRL0_BASE + 0x68) +#define WSA884X_CDC_RX_CTL (WSA884X_DIG_CTRL0_BASE + 0x70) +#define WSA884X_CDC_SPK_DSM_A1_0 (WSA884X_DIG_CTRL0_BASE + 0x71) +#define WSA884X_CDC_SPK_DSM_A1_1 (WSA884X_DIG_CTRL0_BASE + 0x72) +#define WSA884X_CDC_SPK_DSM_A2_0 (WSA884X_DIG_CTRL0_BASE + 0x73) +#define WSA884X_CDC_SPK_DSM_A2_1 (WSA884X_DIG_CTRL0_BASE + 0x74) +#define WSA884X_CDC_SPK_DSM_A3_0 (WSA884X_DIG_CTRL0_BASE + 0x75) +#define WSA884X_CDC_SPK_DSM_A3_1 (WSA884X_DIG_CTRL0_BASE + 0x76) +#define WSA884X_CDC_SPK_DSM_A4_0 (WSA884X_DIG_CTRL0_BASE + 0x77) +#define WSA884X_CDC_SPK_DSM_A4_1 (WSA884X_DIG_CTRL0_BASE + 0x78) +#define WSA884X_CDC_SPK_DSM_A5_0 (WSA884X_DIG_CTRL0_BASE + 0x79) +#define WSA884X_CDC_SPK_DSM_A5_1 (WSA884X_DIG_CTRL0_BASE + 0x7a) +#define WSA884X_CDC_SPK_DSM_A6_0 (WSA884X_DIG_CTRL0_BASE + 0x7b) +#define WSA884X_CDC_SPK_DSM_A7_0 (WSA884X_DIG_CTRL0_BASE + 0x7c) +#define WSA884X_CDC_SPK_DSM_C_0 (WSA884X_DIG_CTRL0_BASE + 0x7d) +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_1 (WSA884X_DIG_CTRL0_BASE + 0x7e) +#define WSA884X_CDC_SPK_DSM_C_2 (WSA884X_DIG_CTRL0_BASE + 0x7f) +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_3 (WSA884X_DIG_CTRL0_BASE + 0x80) +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK 0x3f +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_R1 (WSA884X_DIG_CTRL0_BASE + 0x81) +#define WSA884X_CDC_SPK_DSM_R2 (WSA884X_DIG_CTRL0_BASE + 0x82) +#define WSA884X_CDC_SPK_DSM_R3 (WSA884X_DIG_CTRL0_BASE + 0x83) +#define WSA884X_CDC_SPK_DSM_R4 (WSA884X_DIG_CTRL0_BASE + 0x84) +#define WSA884X_CDC_SPK_DSM_R5 (WSA884X_DIG_CTRL0_BASE + 0x85) +#define WSA884X_CDC_SPK_DSM_R6 (WSA884X_DIG_CTRL0_BASE + 0x86) +#define WSA884X_CDC_SPK_DSM_R7 (WSA884X_DIG_CTRL0_BASE + 0x87) +#define WSA884X_CDC_SPK_GAIN_PDM_0 (WSA884X_DIG_CTRL0_BASE + 0x88) +#define WSA884X_CDC_SPK_GAIN_PDM_1 (WSA884X_DIG_CTRL0_BASE + 0x89) +#define WSA884X_CDC_SPK_GAIN_PDM_2 (WSA884X_DIG_CTRL0_BASE + 0x8a) +#define WSA884X_PDM_WD_CTL (WSA884X_DIG_CTRL0_BASE + 0x8b) +#define WSA884X_PDM_WD_CTL_HOLD_OFF_MASK 0x04 +#define WSA884X_PDM_WD_CTL_HOLD_OFF_SHIFT 2 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_MASK 0x02 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_SHIFT 1 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK 0x01 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_SHIFT 0 +#define WSA884X_DEM_BYPASS_DATA0 (WSA884X_DIG_CTRL0_BASE + 0x90) +#define WSA884X_DEM_BYPASS_DATA1 (WSA884X_DIG_CTRL0_BASE + 0x91) +#define WSA884X_DEM_BYPASS_DATA2 (WSA884X_DIG_CTRL0_BASE + 0x92) +#define WSA884X_DEM_BYPASS_DATA3 (WSA884X_DIG_CTRL0_BASE + 0x93) +#define WSA884X_DRE_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xb0) +#define WSA884X_DRE_CTL_0_PROG_DELAY_MASK 0xf0 +#define WSA884X_DRE_CTL_0_PROG_DELAY_SHIFT 4 +#define WSA884X_DRE_CTL_0_OFFSET_MASK 0x07 +#define WSA884X_DRE_CTL_0_OFFSET_SHIFT 0 +#define WSA884X_DRE_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xb1) +#define WSA884X_DRE_CTL_1_CSR_GAIN_MASK 0x3e +#define WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT 1 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK 0x01 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_SHIFT 0 +#define WSA884X_DRE_IDLE_DET_CTL (WSA884X_DIG_CTRL0_BASE + 0xb2) +#define WSA884X_GAIN_RAMPING_CTL (WSA884X_DIG_CTRL0_BASE + 0xb8) +#define WSA884X_GAIN_RAMPING_MIN (WSA884X_DIG_CTRL0_BASE + 0xb9) +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK 0x1f +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_SHIFT 0 +#define WSA884X_TAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc0) +#define WSA884X_TAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc1) +#define WSA884X_TAGC_FORCE_VAL (WSA884X_DIG_CTRL0_BASE + 0xc2) +#define WSA884X_VAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc8) +#define WSA884X_VAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc9) +#define WSA884X_VAGC_ATTN_LVL_1 (WSA884X_DIG_CTRL0_BASE + 0xca) +#define WSA884X_VAGC_ATTN_LVL_2 (WSA884X_DIG_CTRL0_BASE + 0xcb) +#define WSA884X_VAGC_ATTN_LVL_3 (WSA884X_DIG_CTRL0_BASE + 0xcc) +#define WSA884X_CLSH_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xd0) +#define WSA884X_CLSH_CTL_0_CSR_GAIN_EN_SHIFT 7 +#define WSA884X_CLSH_CTL_0_DLY_CODE_MASK 0x70 +#define WSA884X_CLSH_CTL_0_DLY_CODE_SHIFT 4 +#define WSA884X_CLSH_CTL_0_DLY_RST_SHIFT 3 +#define WSA884X_CLSH_CTL_0_DLY_EN_SHIFT 2 +#define WSA884X_CLSH_CTL_0_INPUT_EN_SHIFT 1 +#define WSA884X_CLSH_CTL_0_CLSH_EN_SHIFT 0 +#define WSA884X_CLSH_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xd1) +#define WSA884X_CLSH_V_HD_PA (WSA884X_DIG_CTRL0_BASE + 0xd2) +#define WSA884X_CLSH_V_PA_MIN (WSA884X_DIG_CTRL0_BASE + 0xd3) +#define WSA884X_CLSH_OVRD_VAL (WSA884X_DIG_CTRL0_BASE + 0xd4) +#define WSA884X_CLSH_HARD_MAX (WSA884X_DIG_CTRL0_BASE + 0xd5) +#define WSA884X_CLSH_SOFT_MAX (WSA884X_DIG_CTRL0_BASE + 0xd6) +#define WSA884X_CLSH_SIG_DP (WSA884X_DIG_CTRL0_BASE + 0xd7) +#define WSA884X_PBR_DELAY_CTL (WSA884X_DIG_CTRL0_BASE + 0xd8) +#define WSA884X_CLSH_SRL_MAX_PBR (WSA884X_DIG_CTRL0_BASE + 0xe0) +#define WSA884X_PBR_MAX_VOLTAGE 20 +#define WSA884X_PBR_MAX_CODE 255 +#define WSA884X_VTH_TO_REG(vth) \ + ((vth) != 0 ? (((vth) - 150) * WSA884X_PBR_MAX_CODE / (WSA884X_PBR_MAX_VOLTAGE * 100) + 1) : 0) +#define WSA884X_CLSH_VTH1 (WSA884X_DIG_CTRL0_BASE + 0xe1) +#define WSA884X_CLSH_VTH2 (WSA884X_DIG_CTRL0_BASE + 0xe2) +#define WSA884X_CLSH_VTH3 (WSA884X_DIG_CTRL0_BASE + 0xe3) +#define WSA884X_CLSH_VTH4 (WSA884X_DIG_CTRL0_BASE + 0xe4) +#define WSA884X_CLSH_VTH5 (WSA884X_DIG_CTRL0_BASE + 0xe5) +#define WSA884X_CLSH_VTH6 (WSA884X_DIG_CTRL0_BASE + 0xe6) +#define WSA884X_CLSH_VTH7 (WSA884X_DIG_CTRL0_BASE + 0xe7) +#define WSA884X_CLSH_VTH8 (WSA884X_DIG_CTRL0_BASE + 0xe8) +#define WSA884X_CLSH_VTH9 (WSA884X_DIG_CTRL0_BASE + 0xe9) +#define WSA884X_CLSH_VTH10 (WSA884X_DIG_CTRL0_BASE + 0xea) +#define WSA884X_CLSH_VTH11 (WSA884X_DIG_CTRL0_BASE + 0xeb) +#define WSA884X_CLSH_VTH12 (WSA884X_DIG_CTRL0_BASE + 0xec) +#define WSA884X_CLSH_VTH13 (WSA884X_DIG_CTRL0_BASE + 0xed) +#define WSA884X_CLSH_VTH14 (WSA884X_DIG_CTRL0_BASE + 0xee) +#define WSA884X_CLSH_VTH15 (WSA884X_DIG_CTRL0_BASE + 0xef) + +#define WSA884X_DIG_CTRL1_BASE (WSA884X_BASE + 0x0500) +#define WSA884X_DIG_CTRL1_PAGE (WSA884X_DIG_CTRL1_BASE + 0x00) +#define WSA884X_VPHX_SYS_EN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x01) +#define WSA884X_ANA_WO_CTL_0 (WSA884X_DIG_CTRL1_BASE + 0x04) +#define WSA884X_ANA_WO_CTL_0_MODE_SHIFT 0 +#define WSA884X_ANA_WO_CTL_0_VPHX_SYS_EN_MASK 0xc0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_DISABLE 0x0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB 0xa +#define WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB 0x7 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK 0x3c +#define WSA884X_ANA_WO_CTL_0_PA_MIN_GAIN_BYP_MASK 0x02 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER 0x1 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK 0x01 +#define WSA884X_ANA_WO_CTL_1 (WSA884X_DIG_CTRL1_BASE + 0x05) +#define WSA884X_PIN_CTL (WSA884X_DIG_CTRL1_BASE + 0x10) +#define WSA884X_PIN_CTL_OE (WSA884X_DIG_CTRL1_BASE + 0x11) +#define WSA884X_PIN_WDATA_IOPAD (WSA884X_DIG_CTRL1_BASE + 0x12) +#define WSA884X_PIN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x13) +#define WSA884X_I2C_SLAVE_CTL (WSA884X_DIG_CTRL1_BASE + 0x14) +#define WSA884X_SPMI_PAD_CTL0 (WSA884X_DIG_CTRL1_BASE + 0x15) +#define WSA884X_SPMI_PAD_CTL1 (WSA884X_DIG_CTRL1_BASE + 0x16) +#define WSA884X_SPMI_PAD_CTL2 (WSA884X_DIG_CTRL1_BASE + 0x17) +#define WSA884X_MEM_CTL (WSA884X_DIG_CTRL1_BASE + 0x18) +#define WSA884X_SWR_HM_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x19) +#define WSA884X_SWR_HM_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x1a) +#define WSA884X_OTP_CTRL0 (WSA884X_DIG_CTRL1_BASE + 0x30) +#define WSA884X_OTP_CTRL1 (WSA884X_DIG_CTRL1_BASE + 0x31) +#define WSA884X_OTP_CTRL2 (WSA884X_DIG_CTRL1_BASE + 0x32) +#define WSA884X_OTP_STAT (WSA884X_DIG_CTRL1_BASE + 0x33) +#define WSA884X_OTP_PRG_TCSP0 (WSA884X_DIG_CTRL1_BASE + 0x34) +#define WSA884X_OTP_PRG_TCSP1 (WSA884X_DIG_CTRL1_BASE + 0x35) +#define WSA884X_OTP_PRG_TPPS (WSA884X_DIG_CTRL1_BASE + 0x36) +#define WSA884X_OTP_PRG_TVPS (WSA884X_DIG_CTRL1_BASE + 0x37) +#define WSA884X_OTP_PRG_TVPH (WSA884X_DIG_CTRL1_BASE + 0x38) +#define WSA884X_OTP_PRG_TPPR0 (WSA884X_DIG_CTRL1_BASE + 0x39) +#define WSA884X_OTP_PRG_TPPR1 (WSA884X_DIG_CTRL1_BASE + 0x3a) +#define WSA884X_OTP_PRG_TPPH (WSA884X_DIG_CTRL1_BASE + 0x3b) +#define WSA884X_OTP_PRG_END (WSA884X_DIG_CTRL1_BASE + 0x3c) +#define WSA884X_WAVG_PLAY (WSA884X_DIG_CTRL1_BASE + 0x40) +#define WSA884X_WAVG_CTL (WSA884X_DIG_CTRL1_BASE + 0x41) +#define WSA884X_WAVG_LRA_PER_0 (WSA884X_DIG_CTRL1_BASE + 0x43) +#define WSA884X_WAVG_LRA_PER_1 (WSA884X_DIG_CTRL1_BASE + 0x44) +#define WSA884X_WAVG_DELTA_THETA_0 (WSA884X_DIG_CTRL1_BASE + 0x45) +#define WSA884X_WAVG_DELTA_THETA_1 (WSA884X_DIG_CTRL1_BASE + 0x46) +#define WSA884X_WAVG_DIRECT_AMP_0 (WSA884X_DIG_CTRL1_BASE + 0x47) +#define WSA884X_WAVG_DIRECT_AMP_1 (WSA884X_DIG_CTRL1_BASE + 0x48) +#define WSA884X_WAVG_PTRN_AMP0_0 (WSA884X_DIG_CTRL1_BASE + 0x49) +#define WSA884X_WAVG_PTRN_AMP0_1 (WSA884X_DIG_CTRL1_BASE + 0x4a) +#define WSA884X_WAVG_PTRN_AMP1_0 (WSA884X_DIG_CTRL1_BASE + 0x4b) +#define WSA884X_WAVG_PTRN_AMP1_1 (WSA884X_DIG_CTRL1_BASE + 0x4c) +#define WSA884X_WAVG_PTRN_AMP2_0 (WSA884X_DIG_CTRL1_BASE + 0x4d) +#define WSA884X_WAVG_PTRN_AMP2_1 (WSA884X_DIG_CTRL1_BASE + 0x4e) +#define WSA884X_WAVG_PTRN_AMP3_0 (WSA884X_DIG_CTRL1_BASE + 0x4f) +#define WSA884X_WAVG_PTRN_AMP3_1 (WSA884X_DIG_CTRL1_BASE + 0x50) +#define WSA884X_WAVG_PTRN_AMP4_0 (WSA884X_DIG_CTRL1_BASE + 0x51) +#define WSA884X_WAVG_PTRN_AMP4_1 (WSA884X_DIG_CTRL1_BASE + 0x52) +#define WSA884X_WAVG_PTRN_AMP5_0 (WSA884X_DIG_CTRL1_BASE + 0x53) +#define WSA884X_WAVG_PTRN_AMP5_1 (WSA884X_DIG_CTRL1_BASE + 0x54) +#define WSA884X_WAVG_PTRN_AMP6_0 (WSA884X_DIG_CTRL1_BASE + 0x55) +#define WSA884X_WAVG_PTRN_AMP6_1 (WSA884X_DIG_CTRL1_BASE + 0x56) +#define WSA884X_WAVG_PTRN_AMP7_0 (WSA884X_DIG_CTRL1_BASE + 0x57) +#define WSA884X_WAVG_PTRN_AMP7_1 (WSA884X_DIG_CTRL1_BASE + 0x58) +#define WSA884X_WAVG_PER_0_1 (WSA884X_DIG_CTRL1_BASE + 0x59) +#define WSA884X_WAVG_PER_2_3 (WSA884X_DIG_CTRL1_BASE + 0x5a) +#define WSA884X_WAVG_PER_4_5 (WSA884X_DIG_CTRL1_BASE + 0x5b) +#define WSA884X_WAVG_PER_6_7 (WSA884X_DIG_CTRL1_BASE + 0x5c) +#define WSA884X_WAVG_STA (WSA884X_DIG_CTRL1_BASE + 0x5d) +#define WSA884X_INTR_MODE (WSA884X_DIG_CTRL1_BASE + 0x80) +#define WSA884X_INTR_MASK0 (WSA884X_DIG_CTRL1_BASE + 0x81) +#define WSA884X_INTR_MASK1 (WSA884X_DIG_CTRL1_BASE + 0x82) +#define WSA884X_INTR_STATUS0 (WSA884X_DIG_CTRL1_BASE + 0x83) +#define WSA884X_INTR_STATUS1 (WSA884X_DIG_CTRL1_BASE + 0x84) +#define WSA884X_INTR_CLEAR0 (WSA884X_DIG_CTRL1_BASE + 0x85) +#define WSA884X_INTR_CLEAR1 (WSA884X_DIG_CTRL1_BASE + 0x86) +#define WSA884X_INTR_LEVEL0 (WSA884X_DIG_CTRL1_BASE + 0x87) +#define WSA884X_INTR_LEVEL1 (WSA884X_DIG_CTRL1_BASE + 0x88) +#define WSA884X_INTR_SET0 (WSA884X_DIG_CTRL1_BASE + 0x89) +#define WSA884X_INTR_SET1 (WSA884X_DIG_CTRL1_BASE + 0x8a) +#define WSA884X_INTR_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x8b) +#define WSA884X_INTR_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x8c) +#define WSA884X_PDM_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc0) +#define WSA884X_ATE_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc1) +#define WSA884X_PA_FSM_DBG (WSA884X_DIG_CTRL1_BASE + 0xc2) +#define WSA884X_DIG_DEBUG_MODE (WSA884X_DIG_CTRL1_BASE + 0xc3) +#define WSA884X_DIG_DEBUG_SEL (WSA884X_DIG_CTRL1_BASE + 0xc4) +#define WSA884X_DIG_DEBUG_EN (WSA884X_DIG_CTRL1_BASE + 0xc5) +#define WSA884X_TADC_DETECT_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xc9) +#define WSA884X_TADC_DEBUG_MSB (WSA884X_DIG_CTRL1_BASE + 0xca) +#define WSA884X_TADC_DEBUG_LSB (WSA884X_DIG_CTRL1_BASE + 0xcb) +#define WSA884X_SAMPLE_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcc) +#define WSA884X_SWR_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcd) +#define WSA884X_TEST_MODE_CTL (WSA884X_DIG_CTRL1_BASE + 0xce) +#define WSA884X_IOPAD_CTL (WSA884X_DIG_CTRL1_BASE + 0xcf) +#define WSA884X_ANA_CSR_DBG_ADD (WSA884X_DIG_CTRL1_BASE + 0xd0) +#define WSA884X_ANA_CSR_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd1) +#define WSA884X_CLK_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd2) +#define WSA884X_SPARE_R (WSA884X_DIG_CTRL1_BASE + 0xf0) +#define WSA884X_SPARE_0 (WSA884X_DIG_CTRL1_BASE + 0xf1) +#define WSA884X_SPARE_1 (WSA884X_DIG_CTRL1_BASE + 0xf2) +#define WSA884X_SPARE_2 (WSA884X_DIG_CTRL1_BASE + 0xf3) +#define WSA884X_SCODE (WSA884X_DIG_CTRL1_BASE + 0xff) + +#define WSA884X_DIG_TRIM_BASE (WSA884X_BASE + 0x0800) +#define WSA884X_DIG_TRIM_PAGE (WSA884X_DIG_TRIM_BASE + 0x00) +#define WSA884X_OTP_REG_0 (WSA884X_DIG_TRIM_BASE + 0x80) +#define WSA884X_OTP_ID_WSA8840 0x0 +#define WSA884X_OTP_ID_WSA8845 0x5 +#define WSA884X_OTP_ID_WSA8845H 0xc +#define WSA884X_OTP_REG_0_ID_MASK 0x0f +#define WSA884X_OTP_REG_1 (WSA884X_DIG_TRIM_BASE + 0x81) +#define WSA884X_OTP_REG_2 (WSA884X_DIG_TRIM_BASE + 0x82) +#define WSA884X_OTP_REG_3 (WSA884X_DIG_TRIM_BASE + 0x83) +#define WSA884X_OTP_REG_4 (WSA884X_DIG_TRIM_BASE + 0x84) +#define WSA884X_OTP_REG_5 (WSA884X_DIG_TRIM_BASE + 0x85) +#define WSA884X_OTP_REG_6 (WSA884X_DIG_TRIM_BASE + 0x86) +#define WSA884X_OTP_REG_7 (WSA884X_DIG_TRIM_BASE + 0x87) +#define WSA884X_OTP_REG_8 (WSA884X_DIG_TRIM_BASE + 0x88) +#define WSA884X_OTP_REG_9 (WSA884X_DIG_TRIM_BASE + 0x89) +#define WSA884X_OTP_REG_10 (WSA884X_DIG_TRIM_BASE + 0x8a) +#define WSA884X_OTP_REG_11 (WSA884X_DIG_TRIM_BASE + 0x8b) +#define WSA884X_OTP_REG_12 (WSA884X_DIG_TRIM_BASE + 0x8c) +#define WSA884X_OTP_REG_13 (WSA884X_DIG_TRIM_BASE + 0x8d) +#define WSA884X_OTP_REG_14 (WSA884X_DIG_TRIM_BASE + 0x8e) +#define WSA884X_OTP_REG_15 (WSA884X_DIG_TRIM_BASE + 0x8f) +#define WSA884X_OTP_REG_16 (WSA884X_DIG_TRIM_BASE + 0x90) +#define WSA884X_OTP_REG_17 (WSA884X_DIG_TRIM_BASE + 0x91) +#define WSA884X_OTP_REG_18 (WSA884X_DIG_TRIM_BASE + 0x92) +#define WSA884X_OTP_REG_19 (WSA884X_DIG_TRIM_BASE + 0x93) +#define WSA884X_OTP_REG_20 (WSA884X_DIG_TRIM_BASE + 0x94) +#define WSA884X_OTP_REG_21 (WSA884X_DIG_TRIM_BASE + 0x95) +#define WSA884X_OTP_REG_22 (WSA884X_DIG_TRIM_BASE + 0x96) +#define WSA884X_OTP_REG_23 (WSA884X_DIG_TRIM_BASE + 0x97) +#define WSA884X_OTP_REG_24 (WSA884X_DIG_TRIM_BASE + 0x98) +#define WSA884X_OTP_REG_25 (WSA884X_DIG_TRIM_BASE + 0x99) +#define WSA884X_OTP_REG_26 (WSA884X_DIG_TRIM_BASE + 0x9a) +#define WSA884X_OTP_REG_27 (WSA884X_DIG_TRIM_BASE + 0x9b) +#define WSA884X_OTP_REG_28 (WSA884X_DIG_TRIM_BASE + 0x9c) +#define WSA884X_OTP_REG_29 (WSA884X_DIG_TRIM_BASE + 0x9d) +#define WSA884X_OTP_REG_30 (WSA884X_DIG_TRIM_BASE + 0x9e) +#define WSA884X_OTP_REG_31 (WSA884X_DIG_TRIM_BASE + 0x9f) +#define WSA884X_OTP_REG_32 (WSA884X_DIG_TRIM_BASE + 0xa0) +#define WSA884X_OTP_REG_33 (WSA884X_DIG_TRIM_BASE + 0xa1) +#define WSA884X_OTP_REG_34 (WSA884X_DIG_TRIM_BASE + 0xa2) +#define WSA884X_OTP_REG_35 (WSA884X_DIG_TRIM_BASE + 0xa3) +#define WSA884X_OTP_REG_36 (WSA884X_DIG_TRIM_BASE + 0xa4) +#define WSA884X_OTP_REG_37 (WSA884X_DIG_TRIM_BASE + 0xa5) +#define WSA884X_OTP_REG_38 (WSA884X_DIG_TRIM_BASE + 0xa6) +#define WSA884X_OTP_REG_38_RESERVER_MASK 0xf0 +#define WSA884X_OTP_REG_38_RESERVER_SHIFT 4 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_MASK 0x08 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_SHIFT 3 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_MASK 0x07 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_SHIFT 0 +#define WSA884X_OTP_REG_39 (WSA884X_DIG_TRIM_BASE + 0xa7) +#define WSA884X_OTP_REG_40 (WSA884X_DIG_TRIM_BASE + 0xa8) +#define WSA884X_OTP_REG_40_SPARE_TYPE2_MASK 0xc0 +#define WSA884X_OTP_REG_40_SPARE_TYPE2_SHIFT 6 +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK 0x3c +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_SHIFT 2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_MASK 0x2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_SHIFT 1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_MASK 0x1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_SHIFT 0 +#define WSA884X_OTP_REG_41 (WSA884X_DIG_TRIM_BASE + 0xa9) +#define WSA884X_OTP_REG_63 (WSA884X_DIG_TRIM_BASE + 0xbf) + +#define WSA884X_DIG_EMEM_BASE (WSA884X_BASE + 0x08C0) +#define WSA884X_EMEM_0 (WSA884X_DIG_EMEM_BASE + 0x00) +#define WSA884X_EMEM_1 (WSA884X_DIG_EMEM_BASE + 0x01) +#define WSA884X_EMEM_2 (WSA884X_DIG_EMEM_BASE + 0x02) +#define WSA884X_EMEM_3 (WSA884X_DIG_EMEM_BASE + 0x03) +#define WSA884X_EMEM_4 (WSA884X_DIG_EMEM_BASE + 0x04) +#define WSA884X_EMEM_5 (WSA884X_DIG_EMEM_BASE + 0x05) +#define WSA884X_EMEM_6 (WSA884X_DIG_EMEM_BASE + 0x06) +#define WSA884X_EMEM_7 (WSA884X_DIG_EMEM_BASE + 0x07) +#define WSA884X_EMEM_8 (WSA884X_DIG_EMEM_BASE + 0x08) +#define WSA884X_EMEM_9 (WSA884X_DIG_EMEM_BASE + 0x09) +#define WSA884X_EMEM_10 (WSA884X_DIG_EMEM_BASE + 0x0a) +#define WSA884X_EMEM_11 (WSA884X_DIG_EMEM_BASE + 0x0b) +#define WSA884X_EMEM_12 (WSA884X_DIG_EMEM_BASE + 0x0c) +#define WSA884X_EMEM_13 (WSA884X_DIG_EMEM_BASE + 0x0d) +#define WSA884X_EMEM_14 (WSA884X_DIG_EMEM_BASE + 0x0e) +#define WSA884X_EMEM_15 (WSA884X_DIG_EMEM_BASE + 0x0f) +#define WSA884X_EMEM_16 (WSA884X_DIG_EMEM_BASE + 0x10) +#define WSA884X_EMEM_17 (WSA884X_DIG_EMEM_BASE + 0x11) +#define WSA884X_EMEM_18 (WSA884X_DIG_EMEM_BASE + 0x12) +#define WSA884X_EMEM_19 (WSA884X_DIG_EMEM_BASE + 0x13) +#define WSA884X_EMEM_20 (WSA884X_DIG_EMEM_BASE + 0x14) +#define WSA884X_EMEM_21 (WSA884X_DIG_EMEM_BASE + 0x15) +#define WSA884X_EMEM_22 (WSA884X_DIG_EMEM_BASE + 0x16) +#define WSA884X_EMEM_23 (WSA884X_DIG_EMEM_BASE + 0x17) +#define WSA884X_EMEM_24 (WSA884X_DIG_EMEM_BASE + 0x18) +#define WSA884X_EMEM_25 (WSA884X_DIG_EMEM_BASE + 0x19) +#define WSA884X_EMEM_26 (WSA884X_DIG_EMEM_BASE + 0x1a) +#define WSA884X_EMEM_27 (WSA884X_DIG_EMEM_BASE + 0x1b) +#define WSA884X_EMEM_28 (WSA884X_DIG_EMEM_BASE + 0x1c) +#define WSA884X_EMEM_29 (WSA884X_DIG_EMEM_BASE + 0x1d) +#define WSA884X_EMEM_30 (WSA884X_DIG_EMEM_BASE + 0x1e) +#define WSA884X_EMEM_31 (WSA884X_DIG_EMEM_BASE + 0x1f) +#define WSA884X_EMEM_32 (WSA884X_DIG_EMEM_BASE + 0x20) +#define WSA884X_EMEM_33 (WSA884X_DIG_EMEM_BASE + 0x21) +#define WSA884X_EMEM_34 (WSA884X_DIG_EMEM_BASE + 0x22) +#define WSA884X_EMEM_35 (WSA884X_DIG_EMEM_BASE + 0x23) +#define WSA884X_EMEM_36 (WSA884X_DIG_EMEM_BASE + 0x24) +#define WSA884X_EMEM_37 (WSA884X_DIG_EMEM_BASE + 0x25) +#define WSA884X_EMEM_38 (WSA884X_DIG_EMEM_BASE + 0x26) +#define WSA884X_EMEM_39 (WSA884X_DIG_EMEM_BASE + 0x27) +#define WSA884X_EMEM_40 (WSA884X_DIG_EMEM_BASE + 0x28) +#define WSA884X_EMEM_41 (WSA884X_DIG_EMEM_BASE + 0x29) +#define WSA884X_EMEM_42 (WSA884X_DIG_EMEM_BASE + 0x2a) +#define WSA884X_EMEM_43 (WSA884X_DIG_EMEM_BASE + 0x2b) +#define WSA884X_EMEM_44 (WSA884X_DIG_EMEM_BASE + 0x2c) +#define WSA884X_EMEM_45 (WSA884X_DIG_EMEM_BASE + 0x2d) +#define WSA884X_EMEM_46 (WSA884X_DIG_EMEM_BASE + 0x2e) +#define WSA884X_EMEM_47 (WSA884X_DIG_EMEM_BASE + 0x2f) +#define WSA884X_EMEM_48 (WSA884X_DIG_EMEM_BASE + 0x30) +#define WSA884X_EMEM_49 (WSA884X_DIG_EMEM_BASE + 0x31) +#define WSA884X_EMEM_50 (WSA884X_DIG_EMEM_BASE + 0x32) +#define WSA884X_EMEM_51 (WSA884X_DIG_EMEM_BASE + 0x33) +#define WSA884X_EMEM_52 (WSA884X_DIG_EMEM_BASE + 0x34) +#define WSA884X_EMEM_53 (WSA884X_DIG_EMEM_BASE + 0x35) +#define WSA884X_EMEM_54 (WSA884X_DIG_EMEM_BASE + 0x36) +#define WSA884X_EMEM_55 (WSA884X_DIG_EMEM_BASE + 0x37) +#define WSA884X_EMEM_56 (WSA884X_DIG_EMEM_BASE + 0x38) +#define WSA884X_EMEM_57 (WSA884X_DIG_EMEM_BASE + 0x39) +#define WSA884X_EMEM_58 (WSA884X_DIG_EMEM_BASE + 0x3a) +#define WSA884X_EMEM_59 (WSA884X_DIG_EMEM_BASE + 0x3b) +#define WSA884X_EMEM_60 (WSA884X_DIG_EMEM_BASE + 0x3c) +#define WSA884X_EMEM_61 (WSA884X_DIG_EMEM_BASE + 0x3d) +#define WSA884X_EMEM_62 (WSA884X_DIG_EMEM_BASE + 0x3e) +#define WSA884X_EMEM_63 (WSA884X_DIG_EMEM_BASE + 0x3f) + +#define WSA884X_NUM_REGISTERS (WSA884X_EMEM_63 + 1) +#define WSA884X_MAX_REGISTER (WSA884X_NUM_REGISTERS - 1) + +#define WSA884X_SUPPLIES_NUM 2 +#define WSA884X_MAX_SWR_PORTS 6 +#define WSA884X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WSA884X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define WSA884X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct wsa884x_priv { + struct regmap *regmap; + struct device *dev; + struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM]; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + bool port_prepared[WSA884X_MAX_SWR_PORTS]; + bool port_enable[WSA884X_MAX_SWR_PORTS]; + unsigned int variant; + int active_ports; + int dev_mode; + bool hw_init; +}; + +enum { + COMP_OFFSET0, + COMP_OFFSET1, + COMP_OFFSET2, + COMP_OFFSET3, + COMP_OFFSET4, +}; + +enum wsa884x_gain { + G_21_DB = 0, + G_19P5_DB, + G_18_DB, + G_16P5_DB, + G_15_DB, + G_13P5_DB, + G_12_DB, + G_10P5_DB, + G_9_DB, + G_7P5_DB, + G_6_DB, + G_4P5_DB, + G_3_DB, + G_1P5_DB, + G_0_DB, + G_M1P5_DB, + G_M3_DB, + G_M4P5_DB, + G_M6_DB, + G_MAX_DB, +}; + +enum wsa884x_isense { + ISENSE_6_DB = 0, + ISENSE_12_DB, + ISENSE_15_DB, + ISENSE_18_DB, +}; + +enum wsa884x_vsense { + VSENSE_M12_DB = 0, + VSENSE_M15_DB, + VSENSE_M18_DB, + VSENSE_M21_DB, + VSENSE_M24_DB, +}; + +enum wsa884x_port_ids { + WSA884X_PORT_DAC, + WSA884X_PORT_COMP, + WSA884X_PORT_BOOST, + WSA884X_PORT_PBR, + WSA884X_PORT_VISENSE, + WSA884X_PORT_CPS, +}; + +static const char * const wsa884x_supply_name[] = { + "vdd-io", + "vdd-1p8", +}; + +static const char * const wsa884x_dev_mode_text[] = { + "Speaker", "Receiver" +}; + +enum wsa884x_mode { + WSA884X_SPEAKER, + WSA884X_RECEIVER, +}; + +static const struct soc_enum wsa884x_dev_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa884x_dev_mode_text), wsa884x_dev_mode_text); + +static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_COMP + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_BOOST + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_PBR + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_CPS + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + } +}; + +static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_COMP + 1, + .ch_mask = 0xf, + }, { + .num = WSA884X_PORT_BOOST + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_PBR + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_CPS + 1, + .ch_mask = 0x3, + }, +}; + +static struct reg_default wsa884x_defaults[] = { + { WSA884X_BG_CTRL, 0xa5 }, + { WSA884X_ADC_CTRL, 0x00 }, + { WSA884X_BOP1_PROG, 0x22 }, + { WSA884X_BOP2_PROG, 0x44 }, + { WSA884X_UVLO_PROG, 0x99 }, + { WSA884X_UVLO_PROG1, 0x70 }, + { WSA884X_SPARE_CTRL_0, 0x00 }, + { WSA884X_SPARE_CTRL_1, 0x00 }, + { WSA884X_SPARE_CTRL_2, 0x00 }, + { WSA884X_SPARE_CTRL_3, 0x00 }, + { WSA884X_REF_CTRL, 0xd2 }, + { WSA884X_BG_TEST_CTL, 0x06 }, + { WSA884X_BG_BIAS, 0xd7 }, + { WSA884X_ADC_PROG, 0x08 }, + { WSA884X_ADC_IREF_CTL, 0x57 }, + { WSA884X_ADC_ISENS_CTL, 0x47 }, + { WSA884X_ADC_CLK_CTL, 0x87 }, + { WSA884X_ADC_TEST_CTL, 0x00 }, + { WSA884X_ADC_BIAS, 0x51 }, + { WSA884X_VBAT_SNS, 0xa0 }, + { WSA884X_BOP_ATEST_SEL, 0x00 }, + { WSA884X_MISC0, 0x04 }, + { WSA884X_MISC1, 0x75 }, + { WSA884X_MISC2, 0x00 }, + { WSA884X_MISC3, 0x10 }, + { WSA884X_SPARE_TSBG_0, 0x00 }, + { WSA884X_SPARE_TUNE_0, 0x00 }, + { WSA884X_SPARE_TUNE_1, 0x00 }, + { WSA884X_VSENSE1, 0xe7 }, + { WSA884X_ISENSE2, 0x27 }, + { WSA884X_SPARE_CTL_1, 0x00 }, + { WSA884X_SPARE_CTL_2, 0x00 }, + { WSA884X_SPARE_CTL_3, 0x00 }, + { WSA884X_SPARE_CTL_4, 0x00 }, + { WSA884X_EN, 0x10 }, + { WSA884X_OVERRIDE1, 0x00 }, + { WSA884X_OVERRIDE2, 0x08 }, + { WSA884X_ISENSE1, 0xd4 }, + { WSA884X_ISENSE_CAL, 0x00 }, + { WSA884X_MISC, 0x00 }, + { WSA884X_ADC_0, 0x00 }, + { WSA884X_ADC_1, 0x00 }, + { WSA884X_ADC_2, 0x40 }, + { WSA884X_ADC_3, 0x80 }, + { WSA884X_ADC_4, 0x25 }, + { WSA884X_ADC_5, 0x24 }, + { WSA884X_ADC_6, 0x0a }, + { WSA884X_ADC_7, 0x81 }, + { WSA884X_IVSENSE_SPARE_TUNE_1, 0x00 }, + { WSA884X_SPARE_TUNE_2, 0x00 }, + { WSA884X_SPARE_TUNE_3, 0x00 }, + { WSA884X_SPARE_TUNE_4, 0x00 }, + { WSA884X_TOP_CTRL1, 0xd3 }, + { WSA884X_CLIP_DET_CTRL1, 0x7e }, + { WSA884X_CLIP_DET_CTRL2, 0x4c }, + { WSA884X_DAC_CTRL1, 0xa4 }, + { WSA884X_DAC_VCM_CTRL_REG1, 0x02 }, + { WSA884X_DAC_VCM_CTRL_REG2, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG3, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG4, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG5, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG6, 0x00 }, + { WSA884X_PWM_CLK_CTL, 0x20 }, + { WSA884X_DRV_LF_LDO_SEL, 0xaa }, + { WSA884X_OCP_CTL, 0xc6 }, + { WSA884X_PDRV_HS_CTL, 0x52 }, + { WSA884X_PDRV_LS_CTL, 0x4a }, + { WSA884X_SPK_TOP_SPARE_CTL_1, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_2, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_3, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_4, 0x00 }, + { WSA884X_SPARE_CTL_5, 0x00 }, + { WSA884X_DAC_EN_DEBUG_REG, 0x00 }, + { WSA884X_DAC_OPAMP_BIAS1_REG, 0x48 }, + { WSA884X_DAC_OPAMP_BIAS2_REG, 0x48 }, + { WSA884X_DAC_TUNE1, 0x02 }, + { WSA884X_DAC_VOLTAGE_CTRL_REG, 0x05 }, + { WSA884X_ATEST1_REG, 0x00 }, + { WSA884X_ATEST2_REG, 0x00 }, + { WSA884X_TOP_BIAS_REG1, 0x6a }, + { WSA884X_TOP_BIAS_REG2, 0x65 }, + { WSA884X_TOP_BIAS_REG3, 0x55 }, + { WSA884X_TOP_BIAS_REG4, 0xa9 }, + { WSA884X_PWRSTG_DBG2, 0x21 }, + { WSA884X_DRV_LF_BLK_EN, 0x0f }, + { WSA884X_DRV_LF_EN, 0x0a }, + { WSA884X_DRV_LF_MASK_DCC_CTL, 0x08 }, + { WSA884X_DRV_LF_MISC_CTL1, 0x30 }, + { WSA884X_DRV_LF_REG_GAIN, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL1, 0x90 }, + { WSA884X_PWRSTG_DBG, 0x08 }, + { WSA884X_BBM_CTL, 0x92 }, + { WSA884X_TOP_MISC1, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG7, 0x00 }, + { WSA884X_TOP_BIAS_REG5, 0x15 }, + { WSA884X_DRV_LF_MISC_CTL2, 0x00 }, + { WSA884X_STB_CTRL1, 0x42 }, + { WSA884X_CURRENT_LIMIT, 0x54 }, + { WSA884X_BYP_CTRL1, 0x01 }, + { WSA884X_SPARE_CTL_0, 0x00 }, + { WSA884X_BOOST_SPARE_CTL_1, 0x00 }, + { WSA884X_IBIAS1, 0x00 }, + { WSA884X_IBIAS2, 0x00 }, + { WSA884X_IBIAS3, 0x00 }, + { WSA884X_EN_CTRL, 0x42 }, + { WSA884X_STB_CTRL2, 0x03 }, + { WSA884X_STB_CTRL3, 0x3c }, + { WSA884X_STB_CTRL4, 0x30 }, + { WSA884X_BYP_CTRL2, 0x97 }, + { WSA884X_BYP_CTRL3, 0x11 }, + { WSA884X_ZX_CTRL1, 0xf0 }, + { WSA884X_ZX_CTRL2, 0x04 }, + { WSA884X_BLEEDER_CTRL, 0x04 }, + { WSA884X_BOOST_MISC, 0x62 }, + { WSA884X_PWRSTAGE_CTRL1, 0x00 }, + { WSA884X_PWRSTAGE_CTRL2, 0x31 }, + { WSA884X_PWRSTAGE_CTRL3, 0x81 }, + { WSA884X_PWRSTAGE_CTRL4, 0x5f }, + { WSA884X_MAXD_REG1, 0x00 }, + { WSA884X_MAXD_REG2, 0x5b }, + { WSA884X_ILIM_CTRL1, 0xe2 }, + { WSA884X_ILIM_CTRL2, 0x90 }, + { WSA884X_TEST_CTRL1, 0x00 }, + { WSA884X_TEST_CTRL2, 0x00 }, + { WSA884X_SPARE1, 0x00 }, + { WSA884X_BOOT_CAP_CHECK, 0x01 }, + { WSA884X_PON_CTL_0, 0x12 }, + { WSA884X_PWRSAV_CTL, 0xaa }, + { WSA884X_PON_LDOL_SPARE_CTL_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_3, 0x00 }, + { WSA884X_PON_CLT_1, 0xe1 }, + { WSA884X_PON_CTL_2, 0x00 }, + { WSA884X_PON_CTL_3, 0x70 }, + { WSA884X_CKWD_CTL_0, 0x14 }, + { WSA884X_CKWD_CTL_1, 0x3b }, + { WSA884X_CKWD_CTL_2, 0x00 }, + { WSA884X_CKSK_CTL_0, 0x00 }, + { WSA884X_PADSW_CTL_0, 0x00 }, + { WSA884X_TEST_0, 0x00 }, + { WSA884X_TEST_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_3, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_4, 0x00 }, + { WSA884X_DIG_CTRL0_PAGE, 0x00 }, + { WSA884X_CDC_RST_CTL, 0x01 }, + { WSA884X_SWR_RESET_EN, 0x00 }, + { WSA884X_TOP_CLK_CFG, 0x00 }, + { WSA884X_SWR_CLK_RATE, 0x00 }, + { WSA884X_CDC_PATH_MODE, 0x00 }, + { WSA884X_CDC_CLK_CTL, 0x1f }, + { WSA884X_PA_FSM_EN, 0x00 }, + { WSA884X_PA_FSM_CTL0, 0x00 }, + { WSA884X_PA_FSM_CTL1, 0xfe }, + { WSA884X_PA_FSM_TIMER0, 0x80 }, + { WSA884X_PA_FSM_TIMER1, 0x80 }, + { WSA884X_PA_FSM_ERR_CTL, 0x00 }, + { WSA884X_PA_FSM_MSK0, 0x00 }, + { WSA884X_PA_FSM_MSK1, 0x00 }, + { WSA884X_PA_FSM_BYP_CTL, 0x00 }, + { WSA884X_PA_FSM_BYP0, 0x00 }, + { WSA884X_PA_FSM_BYP1, 0x00 }, + { WSA884X_TADC_VALUE_CTL, 0x03 }, + { WSA884X_TEMP_DETECT_CTL, 0x01 }, + { WSA884X_TEMP_CONFIG0, 0x00 }, + { WSA884X_TEMP_CONFIG1, 0x00 }, + { WSA884X_VBAT_THRM_FLT_CTL, 0x7f }, + { WSA884X_VBAT_CAL_CTL, 0x01 }, + { WSA884X_UVLO_DEGLITCH_CTL, 0x05 }, + { WSA884X_BOP_DEGLITCH_CTL, 0x05 }, + { WSA884X_VBAT_ZONE_DETC_CTL, 0x31 }, + { WSA884X_CPS_CTL, 0x00 }, + { WSA884X_CDC_RX_CTL, 0xfe }, + { WSA884X_CDC_SPK_DSM_A1_0, 0x00 }, + { WSA884X_CDC_SPK_DSM_A1_1, 0x01 }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x96 }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x09 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xab }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x05 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x1c }, + { WSA884X_CDC_SPK_DSM_A4_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x17 }, + { WSA884X_CDC_SPK_DSM_A5_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A6_0, 0xaa }, + { WSA884X_CDC_SPK_DSM_A7_0, 0xe3 }, + { WSA884X_CDC_SPK_DSM_C_0, 0x69 }, + { WSA884X_CDC_SPK_DSM_C_1, 0x54 }, + { WSA884X_CDC_SPK_DSM_C_2, 0x02 }, + { WSA884X_CDC_SPK_DSM_C_3, 0x15 }, + { WSA884X_CDC_SPK_DSM_R1, 0xa4 }, + { WSA884X_CDC_SPK_DSM_R2, 0xb5 }, + { WSA884X_CDC_SPK_DSM_R3, 0x86 }, + { WSA884X_CDC_SPK_DSM_R4, 0x85 }, + { WSA884X_CDC_SPK_DSM_R5, 0xaa }, + { WSA884X_CDC_SPK_DSM_R6, 0xe2 }, + { WSA884X_CDC_SPK_DSM_R7, 0x62 }, + { WSA884X_CDC_SPK_GAIN_PDM_0, 0x00 }, + { WSA884X_CDC_SPK_GAIN_PDM_1, 0xfc }, + { WSA884X_CDC_SPK_GAIN_PDM_2, 0x05 }, + { WSA884X_PDM_WD_CTL, 0x00 }, + { WSA884X_DEM_BYPASS_DATA0, 0x00 }, + { WSA884X_DEM_BYPASS_DATA1, 0x00 }, + { WSA884X_DEM_BYPASS_DATA2, 0x00 }, + { WSA884X_DEM_BYPASS_DATA3, 0x00 }, + { WSA884X_DRE_CTL_0, 0x70 }, + { WSA884X_DRE_CTL_1, 0x04 }, + { WSA884X_DRE_IDLE_DET_CTL, 0x2f }, + { WSA884X_GAIN_RAMPING_CTL, 0x50 }, + { WSA884X_GAIN_RAMPING_MIN, 0x12 }, + { WSA884X_TAGC_CTL, 0x15 }, + { WSA884X_TAGC_TIME, 0xbc }, + { WSA884X_TAGC_FORCE_VAL, 0x00 }, + { WSA884X_VAGC_CTL, 0x01 }, + { WSA884X_VAGC_TIME, 0x0f }, + { WSA884X_VAGC_ATTN_LVL_1, 0x03 }, + { WSA884X_VAGC_ATTN_LVL_2, 0x06 }, + { WSA884X_VAGC_ATTN_LVL_3, 0x09 }, + { WSA884X_CLSH_CTL_0, 0x37 }, + { WSA884X_CLSH_CTL_1, 0x81 }, + { WSA884X_CLSH_V_HD_PA, 0x0c }, + { WSA884X_CLSH_V_PA_MIN, 0x00 }, + { WSA884X_CLSH_OVRD_VAL, 0x00 }, + { WSA884X_CLSH_HARD_MAX, 0xff }, + { WSA884X_CLSH_SOFT_MAX, 0xf5 }, + { WSA884X_CLSH_SIG_DP, 0x00 }, + { WSA884X_PBR_DELAY_CTL, 0x07 }, + { WSA884X_CLSH_SRL_MAX_PBR, 0x02 }, + { WSA884X_CLSH_VTH1, 0x00 }, + { WSA884X_CLSH_VTH2, 0x00 }, + { WSA884X_CLSH_VTH3, 0x00 }, + { WSA884X_CLSH_VTH4, 0x00 }, + { WSA884X_CLSH_VTH5, 0x00 }, + { WSA884X_CLSH_VTH6, 0x00 }, + { WSA884X_CLSH_VTH7, 0x00 }, + { WSA884X_CLSH_VTH8, 0x00 }, + { WSA884X_CLSH_VTH9, 0x00 }, + { WSA884X_CLSH_VTH10, 0x00 }, + { WSA884X_CLSH_VTH11, 0x00 }, + { WSA884X_CLSH_VTH12, 0x00 }, + { WSA884X_CLSH_VTH13, 0x00 }, + { WSA884X_CLSH_VTH14, 0x00 }, + { WSA884X_CLSH_VTH15, 0x00 }, + { WSA884X_DIG_CTRL1_PAGE, 0x00 }, + { WSA884X_PIN_CTL, 0x04 }, + { WSA884X_PIN_CTL_OE, 0x00 }, + { WSA884X_PIN_WDATA_IOPAD, 0x00 }, + { WSA884X_I2C_SLAVE_CTL, 0x00 }, + { WSA884X_SPMI_PAD_CTL0, 0x2f }, + { WSA884X_SPMI_PAD_CTL1, 0x2f }, + { WSA884X_SPMI_PAD_CTL2, 0x2f }, + { WSA884X_MEM_CTL, 0x00 }, + { WSA884X_SWR_HM_TEST0, 0x08 }, + { WSA884X_OTP_CTRL0, 0x00 }, + { WSA884X_OTP_CTRL2, 0x00 }, + { WSA884X_OTP_PRG_TCSP0, 0x77 }, + { WSA884X_OTP_PRG_TCSP1, 0x00 }, + { WSA884X_OTP_PRG_TPPS, 0x47 }, + { WSA884X_OTP_PRG_TVPS, 0x3b }, + { WSA884X_OTP_PRG_TVPH, 0x47 }, + { WSA884X_OTP_PRG_TPPR0, 0x47 }, + { WSA884X_OTP_PRG_TPPR1, 0x00 }, + { WSA884X_OTP_PRG_TPPH, 0x47 }, + { WSA884X_OTP_PRG_END, 0x47 }, + { WSA884X_WAVG_PLAY, 0x00 }, + { WSA884X_WAVG_CTL, 0x06 }, + { WSA884X_WAVG_LRA_PER_0, 0xd1 }, + { WSA884X_WAVG_LRA_PER_1, 0x00 }, + { WSA884X_WAVG_DELTA_THETA_0, 0xe6 }, + { WSA884X_WAVG_DELTA_THETA_1, 0x04 }, + { WSA884X_WAVG_DIRECT_AMP_0, 0x50 }, + { WSA884X_WAVG_DIRECT_AMP_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP0_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP0_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP1_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP1_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP2_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP2_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP3_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP3_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP4_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP4_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP5_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP5_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP6_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP6_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP7_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP7_1, 0x00 }, + { WSA884X_WAVG_PER_0_1, 0x88 }, + { WSA884X_WAVG_PER_2_3, 0x88 }, + { WSA884X_WAVG_PER_4_5, 0x88 }, + { WSA884X_WAVG_PER_6_7, 0x88 }, + { WSA884X_INTR_MODE, 0x00 }, + { WSA884X_INTR_MASK0, 0x90 }, + { WSA884X_INTR_MASK1, 0x00 }, + { WSA884X_INTR_CLEAR0, 0x00 }, + { WSA884X_INTR_CLEAR1, 0x00 }, + { WSA884X_INTR_LEVEL0, 0x04 }, + { WSA884X_INTR_LEVEL1, 0x00 }, + { WSA884X_INTR_SET0, 0x00 }, + { WSA884X_INTR_SET1, 0x00 }, + { WSA884X_INTR_TEST0, 0x00 }, + { WSA884X_INTR_TEST1, 0x00 }, + { WSA884X_PDM_TEST_MODE, 0x00 }, + { WSA884X_PA_FSM_DBG, 0x00 }, + { WSA884X_DIG_DEBUG_MODE, 0x00 }, + { WSA884X_DIG_DEBUG_SEL, 0x00 }, + { WSA884X_DIG_DEBUG_EN, 0x00 }, + { WSA884X_TADC_DETECT_DBG_CTL, 0x00 }, + { WSA884X_TADC_DEBUG_MSB, 0x00 }, + { WSA884X_TADC_DEBUG_LSB, 0x00 }, + { WSA884X_SAMPLE_EDGE_SEL, 0x7f }, + { WSA884X_SWR_EDGE_SEL, 0x00 }, + { WSA884X_TEST_MODE_CTL, 0x05 }, + { WSA884X_IOPAD_CTL, 0x00 }, + { WSA884X_ANA_CSR_DBG_ADD, 0x00 }, + { WSA884X_ANA_CSR_DBG_CTL, 0x12 }, + { WSA884X_CLK_DBG_CTL, 0x00 }, + { WSA884X_SPARE_0, 0x00 }, + { WSA884X_SPARE_1, 0x00 }, + { WSA884X_SPARE_2, 0x00 }, + { WSA884X_SCODE, 0x00 }, + { WSA884X_DIG_TRIM_PAGE, 0x00 }, + { WSA884X_EMEM_0, 0x00 }, + { WSA884X_EMEM_1, 0x00 }, + { WSA884X_EMEM_2, 0x00 }, + { WSA884X_EMEM_3, 0x00 }, + { WSA884X_EMEM_4, 0x00 }, + { WSA884X_EMEM_5, 0x00 }, + { WSA884X_EMEM_6, 0x00 }, + { WSA884X_EMEM_7, 0x00 }, + { WSA884X_EMEM_8, 0x00 }, + { WSA884X_EMEM_9, 0x00 }, + { WSA884X_EMEM_10, 0x00 }, + { WSA884X_EMEM_11, 0x00 }, + { WSA884X_EMEM_12, 0x00 }, + { WSA884X_EMEM_13, 0x00 }, + { WSA884X_EMEM_14, 0x00 }, + { WSA884X_EMEM_15, 0x00 }, + { WSA884X_EMEM_16, 0x00 }, + { WSA884X_EMEM_17, 0x00 }, + { WSA884X_EMEM_18, 0x00 }, + { WSA884X_EMEM_19, 0x00 }, + { WSA884X_EMEM_20, 0x00 }, + { WSA884X_EMEM_21, 0x00 }, + { WSA884X_EMEM_22, 0x00 }, + { WSA884X_EMEM_23, 0x00 }, + { WSA884X_EMEM_24, 0x00 }, + { WSA884X_EMEM_25, 0x00 }, + { WSA884X_EMEM_26, 0x00 }, + { WSA884X_EMEM_27, 0x00 }, + { WSA884X_EMEM_28, 0x00 }, + { WSA884X_EMEM_29, 0x00 }, + { WSA884X_EMEM_30, 0x00 }, + { WSA884X_EMEM_31, 0x00 }, + { WSA884X_EMEM_32, 0x00 }, + { WSA884X_EMEM_33, 0x00 }, + { WSA884X_EMEM_34, 0x00 }, + { WSA884X_EMEM_35, 0x00 }, + { WSA884X_EMEM_36, 0x00 }, + { WSA884X_EMEM_37, 0x00 }, + { WSA884X_EMEM_38, 0x00 }, + { WSA884X_EMEM_39, 0x00 }, + { WSA884X_EMEM_40, 0x00 }, + { WSA884X_EMEM_41, 0x00 }, + { WSA884X_EMEM_42, 0x00 }, + { WSA884X_EMEM_43, 0x00 }, + { WSA884X_EMEM_44, 0x00 }, + { WSA884X_EMEM_45, 0x00 }, + { WSA884X_EMEM_46, 0x00 }, + { WSA884X_EMEM_47, 0x00 }, + { WSA884X_EMEM_48, 0x00 }, + { WSA884X_EMEM_49, 0x00 }, + { WSA884X_EMEM_50, 0x00 }, + { WSA884X_EMEM_51, 0x00 }, + { WSA884X_EMEM_52, 0x00 }, + { WSA884X_EMEM_53, 0x00 }, + { WSA884X_EMEM_54, 0x00 }, + { WSA884X_EMEM_55, 0x00 }, + { WSA884X_EMEM_56, 0x00 }, + { WSA884X_EMEM_57, 0x00 }, + { WSA884X_EMEM_58, 0x00 }, + { WSA884X_EMEM_59, 0x00 }, + { WSA884X_EMEM_60, 0x00 }, + { WSA884X_EMEM_61, 0x00 }, + { WSA884X_EMEM_62, 0x00 }, + { WSA884X_EMEM_63, 0x00 }, +}; + +static bool wsa884x_readonly_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_DOUT_MSB: + case WSA884X_DOUT_LSB: + case WSA884X_STATUS: + case WSA884X_SPK_TOP_SPARE_TUNE_2: + case WSA884X_SPK_TOP_SPARE_TUNE_3: + case WSA884X_SPK_TOP_SPARE_TUNE_4: + case WSA884X_SPARE_TUNE_5: + case WSA884X_SPARE_TUNE_6: + case WSA884X_SPARE_TUNE_7: + case WSA884X_SPARE_TUNE_8: + case WSA884X_SPARE_TUNE_9: + case WSA884X_SPARE_TUNE_10: + case WSA884X_PA_STATUS0: + case WSA884X_PA_STATUS1: + case WSA884X_PA_STATUS2: + case WSA884X_PA_STATUS3: + case WSA884X_PA_STATUS4: + case WSA884X_PA_STATUS5: + case WSA884X_SPARE_RO_1: + case WSA884X_SPARE_RO_2: + case WSA884X_SPARE_RO_3: + case WSA884X_SPARE_RO_0: + case WSA884X_BOOST_SPARE_RO_1: + case WSA884X_STATUS_0: + case WSA884X_STATUS_1: + case WSA884X_CHIP_ID0: + case WSA884X_CHIP_ID1: + case WSA884X_CHIP_ID2: + case WSA884X_CHIP_ID3: + case WSA884X_BUS_ID: + case WSA884X_PA_FSM_STA0: + case WSA884X_PA_FSM_STA1: + case WSA884X_PA_FSM_ERR_COND0: + case WSA884X_PA_FSM_ERR_COND1: + case WSA884X_TEMP_DIN_MSB: + case WSA884X_TEMP_DIN_LSB: + case WSA884X_TEMP_DOUT_MSB: + case WSA884X_TEMP_DOUT_LSB: + case WSA884X_VBAT_DIN_MSB: + case WSA884X_VBAT_DIN_LSB: + case WSA884X_VBAT_DOUT_MSB: + case WSA884X_VBAT_DOUT_LSB: + case WSA884X_VBAT_CAL_MSB: + case WSA884X_VBAT_CAL_LSB: + case WSA884X_VPHX_SYS_EN_STATUS: + case WSA884X_PIN_STATUS: + case WSA884X_SWR_HM_TEST1: + case WSA884X_OTP_CTRL1: + case WSA884X_OTP_STAT: + case WSA884X_WAVG_STA: + case WSA884X_INTR_STATUS0: + case WSA884X_INTR_STATUS1: + case WSA884X_ATE_TEST_MODE: + case WSA884X_SPARE_R: + return true; + } + return false; +} + +static bool wsa884x_writeable_register(struct device *dev, unsigned int reg) +{ + return !wsa884x_readonly_register(dev, reg); +} + +static bool wsa884x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_ANA_WO_CTL_0: + case WSA884X_ANA_WO_CTL_1: + return true; + } + return wsa884x_readonly_register(dev, reg); +} + +static struct regmap_config wsa884x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wsa884x_defaults, + .max_register = WSA884X_MAX_REGISTER, + .num_reg_defaults = ARRAY_SIZE(wsa884x_defaults), + .volatile_reg = wsa884x_volatile_register, + .writeable_reg = wsa884x_writeable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .use_single_read = true, +}; + +static const struct reg_sequence wsa884x_reg_init[] = { + { WSA884X_BOP2_PROG, FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_VTH_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_HYST_MASK, 0x6) }, + { WSA884X_REF_CTRL, (0xd2 & ~WSA884X_REF_CTRL_BG_RDY_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_REF_CTRL_BG_RDY_SEL_MASK, 0x1) }, + /* + * Downstream suggests for batteries different than 1-Stacked (1S): + * { WSA884X_TOP_CTRL1, 0xd3 & ~WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK }, + */ + { WSA884X_STB_CTRL1, (0x42 & ~WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK) | + FIELD_PREP_CONST(WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK, 0xd) }, + { WSA884X_CURRENT_LIMIT, (0x54 & ~WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK) | + FIELD_PREP_CONST(WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, 0x9) }, + { WSA884X_ZX_CTRL1, (0xf0 & ~WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK, 0x3) }, + { WSA884X_ILIM_CTRL1, (0xe2 & ~WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK) | + FIELD_PREP_CONST(WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK, 0x3) }, + { WSA884X_CKWD_CTL_1, FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK, 0x0) | + FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK, 0x13) }, + { WSA884X_PA_FSM_CTL1, (0xfe & ~WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK) | + FIELD_PREP_CONST(WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK, 0x4) }, /* == 0xfe */ + { WSA884X_VBAT_THRM_FLT_CTL, (0x7f & ~WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK, 0x4) }, + { WSA884X_VBAT_CAL_CTL, FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_RESERVE_MASK, 0x2) | + FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK, 0x1) }, + { WSA884X_BOP_DEGLITCH_CTL, FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK, 0x8) | + FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK, 0x1) }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x0a }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x08 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xf3 }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x07 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x79 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x0b }, + { WSA884X_CDC_SPK_DSM_A6_0, 0x8a }, + { WSA884X_CDC_SPK_DSM_A7_0, 0x9b }, + { WSA884X_CDC_SPK_DSM_C_0, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK, 0x8) }, + { WSA884X_CDC_SPK_DSM_C_2, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK, 0xf) }, + { WSA884X_CDC_SPK_DSM_C_3, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK, 0x20) }, + { WSA884X_CDC_SPK_DSM_R1, 0x83 }, + { WSA884X_CDC_SPK_DSM_R2, 0x7f }, + { WSA884X_CDC_SPK_DSM_R3, 0x9d }, + { WSA884X_CDC_SPK_DSM_R4, 0x82 }, + { WSA884X_CDC_SPK_DSM_R5, 0x8b }, + { WSA884X_CDC_SPK_DSM_R6, 0x9b }, + { WSA884X_CDC_SPK_DSM_R7, 0x3f }, + /* Speaker mode by default */ + { WSA884X_DRE_CTL_0, FIELD_PREP_CONST(WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x7) }, + { WSA884X_CLSH_CTL_0, (0x37 & ~WSA884X_CLSH_CTL_0_DLY_CODE_MASK) | + FIELD_PREP_CONST(WSA884X_CLSH_CTL_0_DLY_CODE_MASK, 0x6) }, + /* + * WSA884X_CLSH_VTH values for speaker mode with G_21_DB system gain, + * battery 1S and rload 8 Ohms. + */ + { WSA884X_CLSH_VTH1, WSA884X_VTH_TO_REG(863), }, + { WSA884X_CLSH_VTH2, WSA884X_VTH_TO_REG(918), }, + { WSA884X_CLSH_VTH3, WSA884X_VTH_TO_REG(980), }, + { WSA884X_CLSH_VTH4, WSA884X_VTH_TO_REG(1043), }, + { WSA884X_CLSH_VTH5, WSA884X_VTH_TO_REG(1098), }, + { WSA884X_CLSH_VTH6, WSA884X_VTH_TO_REG(1137), }, + { WSA884X_CLSH_VTH7, WSA884X_VTH_TO_REG(1184), }, + { WSA884X_CLSH_VTH8, WSA884X_VTH_TO_REG(1239), }, + { WSA884X_CLSH_VTH9, WSA884X_VTH_TO_REG(1278), }, + { WSA884X_CLSH_VTH10, WSA884X_VTH_TO_REG(1380), }, + { WSA884X_CLSH_VTH11, WSA884X_VTH_TO_REG(1482), }, + { WSA884X_CLSH_VTH12, WSA884X_VTH_TO_REG(1584), }, + { WSA884X_CLSH_VTH13, WSA884X_VTH_TO_REG(1663), }, + { WSA884X_CLSH_VTH14, WSA884X_VTH_TO_REG(1780), }, + { WSA884X_CLSH_VTH15, WSA884X_VTH_TO_REG(2000), }, + { WSA884X_ANA_WO_CTL_1, 0x00 }, + { WSA884X_OTP_REG_38, 0x00 }, + { WSA884X_OTP_REG_40, FIELD_PREP_CONST(WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK, 0x8) }, +}; + +static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x) +{ + struct regmap *regmap = wsa884x->regmap; + unsigned int min_gain, igain, vgain, comp_offset; + + /* + * Downstream sets gain parameters customized per boards per use-case. + * Choose here some sane values matching knowon users, like QRD8550 + * board:. + * + * Values match here downstream: + * For WSA884X_RECEIVER - G_7P5_DB system gain + * For WSA884X_SPEAKER - G_21_DB system gain + */ + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + comp_offset = COMP_OFFSET4; + min_gain = G_M6_DB; + igain = ISENSE_18_DB; + vgain = VSENSE_M12_DB; + } else { + /* WSA884X_SPEAKER */ + comp_offset = COMP_OFFSET0; + min_gain = G_0_DB; + igain = ISENSE_12_DB; + vgain = VSENSE_M24_DB; + } + + regmap_update_bits(regmap, WSA884X_ISENSE2, + WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, + FIELD_PREP(WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, igain)); + regmap_update_bits(regmap, WSA884X_VSENSE1, + WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, + FIELD_PREP(WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, vgain)); + regmap_update_bits(regmap, WSA884X_GAIN_RAMPING_MIN, + WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, + FIELD_PREP(WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, min_gain)); + + if (wsa884x->port_enable[WSA884X_PORT_COMP]) { + regmap_update_bits(regmap, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_OFFSET_MASK, + FIELD_PREP(WSA884X_DRE_CTL_0_OFFSET_MASK, comp_offset)); + + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x0)); + } else { + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x1)); + } +} + +static void wsa884x_init(struct wsa884x_priv *wsa884x) +{ + unsigned int wo_ctl_0; + unsigned int variant = 0; + + if (!regmap_read(wsa884x->regmap, WSA884X_OTP_REG_0, &variant)) + wsa884x->variant = variant & WSA884X_OTP_REG_0_ID_MASK; + + regmap_multi_reg_write(wsa884x->regmap, wsa884x_reg_init, + ARRAY_SIZE(wsa884x_reg_init)); + + wo_ctl_0 = 0xc; + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK, + WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER); + /* Assume that compander is enabled by default unless it is haptics sku */ + if (wsa884x->variant == WSA884X_OTP_ID_WSA8845H) + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB); + else + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB); + regmap_write(wsa884x->regmap, WSA884X_ANA_WO_CTL_0, wo_ctl_0); + + wsa884x_set_gain_parameters(wsa884x); + + wsa884x->hw_init = false; +} + +static int wsa884x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + int ret; + + if (status == SDW_SLAVE_UNATTACHED) { + wsa884x->hw_init = false; + regcache_cache_only(wsa884x->regmap, true); + regcache_mark_dirty(wsa884x->regmap); + return 0; + } + + if (wsa884x->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + regcache_cache_only(wsa884x->regmap, false); + ret = regcache_sync(wsa884x->regmap); + if (ret < 0) { + dev_err(&slave->dev, "Cannot sync regmap cache\n"); + return ret; + } + + wsa884x_init(wsa884x); + + return 0; +} + +static int wsa884x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa884x->port_prepared[prepare_ch->num - 1] = true; + else + wsa884x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static const struct sdw_slave_ops wsa884x_slave_ops = { + .update_status = wsa884x_update_status, + .port_prep = wsa884x_port_prep, +}; + +static int wsa884x_dev_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wsa884x->dev_mode; + + return 0; +} + +static int wsa884x_dev_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + if (wsa884x->dev_mode == ucontrol->value.enumerated.item[0]) + return 0; + + wsa884x->dev_mode = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static int wsa884x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = wsa884x->port_enable[portidx]; + + return 0; +} + +static int wsa884x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) { + if (wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = true; + } else { + if (!wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = false; + } + + return 1; +} + +static int wsa884x_codec_probe(struct snd_soc_component *comp) +{ + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa884x->regmap); + + return 0; +} + +static void wsa884x_spkr_post_pmu(struct snd_soc_component *component, + struct wsa884x_priv *wsa884x) +{ + unsigned int curr_limit, curr_ovrd_en; + + wsa884x_set_gain_parameters(wsa884x); + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x3); + snd_soc_component_write_field(component, WSA884X_CDC_PATH_MODE, + WSA884X_CDC_PATH_MODE_RXD_MODE_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PWM_CLK_CTL, + WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK, + 0x1); + } else { + /* WSA884X_SPEAKER */ + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0xf); + } + + if (wsa884x->port_enable[WSA884X_PORT_PBR]) { + curr_ovrd_en = 0x0; + curr_limit = 0x15; + } else { + curr_ovrd_en = 0x1; + if (wsa884x->dev_mode == WSA884X_RECEIVER) + curr_limit = 0x9; + else + curr_limit = 0x15; + } + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK, + curr_ovrd_en); + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, + curr_limit); +} + +static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wsa884x_spkr_post_pmu(component, wsa884x); + + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wsa884x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_SPK("SPKR", wsa884x_spkr_event), +}; + +static const DECLARE_TLV_DB_SCALE(pa_gain, -900, 150, -900); + +static const struct snd_kcontrol_new wsa884x_snd_controls[] = { + SOC_SINGLE_RANGE_TLV("PA Volume", WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT, + 0x0, 0x1f, 1, pa_gain), + SOC_ENUM_EXT("WSA MODE", wsa884x_dev_mode_enum, + wsa884x_dev_mode_get, wsa884x_dev_mode_put), + SOC_SINGLE_EXT("DAC Switch", WSA884X_PORT_DAC, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("COMP Switch", WSA884X_PORT_COMP, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("BOOST Switch", WSA884X_PORT_BOOST, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("PBR Switch", WSA884X_PORT_PBR, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA884X_PORT_VISENSE, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("CPS Switch", WSA884X_PORT_CPS, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), +}; + +static const struct snd_soc_dapm_route wsa884x_audio_map[] = { + {"SPKR", NULL, "IN"}, +}; + +static const struct snd_soc_component_driver wsa884x_component_drv = { + .name = "WSA884x", + .probe = wsa884x_codec_probe, + .controls = wsa884x_snd_controls, + .num_controls = ARRAY_SIZE(wsa884x_snd_controls), + .dapm_widgets = wsa884x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa884x_dapm_widgets), + .dapm_routes = wsa884x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa884x_audio_map), +}; + +static int wsa884x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + int i; + + wsa884x->active_ports = 0; + for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) { + if (!wsa884x->port_enable[i]) + continue; + + wsa884x->port_config[wsa884x->active_ports] = wsa884x_pconfig[i]; + wsa884x->active_ports++; + } + + wsa884x->sconfig.frame_rate = params_rate(params); + + return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig, + wsa884x->port_config, wsa884x->active_ports, + wsa884x->sruntime); +} + +static int wsa884x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime); + + return 0; +} + +static int wsa884x_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + + } else { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + } + + return 0; +} + +static int wsa884x_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + wsa884x->sruntime = stream; + + return 0; +} + +static const struct snd_soc_dai_ops wsa884x_dai_ops = { + .hw_params = wsa884x_hw_params, + .hw_free = wsa884x_hw_free, + .mute_stream = wsa884x_mute_stream, + .set_stream = wsa884x_set_stream, +}; + +static struct snd_soc_dai_driver wsa884x_dais[] = { + { + .name = "SPKR", + .playback = { + .stream_name = "SPKR Playback", + .rates = WSA884X_RATES | WSA884X_FRAC_RATES, + .formats = WSA884X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa884x_dai_ops, + }, +}; + +static void wsa884x_gpio_powerdown(void *data) +{ + gpiod_direction_output(data, 1); +} + +static void wsa884x_regulator_disable(void *data) +{ + regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data); +} + +static int wsa884x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wsa884x_priv *wsa884x; + unsigned int i; + int ret; + + wsa884x = devm_kzalloc(dev, sizeof(*wsa884x), GFP_KERNEL); + if (!wsa884x) + return -ENOMEM; + + for (i = 0; i < WSA884X_SUPPLIES_NUM; i++) + wsa884x->supplies[i].supply = wsa884x_supply_name[i]; + + ret = devm_regulator_bulk_get(dev, WSA884X_SUPPLIES_NUM, + wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(WSA884X_SUPPLIES_NUM, wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, wsa884x_regulator_disable, + wsa884x->supplies); + if (ret) + return ret; + + wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(wsa884x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), + "Shutdown Control GPIO not found\n"); + + dev_set_drvdata(dev, wsa884x); + wsa884x->slave = pdev; + wsa884x->dev = dev; + wsa884x->dev_mode = WSA884X_SPEAKER; + wsa884x->sconfig.ch_count = 1; + wsa884x->sconfig.bps = 1; + wsa884x->sconfig.direction = SDW_DATA_DIR_RX; + wsa884x->sconfig.type = SDW_STREAM_PDM; + + pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS, 0); + pdev->prop.simple_clk_stop_capable = true; + pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; + pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* Bring out of reset */ + gpiod_direction_output(wsa884x->sd_n, 0); + ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n); + if (ret) + return ret; + + wsa884x->regmap = devm_regmap_init_sdw(pdev, &wsa884x_regmap_config); + if (IS_ERR(wsa884x->regmap)) + return dev_err_probe(dev, PTR_ERR(wsa884x->regmap), + "regmap_init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wsa884x->regmap, true); + wsa884x->hw_init = true; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return devm_snd_soc_register_component(dev, + &wsa884x_component_drv, + wsa884x_dais, + ARRAY_SIZE(wsa884x_dais)); +} + +static int __maybe_unused wsa884x_runtime_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int __maybe_unused wsa884x_runtime_resume(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + return 0; +} + +static const struct dev_pm_ops wsa884x_pm_ops = { + SET_RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL) +}; + +static const struct sdw_device_id wsa884x_swr_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x204, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wsa884x_swr_id); + +static struct sdw_driver wsa884x_codec_driver = { + .driver = { + .name = "wsa884x-codec", + .pm = &wsa884x_pm_ops, + }, + .probe = wsa884x_probe, + .ops = &wsa884x_slave_ops, + .id_table = wsa884x_swr_id, +}; +module_sdw_driver(wsa884x_codec_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); +MODULE_DESCRIPTION("WSA884x codec driver"); +MODULE_LICENSE("GPL"); |