diff options
Diffstat (limited to 'sound/soc/codecs')
289 files changed, 9163 insertions, 1540 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 269c31ce0814..252f683be3c1 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -93,6 +93,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_CS4271_I2C imply SND_SOC_CS4271_SPI imply SND_SOC_CS42XX8_I2C + imply SND_SOC_CS42XX8_SPI imply SND_SOC_CS43130 imply SND_SOC_CS4341 imply SND_SOC_CS4349 @@ -122,6 +123,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_ES8328_I2C imply SND_SOC_ES8375 imply SND_SOC_ES8389 + imply SND_SOC_ES9356 imply SND_SOC_ES7134 imply SND_SOC_ES7241 imply SND_SOC_FRAMER @@ -264,6 +266,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_STA529 imply SND_SOC_STAC9766 imply SND_SOC_STI_SAS + imply SND_SOC_TAC5XX2_SDW imply SND_SOC_TAS2552 imply SND_SOC_TAS2562 imply SND_SOC_TAS2764 @@ -275,6 +278,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS571X imply SND_SOC_TAS5720 imply SND_SOC_TAS6424 + imply SND_SOC_TAS675X imply SND_SOC_TDA7419 imply SND_SOC_TFA9879 imply SND_SOC_TFA989X @@ -884,7 +888,7 @@ config SND_SOC_CS35L56_SPI config SND_SOC_CS35L56_SDW tristate "Cirrus Logic CS35L56 CODEC (SDW)" depends on SOUNDWIRE - select REGMAP + select REGMAP_SOUNDWIRE select SND_SOC_CS35L56 select SND_SOC_CS35L56_SHARED help @@ -1074,6 +1078,12 @@ config SND_SOC_CS4271_SPI config SND_SOC_CS42XX8 tristate +config SND_SOC_CS42XX8_SPI + tristate "Cirrus Logic CS42448/CS42888 CODEC (SPI)" + depends on SPI_MASTER + select SND_SOC_CS42XX8 + select REGMAP_SPI + config SND_SOC_CS42XX8_I2C tristate "Cirrus Logic CS42448/CS42888 CODEC (I2C)" depends on I2C @@ -1297,6 +1307,13 @@ config SND_SOC_ES8389 tristate "Everest Semi ES8389 CODEC" depends on I2C +config SND_SOC_ES9356 + tristate "Everest Semi ES9356 CODEC SDW" + depends on SND_SOC_SDCA + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_FRAMER tristate "Framer codec" depends on GENERIC_FRAMER @@ -2136,6 +2153,16 @@ config SND_SOC_STAC9766 config SND_SOC_STI_SAS tristate "codec Audio support for STI SAS codec" +config SND_SOC_TAC5XX2_SDW + tristate "Texas Instruments TAC5XX2 SoundWire Smart Amplifier" + depends on SOUNDWIRE + depends on SND_SOC_SDCA + help + This option enables support for Texas Instruments TAC5XX2 family + of SoundWire Smart Amplifiers. This includes TAC5572, TAC5672, + TAC5682 and TAS2883. To compile this driver as a module, choose + M here: the module will be called snd-soc-tac5xx2. + config SND_SOC_TAS2552 tristate "Texas Instruments TAS2552 Mono Audio amplifier" depends on I2C @@ -2234,6 +2261,17 @@ config SND_SOC_TAS6424 Enable support for Texas Instruments TAS6424 high-efficiency digital input quad-channel Class-D audio power amplifiers. +config SND_SOC_TAS675X + tristate "Texas Instruments TAS675x Quad-Channel Audio Amplifier" + depends on I2C + select REGMAP_I2C + help + Enable support for Texas Instruments TAS675x quad-channel Class-D + audio power amplifiers (TAS6754, TAS67524). The devices support I2S + and TDM interfaces with real-time voltage and current sense feedback + via SDOUT, and provide DC/AC/real-time load diagnostics for speaker + monitoring. + config SND_SOC_TDA7419 tristate "ST TDA7419 audio processor" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 172861d17cfd..aa0396e5b575 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -103,6 +103,7 @@ snd-soc-cs4271-i2c-y := cs4271-i2c.o snd-soc-cs4271-spi-y := cs4271-spi.o snd-soc-cs42xx8-y := cs42xx8.o snd-soc-cs42xx8-i2c-y := cs42xx8-i2c.o +snd-soc-cs42xx8-spi-y := cs42xx8-spi.o snd-soc-cs43130-y := cs43130.o snd-soc-cs4341-y := cs4341.o snd-soc-cs4349-y := cs4349.o @@ -138,6 +139,7 @@ snd-soc-es8328-i2c-y := es8328-i2c.o snd-soc-es8328-spi-y := es8328-spi.o snd-soc-es8375-y := es8375.o snd-soc-es8389-y := es8389.o +snd-soc-es9356-y := es9356.o snd-soc-framer-y := framer-codec.o snd-soc-fs-amp-lib-y := fs-amp-lib.o snd-soc-fs210x-y := fs210x.o @@ -313,11 +315,13 @@ snd-soc-sta350-y := sta350.o snd-soc-sta529-y := sta529.o snd-soc-stac9766-y := stac9766.o snd-soc-sti-sas-y := sti-sas.o +snd-soc-tac5xx2-sdw-y := tac5xx2-sdw.o snd-soc-tas5086-y := tas5086.o snd-soc-tas571x-y := tas571x.o snd-soc-tas5720-y := tas5720.o snd-soc-tas5805m-y := tas5805m.o snd-soc-tas6424-y := tas6424.o +snd-soc-tas675x-y := tas675x.o snd-soc-tda7419-y := tda7419.o snd-soc-tas2770-y := tas2770.o snd-soc-tas2781-comlib-y := tas2781-comlib.o @@ -540,6 +544,7 @@ obj-$(CONFIG_SND_SOC_CS4271_I2C) += snd-soc-cs4271-i2c.o obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o +obj-$(CONFIG_SND_SOC_CS42XX8_SPI) += snd-soc-cs42xx8-spi.o obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o @@ -575,6 +580,7 @@ obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o obj-$(CONFIG_SND_SOC_ES8375) += snd-soc-es8375.o obj-$(CONFIG_SND_SOC_ES8389) += snd-soc-es8389.o +obj-$(CONFIG_SND_SOC_ES9356) += snd-soc-es9356.o obj-$(CONFIG_SND_SOC_FRAMER) += snd-soc-framer.o obj-$(CONFIG_SND_SOC_FS_AMP_LIB)+= snd-soc-fs-amp-lib.o obj-$(CONFIG_SND_SOC_FS210X) += snd-soc-fs210x.o @@ -746,6 +752,7 @@ obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o obj-$(CONFIG_SND_SOC_STA529) += snd-soc-sta529.o obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o obj-$(CONFIG_SND_SOC_STI_SAS) += snd-soc-sti-sas.o +obj-$(CONFIG_SND_SOC_TAC5XX2_SDW) += snd-soc-tac5xx2-sdw.o obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o @@ -760,6 +767,7 @@ obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o obj-$(CONFIG_SND_SOC_TAS5805M) += snd-soc-tas5805m.o obj-$(CONFIG_SND_SOC_TAS6424) += snd-soc-tas6424.o +obj-$(CONFIG_SND_SOC_TAS675X) += snd-soc-tas675x.o obj-$(CONFIG_SND_SOC_TDA7419) += snd-soc-tda7419.o obj-$(CONFIG_SND_SOC_TAS2770) += snd-soc-tas2770.o obj-$(CONFIG_SND_SOC_TFA9879) += snd-soc-tfa9879.o diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index 6aa168e01fbb..d4d6560bdb80 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -14,8 +14,8 @@ #include "ad193x.h" static const struct i2c_device_id ad193x_id[] = { - { "ad1936", AD193X }, - { "ad1937", AD193X }, + { .name = "ad1936", .driver_data = AD193X }, + { .name = "ad1937", .driver_data = AD193X }, { } }; MODULE_DEVICE_TABLE(i2c, ad193x_id); diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c index 73f83be38f74..bdb3e3a8509d 100644 --- a/sound/soc/codecs/adau1372-i2c.c +++ b/sound/soc/codecs/adau1372-i2c.c @@ -7,7 +7,6 @@ */ #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <sound/soc.h> @@ -21,7 +20,7 @@ static int adau1372_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adau1372_i2c_ids[] = { - { "adau1372" }, + { .name = "adau1372" }, { } }; MODULE_DEVICE_TABLE(i2c, adau1372_i2c_ids); diff --git a/sound/soc/codecs/adau1372-spi.c b/sound/soc/codecs/adau1372-spi.c index 656bd1fabeb3..a12961e2fb1e 100644 --- a/sound/soc/codecs/adau1372-spi.c +++ b/sound/soc/codecs/adau1372-spi.c @@ -6,7 +6,6 @@ * Author: Lars-Peter Clausen <lars@metafoo.de> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/spi/spi.h> diff --git a/sound/soc/codecs/adau1372.c b/sound/soc/codecs/adau1372.c index d7363f9d53bb..cc174ec3a1f8 100644 --- a/sound/soc/codecs/adau1372.c +++ b/sound/soc/codecs/adau1372.c @@ -11,7 +11,6 @@ #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/pm.h> #include <linux/slab.h> @@ -813,6 +812,11 @@ static int adau1372_set_power(struct adau1372 *adau1372, bool enable) if (adau1372->use_pll) { ret = adau1372_enable_pll(adau1372); if (ret) { + if (!adau1372->pd_gpio) + regmap_update_bits(adau1372->regmap, + ADAU1372_REG_CLK_CTRL, + ADAU1372_CLK_CTRL_PLL_EN, + 0); regcache_cache_only(adau1372->regmap, true); if (adau1372->pd_gpio) gpiod_set_value(adau1372->pd_gpio, 1); diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 16b9b2658341..5dd961b233ce 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1599,7 +1599,7 @@ static int adau1373_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adau1373_i2c_id[] = { - { "adau1373" }, + { .name = "adau1373" }, { } }; MODULE_DEVICE_TABLE(i2c, adau1373_i2c_id); diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 6876462d8bdb..329ab01b62c0 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -860,10 +860,10 @@ exit_regulators_disable: } static const struct i2c_device_id adau1701_i2c_id[] = { - { "adau1401" }, - { "adau1401a" }, - { "adau1701" }, - { "adau1702" }, + { .name = "adau1401" }, + { .name = "adau1401a" }, + { .name = "adau1701" }, + { .name = "adau1702" }, { } }; MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id); diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index eba7e4f42c78..ae73136d0a6e 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -7,7 +7,6 @@ */ #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <sound/soc.h> @@ -33,10 +32,10 @@ static void adau1761_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id adau1761_i2c_ids[] = { - { "adau1361", ADAU1361 }, - { "adau1461", ADAU1761 }, - { "adau1761", ADAU1761 }, - { "adau1961", ADAU1361 }, + { .name = "adau1361", .driver_data = ADAU1361 }, + { .name = "adau1461", .driver_data = ADAU1761 }, + { .name = "adau1761", .driver_data = ADAU1761 }, + { .name = "adau1961", .driver_data = ADAU1361 }, { } }; MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids); diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c index 7c9242c2ff94..eb6f63d63783 100644 --- a/sound/soc/codecs/adau1761-spi.c +++ b/sound/soc/codecs/adau1761-spi.c @@ -6,7 +6,6 @@ * Author: Lars-Peter Clausen <lars@metafoo.de> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/spi/spi.h> diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index cb67fde8d9a8..3ab624417f12 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -7,7 +7,6 @@ */ #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <sound/soc.h> @@ -33,8 +32,8 @@ static void adau1781_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id adau1781_i2c_ids[] = { - { "adau1381", ADAU1381 }, - { "adau1781", ADAU1781 }, + { .name = "adau1381", .driver_data = ADAU1381 }, + { .name = "adau1781", .driver_data = ADAU1781 }, { } }; MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids); diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c index 1a09633d5a88..0e6d42b10077 100644 --- a/sound/soc/codecs/adau1781-spi.c +++ b/sound/soc/codecs/adau1781-spi.c @@ -6,7 +6,6 @@ * Author: Lars-Peter Clausen <lars@metafoo.de> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/spi/spi.h> diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index 441c8079246a..d1c6c4ddf506 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -7,7 +7,6 @@ */ #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <sound/soc.h> @@ -28,9 +27,9 @@ static int adau1977_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id adau1977_i2c_ids[] = { - { "adau1977", ADAU1977 }, - { "adau1978", ADAU1978 }, - { "adau1979", ADAU1978 }, + { .name = "adau1977", .driver_data = ADAU1977 }, + { .name = "adau1978", .driver_data = ADAU1978 }, + { .name = "adau1979", .driver_data = ADAU1978 }, { } }; MODULE_DEVICE_TABLE(i2c, adau1977_i2c_ids); diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c index e7e95e5d1911..878cde9d1014 100644 --- a/sound/soc/codecs/adau1977-spi.c +++ b/sound/soc/codecs/adau1977-spi.c @@ -6,7 +6,6 @@ * Author: Lars-Peter Clausen <lars@metafoo.de> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/of.h> diff --git a/sound/soc/codecs/adau7118-hw.c b/sound/soc/codecs/adau7118-hw.c index 45a5d2dcc0f2..92b226b8b4bb 100644 --- a/sound/soc/codecs/adau7118-hw.c +++ b/sound/soc/codecs/adau7118-hw.c @@ -6,7 +6,6 @@ // Copyright 2019 Analog Devices Inc. #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include "adau7118.h" diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c index f9dc8f4ef9a4..7cccb4c6cff7 100644 --- a/sound/soc/codecs/adau7118-i2c.c +++ b/sound/soc/codecs/adau7118-i2c.c @@ -68,8 +68,8 @@ static const struct of_device_id adau7118_of_match[] = { MODULE_DEVICE_TABLE(of, adau7118_of_match); static const struct i2c_device_id adau7118_id[] = { - {"adau7118"}, - {} + { .name = "adau7118" }, + { } }; MODULE_DEVICE_TABLE(i2c, adau7118_id); diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c index 8b96c41f0354..9e5f8e866acb 100644 --- a/sound/soc/codecs/adav803.c +++ b/sound/soc/codecs/adav803.c @@ -14,7 +14,7 @@ #include "adav80x.h" static const struct i2c_device_id adav803_id[] = { - { "adav803" }, + { .name = "adav803" }, { } }; MODULE_DEVICE_TABLE(i2c, adav803_id); diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c index a33cb329865c..6ea7cb78cd44 100644 --- a/sound/soc/codecs/ak4104.c +++ b/sound/soc/codecs/ak4104.c @@ -5,7 +5,6 @@ * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/spi/spi.h> diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index 23e868e4e3fb..03dadba0b038 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -395,8 +395,8 @@ MODULE_DEVICE_TABLE(of, ak4118_of_match); #endif static const struct i2c_device_id ak4118_id_table[] = { - { "ak4118" }, - {} + { .name = "ak4118" }, + { } }; MODULE_DEVICE_TABLE(i2c, ak4118_id_table); diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index aadc46a47280..37555674cd15 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -430,7 +430,7 @@ static int ak4535_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ak4535_i2c_id[] = { - { "ak4535" }, + { .name = "ak4535" }, { } }; MODULE_DEVICE_TABLE(i2c, ak4535_i2c_id); diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index de9e43185555..3e0696b5abf5 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -303,7 +303,7 @@ static const struct of_device_id ak4613_of_match[] = { MODULE_DEVICE_TABLE(of, ak4613_of_match); static const struct i2c_device_id ak4613_i2c_id[] = { - { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg }, + { .name = "ak4613", .driver_data = (kernel_ulong_t)&ak4613_regmap_cfg }, { } }; MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id); @@ -748,11 +748,6 @@ static int ak4613_dai_trigger(struct snd_pcm_substream *substream, int cmd, return 0; } -/* - * Select below from Sound Card, not Auto - * SND_SOC_DAIFMT_CBC_CFC - * SND_SOC_DAIFMT_CBP_CFP - */ static const u64 ak4613_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J; diff --git a/sound/soc/codecs/ak4619.c b/sound/soc/codecs/ak4619.c index daf6e15b7077..d9c9f6b20028 100644 --- a/sound/soc/codecs/ak4619.c +++ b/sound/soc/codecs/ak4619.c @@ -778,17 +778,13 @@ static int ak4619_dai_startup(struct snd_pcm_substream *substream, } static u64 ak4619_dai_formats[] = { - /* - * Select below from Sound Card, not here - * SND_SOC_DAIFMT_CBC_CFC - * SND_SOC_DAIFMT_CBP_CFP - */ - /* First Priority */ SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J, /* Second Priority */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, }; @@ -831,7 +827,7 @@ static const struct of_device_id ak4619_of_match[] = { MODULE_DEVICE_TABLE(of, ak4619_of_match); static const struct i2c_device_id ak4619_i2c_id[] = { - { "ak4619", (kernel_ulong_t)&ak4619_regmap_cfg }, + { .name = "ak4619", .driver_data = (kernel_ulong_t)&ak4619_regmap_cfg }, { } }; MODULE_DEVICE_TABLE(i2c, ak4619_i2c_id); diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index fe035d2fc913..08ec2035b270 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -672,10 +672,10 @@ static const struct of_device_id ak4642_of_match[] = { MODULE_DEVICE_TABLE(of, ak4642_of_match); static const struct i2c_device_id ak4642_i2c_id[] = { - { "ak4642", (kernel_ulong_t)&ak4642_drvdata }, - { "ak4643", (kernel_ulong_t)&ak4643_drvdata }, - { "ak4648", (kernel_ulong_t)&ak4648_drvdata }, - {} + { .name = "ak4642", .driver_data = (kernel_ulong_t)&ak4642_drvdata }, + { .name = "ak4643", .driver_data = (kernel_ulong_t)&ak4643_drvdata }, + { .name = "ak4648", .driver_data = (kernel_ulong_t)&ak4648_drvdata }, + { } }; MODULE_DEVICE_TABLE(i2c, ak4642_i2c_id); diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index d545aa2e0a39..aac54e9d54ee 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -646,7 +646,7 @@ static int ak4671_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ak4671_i2c_id[] = { - { "ak4671" }, + { .name = "ak4671" }, { } }; MODULE_DEVICE_TABLE(i2c, ak4671_i2c_id); diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index ec229b315f9f..f5a666c49361 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -963,10 +963,10 @@ static const struct regmap_config alc5623_regmap = { }; static const struct i2c_device_id alc5623_i2c_table[] = { - {"alc5621", 0x21}, - {"alc5622", 0x22}, - {"alc5623", 0x23}, - {} + { .name = "alc5621", .driver_data = 0x21 }, + { .name = "alc5622", .driver_data = 0x22 }, + { .name = "alc5623", .driver_data = 0x23 }, + { } }; MODULE_DEVICE_TABLE(i2c, alc5623_i2c_table); diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index 72f4622204ff..2fd3dd7ef8b3 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1092,8 +1092,8 @@ static const struct regmap_config alc5632_regmap = { }; static const struct i2c_device_id alc5632_i2c_table[] = { - {"alc5632", 0x5c}, - {} + { .name = "alc5632", .driver_data = 0x5c }, + { } }; MODULE_DEVICE_TABLE(i2c, alc5632_i2c_table); diff --git a/sound/soc/codecs/audio-iio-aux.c b/sound/soc/codecs/audio-iio-aux.c index 066e401912b0..964b9a5b2990 100644 --- a/sound/soc/codecs/audio-iio-aux.c +++ b/sound/soc/codecs/audio-iio-aux.c @@ -9,7 +9,6 @@ #include <linux/cleanup.h> #include <linux/iio/consumer.h> #include <linux/minmax.h> -#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/string_helpers.h> diff --git a/sound/soc/codecs/aw87390.c b/sound/soc/codecs/aw87390.c index 37ca42a25889..020213e0ca4b 100644 --- a/sound/soc/codecs/aw87390.c +++ b/sound/soc/codecs/aw87390.c @@ -599,8 +599,8 @@ static const struct of_device_id aw87390_of_match[] = { MODULE_DEVICE_TABLE(of, aw87390_of_match); static const struct i2c_device_id aw87390_i2c_id[] = { - { AW87390_I2C_NAME }, - { AW87391_I2C_NAME }, + { .name = AW87390_I2C_NAME }, + { .name = AW87391_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw87390_i2c_id); diff --git a/sound/soc/codecs/aw88081.c b/sound/soc/codecs/aw88081.c index 8c5bb3ea0227..d5e886a8f106 100644 --- a/sound/soc/codecs/aw88081.c +++ b/sound/soc/codecs/aw88081.c @@ -1137,6 +1137,7 @@ static int aw88081_dev_init(struct aw88081 *aw88081, struct aw_container *aw_cfg static int aw88081_request_firmware_file(struct aw88081 *aw88081) { const struct firmware *cont = NULL; + struct aw_container *aw_cfg; int ret; aw88081->aw_pa->fw_status = AW88081_DEV_FW_FAILED; @@ -1148,13 +1149,16 @@ static int aw88081_request_firmware_file(struct aw88081 *aw88081) dev_dbg(aw88081->aw_pa->dev, "loaded %s - size: %zu\n", AW88081_ACF_FILE, cont ? cont->size : 0); - aw88081->aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); - if (!aw88081->aw_cfg) { + aw_cfg = devm_kzalloc(aw88081->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); + if (!aw_cfg) { release_firmware(cont); return -ENOMEM; } - aw88081->aw_cfg->len = (int)cont->size; - memcpy(aw88081->aw_cfg->data, cont->data, cont->size); + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); + + aw88081->aw_cfg = aw_cfg; + release_firmware(cont); ret = aw88395_dev_load_acf_check(aw88081->aw_pa, aw88081->aw_cfg); @@ -1240,8 +1244,8 @@ static const struct snd_soc_component_driver soc_codec_dev_aw88081 = { }; static const struct i2c_device_id aw88081_i2c_id[] = { - { AW88081_I2C_NAME, AW88081}, - { AW88083_I2C_NAME, AW88083}, + { .name = AW88081_I2C_NAME, .driver_data = AW88081 }, + { .name = AW88083_I2C_NAME, .driver_data = AW88083 }, { } }; MODULE_DEVICE_TABLE(i2c, aw88081_i2c_id); diff --git a/sound/soc/codecs/aw88166.c b/sound/soc/codecs/aw88166.c index ea277a940c44..3f15f4ac51f7 100644 --- a/sound/soc/codecs/aw88166.c +++ b/sound/soc/codecs/aw88166.c @@ -1801,7 +1801,7 @@ static int aw88166_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88166_i2c_id[] = { - { AW88166_I2C_NAME }, + { .name = AW88166_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88166_i2c_id); diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c index a6805d5405cd..549783d3e75e 100644 --- a/sound/soc/codecs/aw88261.c +++ b/sound/soc/codecs/aw88261.c @@ -10,9 +10,12 @@ #include <linux/i2c.h> #include <linux/firmware.h> +#include <linux/bitops.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <sound/soc.h> +#include <sound/pcm_params.h> +#include <sound/tlv.h> #include "aw88261.h" #include "aw88395/aw88395_data_type.h" #include "aw88395/aw88395_device.h" @@ -27,64 +30,10 @@ static const struct regmap_config aw88261_remap_config = { static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value) { - struct aw_volume_desc *vol_desc = &aw_dev->volume_desc; - unsigned int real_value, volume; - unsigned int reg_value; - - volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL); - real_value = DB_TO_REG_VAL(volume); - - regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, ®_value); - - real_value = (real_value | (reg_value & AW88261_VOL_START_MASK)); - - dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value); - - regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value); -} - -static void aw88261_dev_fade_in(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - int fade_in_vol = desc->ctl_volume; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_in_time == 0) { - aw88261_dev_set_volume(aw_dev, fade_in_vol); - return; - } - - for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) { - aw88261_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_in_time, - aw_dev->fade_in_time + 10); - } - - if (i != fade_in_vol) - aw88261_dev_set_volume(aw_dev, fade_in_vol); -} - -static void aw88261_dev_fade_out(struct aw_device *aw_dev) -{ - struct aw_volume_desc *desc = &aw_dev->volume_desc; - int fade_step = aw_dev->fade_step; - int i; - - if (fade_step == 0 || aw_dev->fade_out_time == 0) { - aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); - return; - } + unsigned int volume = min(value, (unsigned int)AW88261_MUTE_VOL); - for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) { - aw88261_dev_set_volume(aw_dev, i); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } - - if (i != AW88261_MUTE_VOL) { - aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); - usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10); - } + regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL2_REG, + ~AW88261_VOL_MASK, DB_TO_REG_VAL(volume)); } static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag) @@ -120,13 +69,13 @@ static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd) static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute) { if (is_mute) { - aw88261_dev_fade_out(aw_dev); + aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL); regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE); } else { regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE); - aw88261_dev_fade_in(aw_dev); + aw88261_dev_set_volume(aw_dev, aw_dev->volume_desc.ctl_volume); } } @@ -151,21 +100,21 @@ static int aw88261_dev_get_iis_status(struct aw_device *aw_dev) if (ret) return ret; if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) { - dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); + dev_dbg(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val); return -EINVAL; } return ret; } -static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev) +static int aw88261_dev_check_pll(struct aw_device *aw_dev) { int ret, i; for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { ret = aw88261_dev_get_iis_status(aw_dev); if (ret) { - dev_err(aw_dev->dev, "mode1 iis signal check error"); + dev_dbg(aw_dev->dev, "mode1 iis signal check error"); usleep_range(AW88261_2000_US, AW88261_2000_US + 10); } else { return ret; @@ -175,71 +124,74 @@ static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev) return -EPERM; } -static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev) +static int aw88261_dev_configure_syspll(struct aw88261 *aw88261) { - unsigned int reg_val; - int ret, i; + struct aw_device *aw_dev = aw88261->aw_pa; + int ret; - ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, ®_val); + /* Configure TDM slots (I2S is represented as no slots) */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG, + ~AW88261_SLOT_NUM_MASK, aw88261->slot_num_value); if (ret) return ret; - reg_val &= (~AW88261_CCO_MUX_MASK); - if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) { - dev_dbg(aw_dev->dev, "CCO_MUX is already divider"); - return -EPERM; - } + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG, + ~AW88261_I2S_TX_SLOTVLD_MASK, + aw88261->tx_slotvld_mask); + if (ret) + return ret; - /* change mode2 */ - ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, - ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE); + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG, + ~AW88261_I2S_RXL_SLOTVLD_MASK, + aw88261->rxl_slotvld_mask); if (ret) return ret; - for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { - ret = aw88261_dev_get_iis_status(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 iis signal check error"); - usleep_range(AW88261_2000_US, AW88261_2000_US + 10); - } else { - break; - } - } + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL2_REG, + ~AW88261_I2S_RXR_SLOTVLD_MASK, + aw88261->rxr_slotvld_mask); + if (ret) + return ret; - /* change mode1 */ + /* PLL divider must be used for 8/16/32 kHz modes */ ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG, - ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE); - if (ret == 0) { - usleep_range(AW88261_2000_US, AW88261_2000_US + 10); - for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) { - ret = aw88261_dev_check_mode1_pll(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error"); - usleep_range(AW88261_2000_US, AW88261_2000_US + 10); - } else { - break; - } - } - } + ~AW88261_CCO_MUX_MASK, aw88261->cco_mux_value); + if (ret) + return ret; - return ret; -} + /* The word clock (WCK) defines the beginning of a frame */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG, + ~AW88261_I2SSR_MASK, aw88261->sr_value); + if (ret) + return ret; -static int aw88261_dev_check_syspll(struct aw_device *aw_dev) -{ - int ret; + /* The bit clock (BCK) defines the length of a frame */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG, + ~AW88261_I2SBCK_MASK, + (aw88261->tdm_bck_value != AW88261_TDM_BCK_UNSET) + ? aw88261->tdm_bck_value : aw88261->bck_value); + if (ret) + return ret; - ret = aw88261_dev_check_mode1_pll(aw_dev); - if (ret) { - dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check"); - ret = aw88261_dev_check_mode2_pll(aw_dev); - if (ret) { - dev_err(aw_dev->dev, "mode2 check iis failed"); - return ret; - } - } + /* The logical frame size is the width of data for 1 slot */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG, + ~AW88261_I2SFS_MASK, aw88261->fs_value); + if (ret) + return ret; - return ret; + /* The I2S interface mode (Philips standard, LSB/MSB justified) */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_I2SCTRL1_REG, + ~AW88261_I2SMD_MASK, aw88261->md_value); + if (ret) + return ret; + + /* The polarity of the bit clock (BCK) */ + ret = regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG, + ~AW88261_BCKINV_MASK, aw88261->bck_inv_value); + if (ret) + return ret; + + return aw88261_dev_check_pll(aw_dev); } static int aw88261_dev_check_sysst(struct aw_device *aw_dev) @@ -256,7 +208,7 @@ static int aw88261_dev_check_sysst(struct aw_device *aw_dev) check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK) & AW88261_BIT_SYSST_CHECK; if (check_val != AW88261_BIT_SYSST_CHECK) { - dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", + dev_dbg(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x", reg_val, AW88261_BIT_SYSST_CHECK); usleep_range(AW88261_2000_US, AW88261_2000_US + 10); } else { @@ -284,22 +236,22 @@ static void aw88261_reg_force_set(struct aw88261 *aw88261) if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) { /* set FORCE_PWM */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG, - AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); + ~AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE); /* set BOOST_OS_WIDTH */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG, - AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); + ~AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE); /* set BURST_LOOPR */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG, - AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); + ~AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE); /* set RSQN_DLY */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG, - AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); + ~AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE); /* set BURST_SSMODE */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG, - AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); + ~AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE); /* set BST_BURST */ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG, - AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); + ~AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE); } else { dev_dbg(aw88261->aw_pa->dev, "needn't set reg value"); } @@ -550,7 +502,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261) int ret; if (aw_dev->status == AW88261_DEV_PW_ON) { - dev_info(aw_dev->dev, "already power on"); + dev_dbg(aw_dev->dev, "already power on"); return 0; } @@ -558,9 +510,9 @@ static int aw88261_dev_start(struct aw88261 *aw88261) aw88261_dev_pwd(aw_dev, false); usleep_range(AW88261_2000_US, AW88261_2000_US + 10); - ret = aw88261_dev_check_syspll(aw_dev); + ret = aw88261_dev_configure_syspll(aw88261); if (ret) { - dev_err(aw_dev->dev, "pll check failed cannot start"); + dev_dbg(aw_dev->dev, "pll check failed"); goto pll_check_fail; } @@ -571,7 +523,7 @@ static int aw88261_dev_start(struct aw88261 *aw88261) /* check i2s status */ ret = aw88261_dev_check_sysst(aw_dev); if (ret) { - dev_err(aw_dev->dev, "sysst check failed"); + dev_dbg(aw_dev->dev, "sysst check failed"); goto sysst_check_fail; } @@ -672,31 +624,25 @@ static void aw88261_start_pa(struct aw88261 *aw88261) for (i = 0; i < AW88261_START_RETRIES; i++) { ret = aw88261_reg_update(aw88261, aw88261->phase_sync); if (ret) { - dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i); + dev_dbg(aw88261->aw_pa->dev, + "aw88261_reg_update failed, cnt:%d, ret:%d\n", i, ret); continue; } ret = aw88261_dev_start(aw88261); if (ret) { - dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i); + dev_dbg(aw88261->aw_pa->dev, + "aw88261_dev_start failed, cnt:%d, ret:%d\n", i, ret); continue; } else { - dev_info(aw88261->aw_pa->dev, "start success\n"); + dev_dbg(aw88261->aw_pa->dev, "start success\n"); break; } } + if (ret != 0) + dev_err(aw88261->aw_pa->dev, "start failure (%d)\n", ret); } -static void aw88261_startup_work(struct work_struct *work) -{ - struct aw88261 *aw88261 = - container_of(work, struct aw88261, start_work.work); - - mutex_lock(&aw88261->lock); - aw88261_start_pa(aw88261); - mutex_unlock(&aw88261->lock); -} - -static void aw88261_start(struct aw88261 *aw88261, bool sync_start) +static void aw88261_start(struct aw88261 *aw88261) { if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK) return; @@ -704,104 +650,248 @@ static void aw88261_start(struct aw88261 *aw88261, bool sync_start) if (aw88261->aw_pa->status == AW88261_DEV_PW_ON) return; - if (sync_start == AW88261_SYNC_START) - aw88261_start_pa(aw88261); - else - queue_delayed_work(system_dfl_wq, - &aw88261->start_work, - AW88261_START_WORK_DELAY_MS); + aw88261_start_pa(aw88261); } -static struct snd_soc_dai_driver aw88261_dai[] = { - { - .name = "aw88261-aif", - .id = 1, - .playback = { - .stream_name = "Speaker_Playback", - .channels_min = 1, - .channels_max = 2, - .rates = AW88261_RATES, - .formats = AW88261_FORMATS, - }, - .capture = { - .stream_name = "Speaker_Capture", - .channels_min = 1, - .channels_max = 2, - .rates = AW88261_RATES, - .formats = AW88261_FORMATS, - }, - }, -}; - -static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int aw88261_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = dai->component; struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - struct aw_device *aw_dev = aw88261->aw_pa; - ucontrol->value.integer.value[0] = aw_dev->fade_in_time; + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE; + break; + case SND_SOC_DAIFMT_IB_NF: + aw88261->bck_inv_value = AW88261_BCKINV_INVERTED_VALUE; + break; + default: + dev_err(aw88261->aw_pa->dev, "unsupported invert mode 0x%x\n", + fmt & SND_SOC_DAIFMT_INV_MASK); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_DSP_A: + aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE; + break; + case SND_SOC_DAIFMT_MSB: + case SND_SOC_DAIFMT_DSP_B: + aw88261->md_value = AW88261_I2SMD_MSB_JUSTIFIED_VALUE; + break; + case SND_SOC_DAIFMT_LSB: + aw88261->md_value = AW88261_I2SMD_LSB_JUSTIFIED_VALUE; + break; + default: + dev_err(aw88261->aw_pa->dev, "unsupported DAI format 0x%x\n", + fmt & SND_SOC_DAIFMT_FORMAT_MASK); + return -EINVAL; + } return 0; } -static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int aw88261_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = dai->component; struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct aw_device *aw_dev = aw88261->aw_pa; - int time; - time = ucontrol->value.integer.value[0]; + if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) + return 0; - if (time < mc->min || time > mc->max) + aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE; + switch (params_rate(params)) { + case 8000: + aw88261->sr_value = AW88261_I2SSR_8KHZ_VALUE; + aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE; + break; + case 11025: + aw88261->sr_value = AW88261_I2SSR_11P025KHZ_VALUE; + break; + case 12000: + aw88261->sr_value = AW88261_I2SSR_12KHZ_VALUE; + break; + case 16000: + aw88261->sr_value = AW88261_I2SSR_16KHZ_VALUE; + aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE; + break; + case 22050: + aw88261->sr_value = AW88261_I2SSR_22P05KHZ_VALUE; + break; + case 24000: + aw88261->sr_value = AW88261_I2SSR_24KHZ_VALUE; + break; + case 32000: + aw88261->sr_value = AW88261_I2SSR_32KHZ_VALUE; + aw88261->cco_mux_value = AW88261_CCO_MUX_DIVIDED_VALUE; + break; + case 44100: + aw88261->sr_value = AW88261_I2SSR_44P1KHZ_VALUE; + break; + case 48000: + aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE; + break; + case 96000: + aw88261->sr_value = AW88261_I2SSR_96KHZ_VALUE; + break; + case 192000: + aw88261->sr_value = AW88261_I2SSR_192KHZ_VALUE; + break; + default: + dev_err(aw88261->aw_pa->dev, "unsupported sample rate %d\n", + params_rate(params)); return -EINVAL; + } - if (time != aw_dev->fade_in_time) { - aw_dev->fade_in_time = time; - return 1; + switch (params_width(params)) { + case 16: + aw88261->fs_value = AW88261_I2SFS_16_BITS_VALUE; + break; + case 20: + aw88261->fs_value = AW88261_I2SFS_20_BITS_VALUE; + break; + case 24: + aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE; + break; + case 32: + aw88261->fs_value = AW88261_I2SFS_32_BITS_VALUE; + break; + default: + dev_err(aw88261->aw_pa->dev, "unsupported bit width %d\n", + params_width(params)); + return -EINVAL; + } + + switch (params_physical_width(params)) { + case 16: + aw88261->bck_value = AW88261_I2SBCK_32FS_VALUE; + break; + case 24: + aw88261->bck_value = AW88261_I2SBCK_48FS_VALUE; + break; + case 32: + aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE; + break; + default: + dev_err(aw88261->aw_pa->dev, "unsupported physical bit width %d\n", + params_physical_width(params)); + return -EINVAL; } return 0; } -static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) +static int aw88261_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width) { - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct snd_soc_component *component = dai->component; struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - struct aw_device *aw_dev = aw88261->aw_pa; + int chan; + + switch (slots) { + case 0: + /* Just reset everything TDM related to I2S values */ + aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE; + aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET; + aw88261->tx_slotvld_mask = 0 << AW88261_I2S_TX_SLOTVLD_START_BIT; + aw88261->rxl_slotvld_mask = 0 << AW88261_I2S_RXL_SLOTVLD_START_BIT; + aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT; + return 0; + case 1: + aw88261->slot_num_value = AW88261_SLOT_NUM_TDM1S_VALUE; + break; + case 2: + aw88261->slot_num_value = AW88261_SLOT_NUM_TDM2S_VALUE; + break; + case 4: + aw88261->slot_num_value = AW88261_SLOT_NUM_TDM4S_VALUE; + break; + case 6: + aw88261->slot_num_value = AW88261_SLOT_NUM_TDM6S_VALUE; + break; + case 8: + aw88261->slot_num_value = AW88261_SLOT_NUM_TDM8S_VALUE; + break; + case 16: + aw88261->slot_num_value = AW88261_SLOT_NUM_TDM16S_VALUE; + break; + default: + dev_err(aw88261->aw_pa->dev, "unsupported slot count %d\n", slots); + return -EINVAL; + } - ucontrol->value.integer.value[0] = aw_dev->fade_out_time; + switch (slot_width) { + case 16: + aw88261->tdm_bck_value = AW88261_I2SBCK_32FS_VALUE; + break; + case 20: + case 24: + aw88261->tdm_bck_value = AW88261_I2SBCK_48FS_VALUE; + break; + case 32: + aw88261->tdm_bck_value = AW88261_I2SBCK_64FS_VALUE; + break; + default: + dev_err(aw88261->aw_pa->dev, "unsupported slot width %d\n", + slot_width); + return -EINVAL; + } - return 0; -} + if (tx_mask != 0) { + if ((chan = __ffs(tx_mask)) > 16) + return -EINVAL; -static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); - struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - struct aw_device *aw_dev = aw88261->aw_pa; - int time; + aw88261->tx_slotvld_mask = chan << AW88261_I2S_TX_SLOTVLD_START_BIT; + } - time = ucontrol->value.integer.value[0]; - if (time < mc->min || time > mc->max) - return -EINVAL; + if (rx_mask != 0) { + if ((chan = __ffs(rx_mask)) > 16) + return -EINVAL; - if (time != aw_dev->fade_out_time) { - aw_dev->fade_out_time = time; - return 1; + aw88261->rxl_slotvld_mask = chan << AW88261_I2S_RXL_SLOTVLD_START_BIT; + } + + if ((rx_mask & ~BIT(chan)) != 0) { + if ((chan = __ffs(rx_mask & ~BIT(chan))) > 16) + return -EINVAL; + + aw88261->rxr_slotvld_mask = chan << AW88261_I2S_RXR_SLOTVLD_START_BIT; } return 0; } +static const struct snd_soc_dai_ops aw88261_dai_ops = { + .set_fmt = aw88261_set_fmt, + .hw_params = aw88261_hw_params, + .set_tdm_slot = aw88261_set_tdm_slot, +}; + +static struct snd_soc_dai_driver aw88261_dai[] = { + { + .name = "aw88261-aif", + .id = 1, + .playback = { + .stream_name = "Speaker_Playback", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + .capture = { + .stream_name = "Speaker_Capture", + .channels_min = 1, + .channels_max = 2, + .rates = AW88261_RATES, + .formats = AW88261_FORMATS, + }, + .ops = &aw88261_dai_ops, + }, +}; + static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index) { /* check the index whether is valid */ @@ -880,7 +970,7 @@ static int aw88261_profile_set(struct snd_kcontrol *kcontrol, if (aw88261->aw_pa->status) { aw88261_dev_stop(aw88261->aw_pa); - aw88261_start(aw88261, AW88261_SYNC_START); + aw88261_start(aw88261); } mutex_unlock(&aw88261->lock); @@ -895,7 +985,8 @@ static int aw88261_volume_get(struct snd_kcontrol *kcontrol, struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; - ucontrol->value.integer.value[0] = vol_desc->ctl_volume; + ucontrol->value.integer.value[0] = + (AW88261_MUTE_VOL - vol_desc->ctl_volume) / 2; return 0; } @@ -908,13 +999,13 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol, struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc; struct soc_mixer_control *mc = (struct soc_mixer_control *)kcontrol->private_value; - int value; - - value = ucontrol->value.integer.value[0]; + int value = ucontrol->value.integer.value[0]; if (value < mc->min || value > mc->max) return -EINVAL; + value = AW88261_MUTE_VOL - (value * 2); + if (vol_desc->ctl_volume != value) { vol_desc->ctl_volume = value; aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume); @@ -925,48 +1016,18 @@ static int aw88261_volume_set(struct snd_kcontrol *kcontrol, return 0; } -static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); - struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); - - ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step; - - return 0; -} - -static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *ucontrol) -{ - struct snd_soc_component *codec = snd_kcontrol_chip(kcontrol); - struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec); - struct soc_mixer_control *mc = - (struct soc_mixer_control *)kcontrol->private_value; - int value; - - value = ucontrol->value.integer.value[0]; - if (value < mc->min || value > mc->max) - return -EINVAL; - - if (aw88261->aw_pa->fade_step != value) { - aw88261->aw_pa->fade_step = value; - return 1; - } - - return 0; -} +/* + * The field contains 4 bits in units of 6dB + 6 bits in units of 0.125dB + * which is too precise for TLV (!) so we have to multiply the scale by 2. + * + * The range is clamped at -90dB to prevent overflowing the 4-bit part. + */ +static const DECLARE_TLV_DB_SCALE(volume_tlv, -9000, 25, 0); static const struct snd_kcontrol_new aw88261_controls[] = { - SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG, - 6, AW88261_MUTE_VOL, 0, aw88261_volume_get, - aw88261_volume_set), - SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0, - aw88261_get_fade_step, aw88261_set_fade_step), - SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, - aw88261_get_fade_in_time, aw88261_set_fade_in_time), - SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN, - aw88261_get_fade_out_time, aw88261_set_fade_out_time), + SOC_SINGLE_EXT_TLV("PCM Playback Volume", AW88261_SYSCTRL2_REG, + 6, AW88261_CTL_MAX_VOL, 1, + aw88261_volume_get, aw88261_volume_set, volume_tlv), AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info, aw88261_profile_get, aw88261_profile_set), }; @@ -980,7 +1041,7 @@ static int aw88261_playback_event(struct snd_soc_dapm_widget *w, mutex_lock(&aw88261->lock); switch (event) { case SND_SOC_DAPM_PRE_PMU: - aw88261_start(aw88261, AW88261_ASYNC_START); + aw88261_start(aw88261); break; case SND_SOC_DAPM_POST_PMD: aw88261_dev_stop(aw88261->aw_pa); @@ -1057,8 +1118,6 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg if (ret) return ret; - aw_dev->fade_in_time = AW88261_500_US; - aw_dev->fade_out_time = AW88261_500_US; aw_dev->prof_cur = AW88261_INIT_PROFILE; aw_dev->prof_index = AW88261_INIT_PROFILE; @@ -1094,6 +1153,7 @@ static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg static int aw88261_request_firmware_file(struct aw88261 *aw88261) { const struct firmware *cont = NULL; + struct aw_container *aw_cfg; const char *fw_name; int ret; @@ -1111,15 +1171,17 @@ static int aw88261_request_firmware_file(struct aw88261 *aw88261) dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n", fw_name, cont ? cont->size : 0); - aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); - if (!aw88261->aw_cfg) { + aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); + if (!aw_cfg) { release_firmware(cont); return -ENOMEM; } - aw88261->aw_cfg->len = (int)cont->size; - memcpy(aw88261->aw_cfg->data, cont->data, cont->size); + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); release_firmware(cont); + aw88261->aw_cfg = aw_cfg; + ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg); if (ret) { dev_err(aw88261->aw_pa->dev, "load [%s] failed !", fw_name); @@ -1142,8 +1204,6 @@ static int aw88261_codec_probe(struct snd_soc_component *component) struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component); int ret; - INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work); - ret = aw88261_request_firmware_file(aw88261); if (ret) return dev_err_probe(aw88261->aw_pa->dev, ret, @@ -1167,16 +1227,8 @@ static int aw88261_codec_probe(struct snd_soc_component *component) return ret; } -static void aw88261_codec_remove(struct snd_soc_component *aw_codec) -{ - struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec); - - cancel_delayed_work_sync(&aw88261->start_work); -} - static const struct snd_soc_component_driver soc_codec_dev_aw88261 = { .probe = aw88261_codec_probe, - .remove = aw88261_codec_remove, }; static void aw88261_parse_channel_dt(struct aw88261 *aw88261) @@ -1208,7 +1260,7 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct return ret; } if (chip_id != AW88261_CHIP_ID) { - dev_err(&i2c->dev, "unsupported device"); + dev_err(&i2c->dev, "unsupported device id = %x", chip_id); return -ENXIO; } @@ -1229,8 +1281,7 @@ static int aw88261_init(struct aw88261 *aw88261, struct i2c_client *i2c, struct aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID; aw_dev->channel = 0; aw_dev->fw_status = AW88261_DEV_FW_FAILED; - aw_dev->fade_step = AW88261_VOLUME_STEP_DB; - aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE; + aw_dev->volume_desc.ctl_volume = AW88261_CTL_DEFAULT_VOL; aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL; aw88261_parse_channel_dt(aw88261); @@ -1249,6 +1300,17 @@ static int aw88261_i2c_probe(struct i2c_client *i2c) if (!aw88261) return -ENOMEM; + /* set defaults */ + aw88261->slot_num_value = AW88261_SLOT_NUM_I2S_MODE_VALUE; + aw88261->sr_value = AW88261_I2SSR_48KHZ_VALUE; + aw88261->cco_mux_value = AW88261_CCO_MUX_BYPASS_VALUE; + aw88261->fs_value = AW88261_I2SFS_24_BITS_VALUE; + aw88261->bck_value = AW88261_I2SBCK_64FS_VALUE; + aw88261->bck_inv_value = AW88261_BCKINV_NOT_INVERT_VALUE; + aw88261->tdm_bck_value = AW88261_TDM_BCK_UNSET; + aw88261->md_value = AW88261_I2SMD_PHILIPS_STANDARD_VALUE; + aw88261->rxr_slotvld_mask = 1 << AW88261_I2S_RXR_SLOTVLD_START_BIT; + mutex_init(&aw88261->lock); i2c_set_clientdata(i2c, aw88261); @@ -1274,7 +1336,7 @@ static int aw88261_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88261_i2c_id[] = { - { "aw88261" }, + { .name = "aw88261" }, { } }; MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id); diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h index 1fee589608d6..270ccf375f36 100644 --- a/sound/soc/codecs/aw88261.h +++ b/sound/soc/codecs/aw88261.h @@ -116,6 +116,19 @@ #define AW88261_VCALK_SHIFT (0) #define AW88261_VCALKL_SHIFT (0) +#define AW88261_BCKINV_START_BIT (4) +#define AW88261_BCKINV_BITS_LEN (1) +#define AW88261_BCKINV_MASK \ + (~(((1<<AW88261_BCKINV_BITS_LEN)-1) << AW88261_BCKINV_START_BIT)) + +#define AW88261_BCKINV_NOT_INVERT (0) +#define AW88261_BCKINV_NOT_INVERT_VALUE \ + (AW88261_BCKINV_NOT_INVERT << AW88261_BCKINV_START_BIT) + +#define AW88261_BCKINV_INVERTED (1) +#define AW88261_BCKINV_INVERTED_VALUE \ + (AW88261_BCKINV_INVERTED << AW88261_BCKINV_START_BIT) + #define AW88261_AMPPD_START_BIT (1) #define AW88261_AMPPD_BITS_LEN (1) #define AW88261_AMPPD_MASK \ @@ -249,7 +262,8 @@ #define AW88261_VOL_MASK \ (~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT)) -#define AW88261_VOL_DEFAULT_VALUE (0) +#define AW88261_CTL_MAX_VOL (AW88261_MUTE_VOL / 2) +#define AW88261_CTL_DEFAULT_VOL (AW88261_CTL_MAX_VOL / 2) #define AW88261_I2STXEN_START_BIT (6) #define AW88261_I2STXEN_BITS_LEN (1) @@ -264,7 +278,148 @@ #define AW88261_I2STXEN_ENABLE_VALUE \ (AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT) -#define AW88261_CCO_MUX_START_BIT (14) +#define AW88261_I2SMD_START_BIT (8) +#define AW88261_I2SMD_BITS_LEN (2) +#define AW88261_I2SMD_MASK \ + (~(((1<<AW88261_I2SMD_BITS_LEN)-1) << AW88261_I2SMD_START_BIT)) + +#define AW88261_I2SMD_PHILIPS_STANDARD (0) +#define AW88261_I2SMD_PHILIPS_STANDARD_VALUE \ + (AW88261_I2SMD_PHILIPS_STANDARD << AW88261_I2SMD_START_BIT) + +#define AW88261_I2SMD_MSB_JUSTIFIED (1) +#define AW88261_I2SMD_MSB_JUSTIFIED_VALUE \ + (AW88261_I2SMD_MSB_JUSTIFIED << AW88261_I2SMD_START_BIT) + +#define AW88261_I2SMD_LSB_JUSTIFIED (2) +#define AW88261_I2SMD_LSB_JUSTIFIED_VALUE \ + (AW88261_I2SMD_LSB_JUSTIFIED << AW88261_I2SMD_START_BIT) + +#define AW88261_I2SFS_START_BIT (6) +#define AW88261_I2SFS_BITS_LEN (2) +#define AW88261_I2SFS_MASK \ + (~(((1<<AW88261_I2SFS_BITS_LEN)-1)<<AW88261_I2SFS_START_BIT)) + +#define AW88261_I2SFS_16_BITS (0) +#define AW88261_I2SFS_16_BITS_VALUE \ + (AW88261_I2SFS_16_BITS << AW88261_I2SFS_START_BIT) +#define AW88261_I2SFS_20_BITS (1) +#define AW88261_I2SFS_20_BITS_VALUE \ + (AW88261_I2SFS_20_BITS << AW88261_I2SFS_START_BIT) +#define AW88261_I2SFS_24_BITS (2) +#define AW88261_I2SFS_24_BITS_VALUE \ + (AW88261_I2SFS_24_BITS << AW88261_I2SFS_START_BIT) +#define AW88261_I2SFS_32_BITS (3) +#define AW88261_I2SFS_32_BITS_VALUE \ + (AW88261_I2SFS_32_BITS << AW88261_I2SFS_START_BIT) + +#define AW88261_I2SBCK_START_BIT (4) +#define AW88261_I2SBCK_BITS_LEN (2) +#define AW88261_I2SBCK_MASK \ + (~(((1<<AW88261_I2SBCK_BITS_LEN)-1) << AW88261_I2SBCK_START_BIT)) + +#define AW88261_I2SBCK_32FS (0) +#define AW88261_I2SBCK_32FS_VALUE \ + (AW88261_I2SBCK_32FS << AW88261_I2SBCK_START_BIT) + +#define AW88261_I2SBCK_48FS (1) +#define AW88261_I2SBCK_48FS_VALUE \ + (AW88261_I2SBCK_48FS << AW88261_I2SBCK_START_BIT) + +#define AW88261_I2SBCK_64FS (2) +#define AW88261_I2SBCK_64FS_VALUE \ + (AW88261_I2SBCK_64FS << AW88261_I2SBCK_START_BIT) + +#define AW88261_TDM_BCK_UNSET UINT_MAX + +#define AW88261_I2SSR_START_BIT (0) +#define AW88261_I2SSR_BITS_LEN (4) +#define AW88261_I2SSR_MASK \ + (~(((1<<AW88261_I2SSR_BITS_LEN)-1) << AW88261_I2SSR_START_BIT)) + +#define AW88261_I2SSR_8KHZ (0) +#define AW88261_I2SSR_8KHZ_VALUE \ + (AW88261_I2SSR_8KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_11P025KHZ (1) +#define AW88261_I2SSR_11P025KHZ_VALUE \ + (AW88261_I2SSR_11P025KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_12KHZ (2) +#define AW88261_I2SSR_12KHZ_VALUE \ + (AW88261_I2SSR_12KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_16KHZ (3) +#define AW88261_I2SSR_16KHZ_VALUE \ + (AW88261_I2SSR_16KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_22P05KHZ (4) +#define AW88261_I2SSR_22P05KHZ_VALUE \ + (AW88261_I2SSR_22P05KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_24KHZ (5) +#define AW88261_I2SSR_24KHZ_VALUE \ + (AW88261_I2SSR_24KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_32KHZ (6) +#define AW88261_I2SSR_32KHZ_VALUE \ + (AW88261_I2SSR_32KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_44P1KHZ (7) +#define AW88261_I2SSR_44P1KHZ_VALUE \ + (AW88261_I2SSR_44P1KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_48KHZ (8) +#define AW88261_I2SSR_48KHZ_VALUE \ + (AW88261_I2SSR_48KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_96KHZ (9) +#define AW88261_I2SSR_96KHZ_VALUE \ + (AW88261_I2SSR_96KHZ << AW88261_I2SSR_START_BIT) +#define AW88261_I2SSR_192KHZ (10) +#define AW88261_I2SSR_192KHZ_VALUE \ + (AW88261_I2SSR_192KHZ << AW88261_I2SSR_START_BIT) + +#define AW88261_SLOT_NUM_START_BIT (12) +#define AW88261_SLOT_NUM_BITS_LEN (3) +#define AW88261_SLOT_NUM_MASK \ + (~(((1<<AW88261_SLOT_NUM_BITS_LEN)-1) << AW88261_SLOT_NUM_START_BIT)) + +#define AW88261_SLOT_NUM_I2S_MODE (0) +#define AW88261_SLOT_NUM_I2S_MODE_VALUE \ + (AW88261_SLOT_NUM_I2S_MODE << AW88261_SLOT_NUM_START_BIT) + +#define AW88261_SLOT_NUM_TDM1S (1) +#define AW88261_SLOT_NUM_TDM1S_VALUE \ + (AW88261_SLOT_NUM_TDM1S << AW88261_SLOT_NUM_START_BIT) + +#define AW88261_SLOT_NUM_TDM2S (2) +#define AW88261_SLOT_NUM_TDM2S_VALUE \ + (AW88261_SLOT_NUM_TDM2S << AW88261_SLOT_NUM_START_BIT) + +#define AW88261_SLOT_NUM_TDM4S (3) +#define AW88261_SLOT_NUM_TDM4S_VALUE \ + (AW88261_SLOT_NUM_TDM4S << AW88261_SLOT_NUM_START_BIT) + +#define AW88261_SLOT_NUM_TDM6S (4) +#define AW88261_SLOT_NUM_TDM6S_VALUE \ + (AW88261_SLOT_NUM_TDM6S << AW88261_SLOT_NUM_START_BIT) + +#define AW88261_SLOT_NUM_TDM8S (5) +#define AW88261_SLOT_NUM_TDM8S_VALUE \ + (AW88261_SLOT_NUM_TDM8S << AW88261_SLOT_NUM_START_BIT) + +#define AW88261_SLOT_NUM_TDM16S (6) +#define AW88261_SLOT_NUM_TDM16S_VALUE \ + (AW88261_SLOT_NUM_TDM16S << AW88261_SLOT_NUM_START_BIT) + +#define AW88261_I2S_TX_SLOTVLD_START_BIT (8) +#define AW88261_I2S_TX_SLOTVLD_BITS_LEN (4) +#define AW88261_I2S_TX_SLOTVLD_MASK \ + (~(((1<<AW88261_I2S_TX_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_TX_SLOTVLD_START_BIT)) + +#define AW88261_I2S_RXR_SLOTVLD_START_BIT (4) +#define AW88261_I2S_RXR_SLOTVLD_BITS_LEN (4) +#define AW88261_I2S_RXR_SLOTVLD_MASK \ + (~(((1<<AW88261_I2S_RXR_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXR_SLOTVLD_START_BIT)) + +#define AW88261_I2S_RXL_SLOTVLD_START_BIT (0) +#define AW88261_I2S_RXL_SLOTVLD_BITS_LEN (4) +#define AW88261_I2S_RXL_SLOTVLD_MASK \ + (~(((1<<AW88261_I2S_RXL_SLOTVLD_BITS_LEN)-1) << AW88261_I2S_RXL_SLOTVLD_START_BIT)) + +#define AW88261_CCO_MUX_START_BIT (6) #define AW88261_CCO_MUX_BITS_LEN (1) #define AW88261_CCO_MUX_MASK \ (~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT)) @@ -370,21 +525,20 @@ #define AW88261_START_RETRIES (5) #define AW88261_START_WORK_DELAY_MS (0) +/* NOTE: 192000 has a reg value donwstream but not listed in datasheet */ #define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \ + SNDRV_PCM_RATE_12000 | \ + SNDRV_PCM_RATE_24000 | \ SNDRV_PCM_RATE_96000) #define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ SNDRV_PCM_FMTBIT_S24_LE | \ SNDRV_PCM_FMTBIT_S32_LE) -#define FADE_TIME_MAX 100000 -#define FADE_TIME_MIN 0 - #define AW88261_DEV_DEFAULT_CH (0) #define AW88261_ACF_FILE "aw88261_acf.bin" #define AW88261_DEV_SYSST_CHECK_MAX (10) #define AW88261_SOFT_RESET_VALUE (0x55aa) #define AW88261_REG_TO_DB (0x3f) -#define AW88261_VOL_START_MASK (0xfc00) #define AW88261_INIT_PROFILE (0) #define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \ @@ -403,11 +557,6 @@ .put = profile_set, \ } -enum { - AW88261_SYNC_START = 0, - AW88261_ASYNC_START, -}; - enum aw88261_id { AW88261_CHIP_ID = 0x2113, }; @@ -442,7 +591,6 @@ struct aw88261 { struct aw_device *aw_pa; struct mutex lock; struct gpio_desc *reset_gpio; - struct delayed_work start_work; struct regmap *regmap; struct aw_container *aw_cfg; @@ -451,6 +599,19 @@ struct aw88261 { unsigned int mute_st; unsigned int amppd_st; + unsigned int sr_value; + unsigned int cco_mux_value; + unsigned int fs_value; + unsigned int bck_value; + unsigned int bck_inv_value; + unsigned int tdm_bck_value; + unsigned int md_value; + + unsigned int slot_num_value; + unsigned int tx_slotvld_mask; + unsigned int rxl_slotvld_mask; + unsigned int rxr_slotvld_mask; + bool phase_sync; }; diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index dd09bac652f7..ee0e8bd8c54c 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -462,6 +462,7 @@ static void aw88395_hw_reset(struct aw88395 *aw88395) static int aw88395_request_firmware_file(struct aw88395 *aw88395) { const struct firmware *cont = NULL; + struct aw_container *aw_cfg; int ret; aw88395->aw_pa->fw_status = AW88395_DEV_FW_FAILED; @@ -475,15 +476,17 @@ static int aw88395_request_firmware_file(struct aw88395 *aw88395) dev_info(aw88395->aw_pa->dev, "loaded %s - size: %zu\n", AW88395_ACF_FILE, cont ? cont->size : 0); - aw88395->aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL); - if (!aw88395->aw_cfg) { + aw_cfg = devm_kzalloc(aw88395->aw_pa->dev, struct_size(aw_cfg, data, cont->size), GFP_KERNEL); + if (!aw_cfg) { release_firmware(cont); return -ENOMEM; } - aw88395->aw_cfg->len = (int)cont->size; - memcpy(aw88395->aw_cfg->data, cont->data, cont->size); + aw_cfg->len = (int)cont->size; + memcpy(aw_cfg->data, cont->data, cont->size); release_firmware(cont); + aw88395->aw_cfg = aw_cfg; + ret = aw88395_dev_load_acf_check(aw88395->aw_pa, aw88395->aw_cfg); if (ret < 0) { dev_err(aw88395->aw_pa->dev, "Load [%s] failed ....!", AW88395_ACF_FILE); @@ -557,7 +560,7 @@ static int aw88395_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88395_i2c_id[] = { - { AW88395_I2C_NAME }, + { .name = AW88395_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88395_i2c_id); diff --git a/sound/soc/codecs/aw88395/aw88395_device.h b/sound/soc/codecs/aw88395/aw88395_device.h index 3626f222899d..7b74eeb84c43 100644 --- a/sound/soc/codecs/aw88395/aw88395_device.h +++ b/sound/soc/codecs/aw88395/aw88395_device.h @@ -152,7 +152,7 @@ struct aw_cali_desc { struct aw_container { int len; - u8 data[]; + u8 data[] __counted_by(len); }; struct aw_device { diff --git a/sound/soc/codecs/aw88399.c b/sound/soc/codecs/aw88399.c index b588c27909b5..b2ec3503f7e2 100644 --- a/sound/soc/codecs/aw88399.c +++ b/sound/soc/codecs/aw88399.c @@ -2146,7 +2146,7 @@ static int aw88399_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id aw88399_i2c_id[] = { - { AW88399_I2C_NAME }, + { .name = AW88399_I2C_NAME }, { } }; MODULE_DEVICE_TABLE(i2c, aw88399_i2c_id); diff --git a/sound/soc/codecs/cs-amp-lib.c b/sound/soc/codecs/cs-amp-lib.c index fb5b950e584c..371e99205b58 100644 --- a/sound/soc/codecs/cs-amp-lib.c +++ b/sound/soc/codecs/cs-amp-lib.c @@ -748,10 +748,7 @@ static const char *cs_amp_devm_get_dell_ssidex(struct device *dev, char *ssidex_buf __free(kfree) = cs_amp_alloc_get_efi_variable(DELL_SSIDEXV2_EFI_NAME, &DELL_SSIDEXV2_EFI_GUID, NULL); - ret = PTR_ERR_OR_ZERO(ssidex_buf); - if (ret == -ENOENT) - return ERR_PTR(-ENOENT); - else if (ret < 0) + if (IS_ERR(ssidex_buf)) return ssidex_buf; /* diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index 0bb4bdb3deec..c835088de578 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -555,8 +555,8 @@ MODULE_DEVICE_TABLE(of, cs35l32_of_match); static const struct i2c_device_id cs35l32_id[] = { - {"cs35l32"}, - {} + { .name = "cs35l32" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l32_id); diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index 98b4d371d931..f49edb9ea1f2 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -1262,8 +1262,8 @@ static const struct of_device_id cs35l33_of_match[] = { MODULE_DEVICE_TABLE(of, cs35l33_of_match); static const struct i2c_device_id cs35l33_id[] = { - {"cs35l33"}, - {} + { .name = "cs35l33" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l33_id); diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index a5a8075598ff..e80984b22159 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -1175,8 +1175,8 @@ static const struct of_device_id cs35l34_of_match[] = { MODULE_DEVICE_TABLE(of, cs35l34_of_match); static const struct i2c_device_id cs35l34_id[] = { - {"cs35l34"}, - {} + { .name = "cs35l34" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l34_id); diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 7a01b1d9fc9d..b2439ec3c19f 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1639,8 +1639,8 @@ static const struct of_device_id cs35l35_of_match[] = { MODULE_DEVICE_TABLE(of, cs35l35_of_match); static const struct i2c_device_id cs35l35_id[] = { - {"cs35l35"}, - {} + { .name = "cs35l35" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l35_id); diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index 93818d7ec1a7..89645327945f 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1918,8 +1918,8 @@ static const struct of_device_id cs35l36_of_match[] = { MODULE_DEVICE_TABLE(of, cs35l36_of_match); static const struct i2c_device_id cs35l36_id[] = { - {"cs35l36"}, - {} + { .name = "cs35l36" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l36_id); diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index 34097996b784..23f59f3025bd 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -20,11 +20,11 @@ #include "cs35l41.h" static const struct i2c_device_id cs35l41_id_i2c[] = { - { "cs35l40" }, - { "cs35l41" }, - { "cs35l51" }, - { "cs35l53" }, - {} + { .name = "cs35l40" }, + { .name = "cs35l41" }, + { .name = "cs35l51" }, + { .name = "cs35l53" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l41_id_i2c); diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c index a09aa3b92ae1..3eba61e107a5 100644 --- a/sound/soc/codecs/cs35l45-i2c.c +++ b/sound/soc/codecs/cs35l45-i2c.c @@ -53,8 +53,8 @@ static const struct of_device_id cs35l45_of_match[] = { MODULE_DEVICE_TABLE(of, cs35l45_of_match); static const struct i2c_device_id cs35l45_id_i2c[] = { - { "cs35l45" }, - {} + { .name = "cs35l45" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l45_id_i2c); diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 0492ddc4102d..0f64ab628b03 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -72,9 +72,9 @@ static void cs35l56_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id cs35l56_id_i2c[] = { - { "cs35l56", 0x3556 }, - { "cs35l63", 0x3563 }, - {} + { .name = "cs35l56", .driver_data = 0x3556 }, + { .name = "cs35l63", .driver_data = 0x3563 }, + { } }; MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c); diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 88e0aac540d6..0a55b93b96f9 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -17,6 +17,7 @@ #include <linux/string_choices.h> #include <linux/swab.h> #include <linux/types.h> +#include <linux/unaligned.h> #include <linux/workqueue.h> #include "cs35l56.h" @@ -59,8 +60,6 @@ static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg, { int ret, i; - reg += CS35L56_SDW_ADDR_OFFSET; - for (i = 0; i < val_size; i += sizeof(u32)) { /* Poll for bus bridge idle */ ret = cs35l56_sdw_poll_mem_status(peripheral, @@ -97,57 +96,23 @@ static int cs35l56_sdw_slow_read(struct sdw_slave *peripheral, unsigned int reg, return 0; } -static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf) -{ - int ret; - - ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf); - if (ret != 0) { - dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret); - return ret; - } - - swab32s((u32 *)buf); - - return 0; -} - static int cs35l56_sdw_read(void *context, const void *reg_buf, const size_t reg_size, void *val_buf, size_t val_size) { struct sdw_slave *peripheral = context; - u8 *buf8 = val_buf; - unsigned int reg, bytes; + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + unsigned int reg_addr = get_unaligned_le32(reg_buf); int ret; - reg = le32_to_cpu(*(const __le32 *)reg_buf); - - if (cs35l56_is_otp_register(reg)) - return cs35l56_sdw_slow_read(peripheral, reg, buf8, val_size); + if (cs35l56_is_otp_register(reg_addr - CS35L56_SDW_ADDR_OFFSET)) + return cs35l56_sdw_slow_read(peripheral, reg_addr, (u8 *)val_buf, val_size); - reg += CS35L56_SDW_ADDR_OFFSET; - - if (val_size == 4) - return cs35l56_sdw_read_one(peripheral, reg, val_buf); - - while (val_size) { - bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */ - if (bytes > val_size) - bytes = val_size; - - ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8); - if (ret != 0) { - dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n", - reg, reg + bytes - 1, ret); - return ret; - } + ret = regmap_raw_read(cs35l56->sdw_bus_regmap, reg_addr, val_buf, val_size); + if (ret) + return ret; - swab32_array((u32 *)buf8, bytes / 4); - val_size -= bytes; - reg += bytes; - buf8 += bytes; - } + swab32_array((u32 *)val_buf, val_size / sizeof(u32)); return 0; } @@ -161,58 +126,34 @@ static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes) *dest32++ = swab32(*src32++); } -static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf) -{ - u32 val_le = swab32(*(u32 *)buf); - int ret; - - ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le); - if (ret != 0) { - dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret); - return ret; - } - - return 0; -} - static int cs35l56_sdw_gather_write(void *context, const void *reg_buf, size_t reg_size, const void *val_buf, size_t val_size) { struct sdw_slave *peripheral = context; - const u8 *src_be = val_buf; - u32 val_le_buf[64]; /* Define u32 so it is 32-bit aligned */ - unsigned int reg, bytes; + struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); + unsigned int reg_addr = get_unaligned_le32(reg_buf); + u32 swab_buf[64]; /* Define u32 so it is 32-bit aligned */ int ret; - reg = le32_to_cpu(*(const __le32 *)reg_buf); - reg += CS35L56_SDW_ADDR_OFFSET; - - if (val_size == 4) - return cs35l56_sdw_write_one(peripheral, reg, src_be); - - while (val_size) { - bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */ - if (bytes > val_size) - bytes = val_size; - if (bytes > sizeof(val_le_buf)) - bytes = sizeof(val_le_buf); - - cs35l56_swab_copy(val_le_buf, src_be, bytes); - - ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf); - if (ret != 0) { - dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n", - reg, reg + bytes - 1, ret); + while (val_size > sizeof(swab_buf)) { + cs35l56_swab_copy(swab_buf, val_buf, sizeof(swab_buf)); + ret = regmap_raw_write(cs35l56->sdw_bus_regmap, reg_addr, + swab_buf, sizeof(swab_buf)); + if (ret) return ret; - } - val_size -= bytes; - reg += bytes; - src_be += bytes; + val_size -= sizeof(swab_buf); + reg_addr += sizeof(swab_buf); + val_buf += sizeof(swab_buf); } - return 0; + if (val_size == 0) + return 0; + + cs35l56_swab_copy(swab_buf, val_buf, val_size); + + return regmap_raw_write(cs35l56->sdw_bus_regmap, reg_addr, swab_buf, val_size); } static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size) @@ -231,7 +172,7 @@ static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size * byte controls always have the same byte order, and firmware file blobs * can be written verbatim. */ -static const struct regmap_bus cs35l56_regmap_bus_sdw = { +static const struct regmap_bus cs35l56_regmap_swab_bus_sdw = { .read = cs35l56_sdw_read, .write = cs35l56_sdw_write, .gather_write = cs35l56_sdw_gather_write, @@ -239,6 +180,18 @@ static const struct regmap_bus cs35l56_regmap_bus_sdw = { .val_format_endian_default = REGMAP_ENDIAN_BIG, }; +/* Low-level SoundWire regmap to transfer the data over the bus */ +static const struct regmap_config cs35l56_sdw_bus_regmap = { + .name = "sdw-le32", + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .reg_format_endian = REGMAP_ENDIAN_LITTLE, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .max_register = CS35L56_DSP1_PMEM_5114 + 0x8000, + .cache_type = REGCACHE_NONE, +}; + static int cs35l56_sdw_get_unique_id(struct cs35l56_private *cs35l56) { int ret; @@ -277,11 +230,8 @@ static void cs35l56_sdw_init(struct sdw_slave *peripheral) * cs35l56_init can return with !init_done if it triggered * a soft reset. */ - if (cs35l56->base.init_done) { - /* Enable SoundWire interrupts */ - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, - CS35L56_SDW_INT_MASK_CODEC_IRQ); - } + if (cs35l56->base.init_done) + cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); out: pm_runtime_put_autosuspend(cs35l56->base.dev); @@ -306,15 +256,11 @@ static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral, pm_runtime_get_noresume(cs35l56->base.dev); /* - * Mask and clear until it has been handled. The read of GEN_INT_STAT_1 - * is required as per the SoundWire spec for interrupt status bits - * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. + * Mask and clear until it has been handled. * None of the interrupts are time-critical so use the * power-efficient queue. */ - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); - sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); + cs35l56_mask_soundwire_interrupts(peripheral); queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work); return 0; @@ -330,8 +276,7 @@ static void cs35l56_sdw_irq_work(struct work_struct *work) /* unmask interrupts */ if (!cs35l56->sdw_irq_no_unmask) - sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, - CS35L56_SDW_INT_MASK_CODEC_IRQ); + cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); pm_runtime_put_autosuspend(cs35l56->base.dev); } @@ -385,7 +330,6 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, switch (status) { case SDW_SLAVE_ATTACHED: - cs35l56->sdw_in_clock_stop_1 = false; if (cs35l56->sdw_attached) break; @@ -407,57 +351,29 @@ static int cs35l56_sdw_update_status(struct sdw_slave *peripheral, return 0; } -static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral, - enum sdw_clk_stop_mode mode, - enum sdw_clk_stop_type type) -{ - struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - - dev_dbg(cs35l56->base.dev, "%s: clock_stop_mode%d stage:%d\n", __func__, mode, type); - - switch (type) { - case SDW_CLK_POST_PREPARE: - if (mode == SDW_CLK_STOP_MODE1) - cs35l56->sdw_in_clock_stop_1 = true; - else - cs35l56->sdw_in_clock_stop_1 = false; - return 0; - default: - return 0; - } -} - static const struct sdw_slave_ops cs35l56_sdw_ops = { .read_prop = cs35l56_sdw_read_prop, .interrupt_callback = cs35l56_sdw_interrupt, .update_status = cs35l56_sdw_update_status, - .clk_stop = cs35l56_sdw_clk_stop, }; static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56) { struct sdw_slave *peripheral = cs35l56->sdw_peripheral; + int ret; - dev_dbg(cs35l56->base.dev, "attached:%u unattach_request:%u in_clock_stop_1:%u\n", - cs35l56->sdw_attached, peripheral->unattach_request, cs35l56->sdw_in_clock_stop_1); - - if (cs35l56->sdw_in_clock_stop_1 || peripheral->unattach_request) { - /* Cannot access registers until bus is re-initialized. */ - dev_dbg(cs35l56->base.dev, "Wait for initialization_complete\n"); - if (!wait_for_completion_timeout(&peripheral->initialization_complete, - msecs_to_jiffies(5000))) { - dev_err(cs35l56->base.dev, "initialization_complete timed out\n"); - return -ETIMEDOUT; - } + dev_dbg(cs35l56->base.dev, "attached:%u unattach_request:%u\n", + cs35l56->sdw_attached, peripheral->unattach_request); - peripheral->unattach_request = 0; - cs35l56->sdw_in_clock_stop_1 = false; + /* Cannot access registers until bus is re-initialized. */ + ret = sdw_slave_wait_for_init(peripheral, 5000); + if (ret) + return ret; - /* - * Don't call regcache_mark_dirty(), we can't be sure that the - * Manager really did issue a Bus Reset. - */ - } + /* + * Don't call regcache_mark_dirty(), we can't be sure that the + * Manager really did issue a Bus Reset. + */ return 0; } @@ -490,9 +406,7 @@ static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev) if (ret) return ret; - /* Re-enable SoundWire interrupts */ - sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, - CS35L56_SDW_INT_MASK_CODEC_IRQ); + cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); return 0; } @@ -504,18 +418,7 @@ static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev) if (!cs35l56->base.init_done) return 0; - /* - * Disable SoundWire interrupts. - * Flush - don't cancel because that could leave an unbalanced pm_runtime_get. - */ - cs35l56->sdw_irq_no_unmask = true; - flush_work(&cs35l56->sdw_irq_work); - - /* Mask interrupts and flush in case sdw_irq_work was queued again */ - sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); - sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); - sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); - flush_work(&cs35l56->sdw_irq_work); + cs35l56_disable_sdw_interrupts(cs35l56); return cs35l56_system_suspend(dev); } @@ -562,8 +465,16 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi cs35l56->base.type = ((unsigned int)id->driver_data) & 0xff; - cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw, - peripheral, regmap_config); + /* Low-level regmap to transfer read/writes over SoundWire bus */ + cs35l56->sdw_bus_regmap = devm_regmap_init_sdw(peripheral, &cs35l56_sdw_bus_regmap); + if (IS_ERR(cs35l56->sdw_bus_regmap)) { + ret = PTR_ERR(cs35l56->sdw_bus_regmap); + return dev_err_probe(dev, ret, "Failed to allocate bus register map\n"); + } + + /* Wrapper regmap to simulate big-endian ordering */ + cs35l56->base.regmap = devm_regmap_init(dev, &cs35l56_regmap_swab_bus_sdw, + peripheral, regmap_config); if (IS_ERR(cs35l56->base.regmap)) { ret = PTR_ERR(cs35l56->base.regmap); return dev_err_probe(dev, ret, "Failed to allocate register map\n"); @@ -583,13 +494,7 @@ static void cs35l56_sdw_remove(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); - /* Disable SoundWire interrupts */ - cs35l56->sdw_irq_no_unmask = true; - flush_work(&cs35l56->sdw_irq_work); - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); - sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); - sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); - flush_work(&cs35l56->sdw_irq_work); + cs35l56_disable_sdw_interrupts(cs35l56); cs35l56_remove(cs35l56); } diff --git a/sound/soc/codecs/cs35l56-shared-test.c b/sound/soc/codecs/cs35l56-shared-test.c index cfe7938065f9..4f52c8a192e5 100644 --- a/sound/soc/codecs/cs35l56-shared-test.c +++ b/sound/soc/codecs/cs35l56-shared-test.c @@ -29,6 +29,7 @@ struct cs35l56_shared_test_priv { struct faux_device *gpio_dev; struct cs35l56_shared_test_mock_gpio *gpio_priv; struct regmap *registers; + unsigned int reg_offset; struct cs35l56_base *cs35l56_base; u8 applied_pad_pull_state[CS35L56_MAX_GPIO]; }; @@ -194,6 +195,8 @@ static int cs35l56_shared_test_reg_read(void *context, unsigned int reg, unsigne { struct cs35l56_shared_test_priv *priv = context; + reg -= priv->reg_offset; + switch (reg) { case CS35L56_SYNC_GPIO1_CFG ... CS35L56_ASP2_DIO_GPIO13_CFG: case CS35L56_GPIO1_CTRL1 ... CS35L56_GPIO13_CTRL1: @@ -214,6 +217,8 @@ static int cs35l56_shared_test_reg_write(void *context, unsigned int reg, unsign { struct cs35l56_shared_test_priv *priv = context; + reg -= priv->reg_offset; + switch (reg) { case CS35L56_UPDATE_REGS: return cs35l56_shared_test_updt_gpio_pres(priv, reg, val); @@ -673,6 +678,7 @@ static int cs35l56_shared_test_case_base_init(struct kunit *test, u8 type, u8 re priv->cs35l56_base->rev = rev; if (regmap_config) { + priv->reg_offset = regmap_config->reg_base; ret = cs35l56_shared_test_case_regmap_init(test, regmap_config); if (ret) return ret; diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c index 795e2764d67e..f14e2eaaa4ee 100644 --- a/sound/soc/codecs/cs35l56-shared.c +++ b/sound/soc/codecs/cs35l56-shared.c @@ -534,6 +534,7 @@ static void cs35l56_spi_system_reset(struct cs35l56_base *cs35l56_base) * The regmap must remain in cache-only until the chip has * booted, so use a bypassed read. */ + val = 0; ret = read_poll_timeout(regmap_read_bypassed, read_ret, (val > 0) && (val < 0xffffffff), CS35L56_HALO_STATE_POLL_US, @@ -1259,7 +1260,7 @@ ssize_t cs35l56_cal_data_debugfs_write(struct cs35l56_base *cs35l56_base, return -EMSGSIZE; ret = simple_write_to_buffer(&cal_data, sizeof(cal_data), ppos, from, count); - if (ret) + if (ret < 0) return ret; ret = cs35l56_stash_calibration(cs35l56_base, &cal_data); @@ -1293,6 +1294,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_create_cal_debugfs, "SND_SOC_CS35L56_SHARED"); void cs35l56_remove_cal_debugfs(struct cs35l56_base *cs35l56_base) { debugfs_remove_recursive(cs35l56_base->debugfs); + cs35l56_base->debugfs = ERR_PTR(-ENOENT); } EXPORT_SYMBOL_NS_GPL(cs35l56_remove_cal_debugfs, "SND_SOC_CS35L56_SHARED"); @@ -1880,6 +1882,7 @@ EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, "SND_SOC_CS35L56_SHARED"); const struct regmap_config cs35l56_regmap_sdw = { .reg_bits = 32, + .reg_base = 0x8000, .val_bits = 32, .reg_stride = 4, .reg_format_endian = REGMAP_ENDIAN_LITTLE, @@ -1915,6 +1918,7 @@ const struct regmap_config cs35l63_regmap_sdw = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, + .reg_base = 0x8000, .reg_format_endian = REGMAP_ENDIAN_LITTLE, .val_format_endian = REGMAP_ENDIAN_BIG, .max_register = CS35L56_DSP1_PMEM_5114, diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index 4fbbdcc87151..570a68829ccd 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -37,6 +37,49 @@ #include "wm_adsp.h" #include "cs35l56.h" +void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral) +{ + /* + * The read of GEN_INT_STAT_1 is required as per the SoundWire spec + * for interrupt status bits to clear. + * GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1. + */ + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); + sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1); + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_mask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); + +void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral) +{ + sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, CS35L56_SDW_INT_MASK_CODEC_IRQ); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_unmask_soundwire_interrupts, "SND_SOC_CS35L56_CORE"); + +void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56) +{ + if (!cs35l56->sdw_peripheral) + return; + + cs35l56->sdw_irq_no_unmask = true; + flush_work(&cs35l56->sdw_irq_work); + + /* Mask interrupts and flush in case sdw_irq_work was queued again */ + cs35l56_mask_soundwire_interrupts(cs35l56->sdw_peripheral); + flush_work(&cs35l56->sdw_irq_work); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_disable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); + +void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56) +{ + if (!cs35l56->sdw_peripheral) + return; + + cs35l56->sdw_irq_no_unmask = false; + cs35l56_unmask_soundwire_interrupts(cs35l56->sdw_peripheral); +} +EXPORT_SYMBOL_NS_GPL(cs35l56_enable_sdw_interrupts, "SND_SOC_CS35L56_CORE"); + static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event); @@ -426,6 +469,8 @@ static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned reg_val &= ~(0x3f << channel_shift); reg_val |= bit_num << channel_shift; channel_shift += 8; + if (channel_shift > 24) + break; } return reg_val; @@ -788,14 +833,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing * Setting sdw_irq_no_unmask prevents the handler re-enabling * the SoundWire interrupt. */ - if (cs35l56->sdw_peripheral) { - cs35l56->sdw_irq_no_unmask = true; - flush_work(&cs35l56->sdw_irq_work); - sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0); - sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1); - sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); - flush_work(&cs35l56->sdw_irq_work); - } + cs35l56_disable_sdw_interrupts(cs35l56); ret = cs35l56_firmware_shutdown(&cs35l56->base); if (ret) @@ -847,12 +885,7 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56, bool firmware_missing err_unlock: mutex_unlock(&cs35l56->base.irq_lock); err: - /* Re-enable SoundWire interrupts */ - if (cs35l56->sdw_peripheral) { - cs35l56->sdw_irq_no_unmask = false; - sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, - CS35L56_SDW_INT_MASK_CODEC_IRQ); - } + cs35l56_enable_sdw_interrupts(cs35l56); } static void cs35l56_dsp_work(struct work_struct *work) @@ -1326,7 +1359,7 @@ VISIBLE_IF_KUNIT int cs35l56_set_fw_name(struct snd_soc_component *component) } EXPORT_SYMBOL_IF_KUNIT(cs35l56_set_fw_name); -static int cs35l56_component_probe(struct snd_soc_component *component) +static int _cs35l56_component_probe(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); @@ -1426,6 +1459,17 @@ static void cs35l56_component_remove(struct snd_soc_component *component) cs35l56->component = NULL; } +static int cs35l56_component_probe(struct snd_soc_component *component) +{ + int ret; + + ret = _cs35l56_component_probe(component); + if (ret < 0) + cs35l56_component_remove(component); + + return ret; +} + static int cs35l56_set_bias_level(struct snd_soc_component *component, enum snd_soc_bias_level level) { @@ -1480,6 +1524,7 @@ static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev) int cs35l56_system_suspend(struct device *dev) { struct cs35l56_private *cs35l56 = dev_get_drvdata(dev); + int ret; dev_dbg(dev, "system_suspend\n"); @@ -1495,7 +1540,11 @@ int cs35l56_system_suspend(struct device *dev) if (cs35l56->base.irq) disable_irq(cs35l56->base.irq); - return pm_runtime_force_suspend(dev); + ret = pm_runtime_force_suspend(dev); + if ((ret < 0) && cs35l56->base.irq) + enable_irq(cs35l56->base.irq); + + return ret; } EXPORT_SYMBOL_GPL(cs35l56_system_suspend); @@ -1964,11 +2013,14 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56) cs35l56_dai, ARRAY_SIZE(cs35l56_dai)); if (ret < 0) { dev_err_probe(cs35l56->base.dev, ret, "Register codec failed\n"); - goto err; + goto err_remove_wm_adsp; } return 0; +err_remove_wm_adsp: + wm_adsp2_remove(&cs35l56->dsp); + err: gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0); regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies); @@ -1994,7 +2046,8 @@ int cs35l56_init(struct cs35l56_private *cs35l56) if (cs35l56->base.init_done) return 0; - pm_runtime_set_autosuspend_delay(cs35l56->base.dev, 100); + pm_runtime_set_autosuspend_delay(cs35l56->base.dev, + CS35L56_FW_REQ_ACTIVE_TIMEOUT_MS + 50); pm_runtime_use_autosuspend(cs35l56->base.dev); pm_runtime_set_active(cs35l56->base.dev); pm_runtime_enable(cs35l56->base.dev); @@ -2076,6 +2129,8 @@ void cs35l56_remove(struct cs35l56_private *cs35l56) destroy_workqueue(cs35l56->dsp_wq); + wm_adsp2_remove(&cs35l56->dsp); + pm_runtime_dont_use_autosuspend(cs35l56->base.dev); pm_runtime_suspend(cs35l56->base.dev); pm_runtime_disable(cs35l56->base.dev); diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h index cd71b23b2a3a..9acd2e7e17c9 100644 --- a/sound/soc/codecs/cs35l56.h +++ b/sound/soc/codecs/cs35l56.h @@ -37,12 +37,12 @@ struct cs35l56_private { struct snd_soc_component *component; struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES]; struct sdw_slave *sdw_peripheral; + struct regmap *sdw_bus_regmap; const char *fallback_fw_suffix; struct work_struct sdw_irq_work; bool sdw_irq_no_unmask; bool soft_resetting; bool sdw_attached; - bool sdw_in_clock_stop_1; struct completion init_completion; int speaker_id; @@ -65,6 +65,11 @@ static inline struct cs35l56_private *cs35l56_private_from_base(struct cs35l56_b extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi; +void cs35l56_mask_soundwire_interrupts(struct sdw_slave *peripheral); +void cs35l56_unmask_soundwire_interrupts(struct sdw_slave *peripheral); +void cs35l56_disable_sdw_interrupts(struct cs35l56_private *cs35l56); +void cs35l56_enable_sdw_interrupts(struct cs35l56_private *cs35l56); + int cs35l56_system_suspend(struct device *dev); int cs35l56_system_suspend_late(struct device *dev); int cs35l56_system_suspend_no_irq(struct device *dev); diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c index 89c424dd838b..a889fbd519a8 100644 --- a/sound/soc/codecs/cs4234.c +++ b/sound/soc/codecs/cs4234.c @@ -11,7 +11,6 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/jiffies.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <sound/pcm.h> #include <sound/pcm_params.h> diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 3f759c13d6d1..286d5ca29854 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -638,7 +638,7 @@ static const struct of_device_id cs4265_of_match[] = { MODULE_DEVICE_TABLE(of, cs4265_of_match); static const struct i2c_device_id cs4265_id[] = { - { "cs4265" }, + { .name = "cs4265" }, { } }; MODULE_DEVICE_TABLE(i2c, cs4265_id); diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index a48980e746ff..47b2f903a32c 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -19,7 +19,6 @@ * - Power management is supported */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/slab.h> #include <sound/core.h> @@ -732,8 +731,8 @@ static int cs4270_i2c_probe(struct i2c_client *i2c_client) * cs4270_id - I2C device IDs supported by this driver */ static const struct i2c_device_id cs4270_id[] = { - {"cs4270"}, - {} + { .name = "cs4270" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs4270_id); diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c index 1d210b969173..869a5317f677 100644 --- a/sound/soc/codecs/cs4271-i2c.c +++ b/sound/soc/codecs/cs4271-i2c.c @@ -23,7 +23,7 @@ static int cs4271_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id cs4271_i2c_id[] = { - { "cs4271" }, + { .name = "cs4271" }, { } }; MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id); diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c index 98b6718ccabf..c0942f28723b 100644 --- a/sound/soc/codecs/cs42l42-i2c.c +++ b/sound/soc/codecs/cs42l42-i2c.c @@ -78,8 +78,8 @@ static const struct acpi_device_id __maybe_unused cs42l42_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, cs42l42_acpi_match); static const struct i2c_device_id cs42l42_id[] = { - {"cs42l42"}, - {} + { .name = "cs42l42" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs42l42_id); diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c index d5999ad9ff9b..ad1256910a18 100644 --- a/sound/soc/codecs/cs42l42-sdw.c +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -9,7 +9,6 @@ #include <linux/gpio/consumer.h> #include <linux/iopoll.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/of_irq.h> #include <linux/pm_runtime.h> #include <linux/soundwire/sdw.h> @@ -433,19 +432,16 @@ static const struct reg_sequence cs42l42_soft_reboot_seq[] = { static int cs42l42_sdw_handle_unattach(struct cs42l42_private *cs42l42) { struct sdw_slave *peripheral = cs42l42->sdw_peripheral; + int ret; if (!peripheral->unattach_request) return 0; /* Cannot access registers until master re-attaches. */ dev_dbg(&peripheral->dev, "Wait for initialization_complete\n"); - if (!wait_for_completion_timeout(&peripheral->initialization_complete, - msecs_to_jiffies(5000))) { - dev_err(&peripheral->dev, "initialization_complete timed out\n"); - return -ETIMEDOUT; - } - - peripheral->unattach_request = 0; + ret = sdw_slave_wait_for_init(peripheral, 5000); + if (ret) + return ret; /* * After a bus reset there must be a reconfiguration reset to diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c index 3e04e6897b14..dbf45cabfffe 100644 --- a/sound/soc/codecs/cs42l43-jack.c +++ b/sound/soc/codecs/cs42l43-jack.c @@ -310,6 +310,7 @@ irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data) #define CS42L43_JACK_ABSENT 0x0 #define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT) +#define CS42L43_JACK_MICROPHONE (SND_JACK_MECHANICAL | SND_JACK_MICROPHONE) #define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE) #define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET) #define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT) @@ -805,7 +806,7 @@ irqreturn_t cs42l43_tip_sense(int irq, void *data) if (priv->suspend_jack_debounce) db_delay += priv->tip_fall_db_ms + priv->tip_rise_db_ms; - queue_delayed_work(system_long_wq, &priv->tip_sense_work, + queue_delayed_work(system_dfl_long_wq, &priv->tip_sense_work, msecs_to_jiffies(db_delay)); return IRQ_HANDLED; @@ -871,7 +872,7 @@ static const struct cs42l43_jack_override_mode { .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) | CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK, - .report = CS42L43_JACK_LINEIN, + .report = CS42L43_JACK_MICROPHONE, }, [CS42L43_JACK_RAW_OPTICAL] = { .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES, @@ -932,7 +933,8 @@ int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF); if (!override) { - queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0); + queue_delayed_work(system_dfl_long_wq, &priv->tip_sense_work, + 0); } else { override--; diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c index f0d6ff0b2976..1d133577702e 100644 --- a/sound/soc/codecs/cs42l43.c +++ b/sound/soc/codecs/cs42l43.c @@ -19,7 +19,6 @@ #include <linux/jiffies.h> #include <linux/mfd/cs42l43.h> #include <linux/mfd/cs42l43-regs.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index ba7e237619f2..a20a1030c498 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -14,7 +14,7 @@ #include "cs42l51.h" static const struct i2c_device_id cs42l51_i2c_id[] = { - { "cs42l51" }, + { .name = "cs42l51" }, { } }; MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id); diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 662dc1a4835b..9d6bcfbbf0b2 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1226,7 +1226,7 @@ MODULE_DEVICE_TABLE(of, cs42l52_of_match); static const struct i2c_device_id cs42l52_id[] = { - { "cs42l52" }, + { .name = "cs42l52" }, { } }; MODULE_DEVICE_TABLE(i2c, cs42l52_id); diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index aabb74f1f43c..4d9a22a1029c 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1353,7 +1353,7 @@ MODULE_DEVICE_TABLE(of, cs42l56_of_match); static const struct i2c_device_id cs42l56_id[] = { - { "cs42l56" }, + { .name = "cs42l56" }, { } }; MODULE_DEVICE_TABLE(i2c, cs42l56_id); diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index bda8442c1d66..6ddc6549492a 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1369,8 +1369,8 @@ static const struct of_device_id cs42l73_of_match[] = { MODULE_DEVICE_TABLE(of, cs42l73_of_match); static const struct i2c_device_id cs42l73_id[] = { - {"cs42l73"}, - {} + { .name = "cs42l73" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs42l73_id); diff --git a/sound/soc/codecs/cs42l84.c b/sound/soc/codecs/cs42l84.c index e590a43559e4..f2a58163de0e 100644 --- a/sound/soc/codecs/cs42l84.c +++ b/sound/soc/codecs/cs42l84.c @@ -1101,8 +1101,8 @@ static const struct of_device_id cs42l84_of_match[] = { MODULE_DEVICE_TABLE(of, cs42l84_of_match); static const struct i2c_device_id cs42l84_id[] = { - { "cs42l84" }, - {} + { .name = "cs42l84" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs42l84_id); diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index 0faca384073a..4427d3214dee 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -9,7 +9,6 @@ #include <linux/i2c.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/pm_runtime.h> #include <sound/soc.h> @@ -49,9 +48,9 @@ static const struct of_device_id cs42xx8_of_match[] = { MODULE_DEVICE_TABLE(of, cs42xx8_of_match); static const struct i2c_device_id cs42xx8_i2c_id[] = { - {"cs42448", (kernel_ulong_t)&cs42448_data}, - {"cs42888", (kernel_ulong_t)&cs42888_data}, - {} + { .name = "cs42448", .driver_data = (kernel_ulong_t)&cs42448_data }, + { .name = "cs42888", .driver_data = (kernel_ulong_t)&cs42888_data }, + { } }; MODULE_DEVICE_TABLE(i2c, cs42xx8_i2c_id); diff --git a/sound/soc/codecs/cs42xx8-spi.c b/sound/soc/codecs/cs42xx8-spi.c new file mode 100644 index 000000000000..2e4b8e6c4081 --- /dev/null +++ b/sound/soc/codecs/cs42xx8-spi.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Cirrus Logic CS42448/CS42888 Audio CODEC DAI SPI driver + * + * Copyright 2026 NXP + * + */ + +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> + +#include "cs42xx8.h" + +/* + * CS42448/CS42888 SPI register access (from datasheet Figure 23): + * + * The SPI frame is 3 bytes: + * Byte 0: chip address [7:1] = 1001111, bit[0] = R/W (0=write, 1=read) + * Write: 0x9E, Read: 0x9F + * Byte 1: MAP - Memory Address Pointer + * bit[7] = INCR (auto-increment for burst), bits[6:0] = address + * Byte 2: data byte + * + * We configure reg_bits=16 so that regmap treats the address field as 2 bytes + * (big-endian). The chip address byte (0x9E/0x9F) is placed in the high byte + * via write_flag_mask / read_flag_mask, and the MAP register address occupies + * the low byte. Currently INCR (MAP bit[7]) is not set and use_single_read/write + * are enabled. This produces the correct 3-byte on-wire frame without any + * custom bus implementation: + * + * write: [0x9E, MAP_addr, data] + * read: [0x9F, MAP_addr] -> [data] + */ + +static int cs42xx8_spi_probe(struct spi_device *spi) +{ + struct cs42xx8_driver_data *drvdata; + struct regmap_config config; + int ret; + + drvdata = (struct cs42xx8_driver_data *)spi_get_device_match_data(spi); + if (!drvdata) + return dev_err_probe(&spi->dev, -EINVAL, + "failed to find driver data\n"); + + config = cs42xx8_regmap_config; + /* + * reg_bits=16 makes regmap send a 2-byte address field (big-endian). + * write_flag_mask/read_flag_mask are OR'd into that address field: + */ + config.reg_bits = 16; + config.write_flag_mask = 0x9E; + config.read_flag_mask = 0x9F; + + ret = cs42xx8_probe(&spi->dev, + devm_regmap_init_spi(spi, &config), drvdata); + if (ret) + return ret; + + pm_runtime_enable(&spi->dev); + pm_request_idle(&spi->dev); + + return 0; +} + +static void cs42xx8_spi_remove(struct spi_device *spi) +{ + pm_runtime_disable(&spi->dev); +} + +static const struct of_device_id cs42xx8_of_match[] = { + { .compatible = "cirrus,cs42448", .data = &cs42448_data, }, + { .compatible = "cirrus,cs42888", .data = &cs42888_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, cs42xx8_of_match); + +static const struct spi_device_id cs42xx8_spi_id[] = { + { .name = "cs42448", .driver_data = (kernel_ulong_t)&cs42448_data }, + { .name = "cs42888", .driver_data = (kernel_ulong_t)&cs42888_data }, + { } +}; +MODULE_DEVICE_TABLE(spi, cs42xx8_spi_id); + +static struct spi_driver cs42xx8_spi_driver = { + .driver = { + .name = "cs42xx8", + .pm = pm_ptr(&cs42xx8_pm), + .of_match_table = cs42xx8_of_match, + }, + .probe = cs42xx8_spi_probe, + .remove = cs42xx8_spi_remove, + .id_table = cs42xx8_spi_id, +}; + +module_spi_driver(cs42xx8_spi_driver); + +MODULE_DESCRIPTION("Cirrus Logic CS42448/CS42888 ALSA SoC Codec SPI Driver"); +MODULE_AUTHOR("Chancel Liu <chancel.liu@nxp.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c index 12fe9b3e2525..5b689549c74e 100644 --- a/sound/soc/codecs/cs42xx8.c +++ b/sound/soc/codecs/cs42xx8.c @@ -478,6 +478,9 @@ const struct regmap_config cs42xx8_regmap_config = { .volatile_reg = cs42xx8_volatile_register, .writeable_reg = cs42xx8_writeable_register, .cache_type = REGCACHE_MAPLE, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .use_single_read = true, + .use_single_write = true, }; EXPORT_SYMBOL_GPL(cs42xx8_regmap_config); diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index a3bdaac9c059..e7b06f962790 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -2753,11 +2753,11 @@ MODULE_DEVICE_TABLE(acpi, cs43130_acpi_match); static const struct i2c_device_id cs43130_i2c_id[] = { - {"cs43130"}, - {"cs4399"}, - {"cs43131"}, - {"cs43198"}, - {} + { .name = "cs43130" }, + { .name = "cs4399" }, + { .name = "cs43131" }, + { .name = "cs43198" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs43130_i2c_id); diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c index b726e22ef57d..a44e6b3c298e 100644 --- a/sound/soc/codecs/cs4341.c +++ b/sound/soc/codecs/cs4341.c @@ -248,7 +248,7 @@ static int cs4341_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id cs4341_i2c_id[] = { - { "cs4341" }, + { .name = "cs4341" }, { } }; MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id); diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index d9a9c34fffe3..6ac6d306b054 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -7,7 +7,6 @@ * Authors: Tim Howe <Tim.Howe@cirrus.com> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -358,8 +357,8 @@ static const struct of_device_id cs4349_of_match[] = { MODULE_DEVICE_TABLE(of, cs4349_of_match); static const struct i2c_device_id cs4349_i2c_id[] = { - {"cs4349"}, - {} + { .name = "cs4349" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs4349_i2c_id); diff --git a/sound/soc/codecs/cs530x-i2c.c b/sound/soc/codecs/cs530x-i2c.c index 52b02ceaa7e3..98cb61dd88c1 100644 --- a/sound/soc/codecs/cs530x-i2c.c +++ b/sound/soc/codecs/cs530x-i2c.c @@ -40,13 +40,13 @@ static const struct of_device_id cs530x_of_match[] = { MODULE_DEVICE_TABLE(of, cs530x_of_match); static const struct i2c_device_id cs530x_i2c_id[] = { - { "cs4282", CS4282 }, - { "cs4302", CS4302 }, - { "cs4304", CS4304 }, - { "cs4308", CS4308 }, - { "cs5302", CS5302 }, - { "cs5304", CS5304 }, - { "cs5308", CS5308 }, + { .name = "cs4282", .driver_data = CS4282 }, + { .name = "cs4302", .driver_data = CS4302 }, + { .name = "cs4304", .driver_data = CS4304 }, + { .name = "cs4308", .driver_data = CS4308 }, + { .name = "cs5302", .driver_data = CS5302 }, + { .name = "cs5304", .driver_data = CS5304 }, + { .name = "cs5308", .driver_data = CS5308 }, { } }; MODULE_DEVICE_TABLE(i2c, cs530x_i2c_id); diff --git a/sound/soc/codecs/cs530x.c b/sound/soc/codecs/cs530x.c index 18b5ff75feec..2c7e33135911 100644 --- a/sound/soc/codecs/cs530x.c +++ b/sound/soc/codecs/cs530x.c @@ -1093,6 +1093,29 @@ static int cs530x_component_probe(struct snd_soc_component *component) return 0; } +static bool cs530x_mclk_freq_is_valid(struct cs530x_priv *cs530x, + unsigned int freq) +{ + /* + * All these chips support 48 kHz- and 44.1 kHz-related sample rates, + * but they differ in what MCLK frequency is required for achieving + * the sample rate. + */ + switch (cs530x->devtype) { + case CS4282: + case CS4302: + case CS4304: + case CS4308: + return freq == 49152000 || freq == 45158400; + case CS5302: + case CS5304: + case CS5308: + return freq == 24576000 || freq == 22579200; + } + + return false; +} + static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, int source, unsigned int freq, int dir) { @@ -1101,11 +1124,7 @@ static int cs530x_set_sysclk(struct snd_soc_component *component, int clk_id, switch (source) { case CS530X_SYSCLK_SRC_MCLK: - switch (freq) { - case CS530X_SYSCLK_REF_45_1MHZ: - case CS530X_SYSCLK_REF_49_1MHZ: - break; - default: + if (!cs530x_mclk_freq_is_valid(cs530x, freq)) { dev_err(component->dev, "Invalid MCLK source rate %d\n", freq); return -EINVAL; } diff --git a/sound/soc/codecs/cs530x.h b/sound/soc/codecs/cs530x.h index 1e2f6a7a589c..18aa4dfd0c86 100644 --- a/sound/soc/codecs/cs530x.h +++ b/sound/soc/codecs/cs530x.h @@ -200,12 +200,6 @@ /* IN_VOL_CTL5 and OUT_VOL_CTL5 */ #define CS530X_INOUT_VU BIT(0) -/* MCLK Reference Source Frequency */ -/* 41KHz related */ -#define CS530X_SYSCLK_REF_45_1MHZ 45158400 -/* 48KHz related */ -#define CS530X_SYSCLK_REF_49_1MHZ 49152000 - /* System Clock Source */ #define CS530X_SYSCLK_SRC_MCLK 0 #define CS530X_SYSCLK_SRC_PLL 1 diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 93ea2fb4dae9..511453b4c375 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -1083,8 +1083,8 @@ static const struct of_device_id cs53l30_of_match[] = { MODULE_DEVICE_TABLE(of, cs53l30_of_match); static const struct i2c_device_id cs53l30_id[] = { - { "cs53l30" }, - {} + { .name = "cs53l30" }, + { } }; MODULE_DEVICE_TABLE(i2c, cs53l30_id); diff --git a/sound/soc/codecs/cx20442.c b/sound/soc/codecs/cx20442.c index c0dc69ce4d0e..0dea84cd8ef2 100644 --- a/sound/soc/codecs/cx20442.c +++ b/sound/soc/codecs/cx20442.c @@ -236,7 +236,8 @@ err: /* Line discipline .close() */ static void v253_close(struct tty_struct *tty) { - struct snd_soc_component *component = tty->disc_data; + struct cx20442_codec *codec = tty->disc_data; + struct snd_soc_component *component = codec->component; struct cx20442_priv *cx20442; tty->disc_data = NULL; @@ -248,7 +249,7 @@ static void v253_close(struct tty_struct *tty) /* Prevent the codec driver from further accessing the modem */ cx20442->tty = NULL; - component->card->pop_time = 0; + codec->ready = false; } /* Line discipline .hangup() */ @@ -261,7 +262,8 @@ static void v253_hangup(struct tty_struct *tty) static void v253_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp, size_t count) { - struct snd_soc_component *component = tty->disc_data; + struct cx20442_codec *codec = tty->disc_data; + struct snd_soc_component *component = codec->component; struct cx20442_priv *cx20442; if (!component) @@ -274,7 +276,7 @@ static void v253_receive(struct tty_struct *tty, const u8 *cp, const u8 *fp, /* Set up codec driver access to modem controls */ cx20442->tty = tty; - component->card->pop_time = 1; + codec->ready = true; } } @@ -375,7 +377,6 @@ static int cx20442_component_probe(struct snd_soc_component *component) cx20442->tty = NULL; snd_soc_component_set_drvdata(component, cx20442); - component->card->pop_time = 0; return 0; } diff --git a/sound/soc/codecs/cx20442.h b/sound/soc/codecs/cx20442.h index bb897bcb2486..78ab1eab083e 100644 --- a/sound/soc/codecs/cx20442.h +++ b/sound/soc/codecs/cx20442.h @@ -8,6 +8,11 @@ #ifndef _CX20442_CODEC_H #define _CX20442_CODEC_H +struct cx20442_codec { + struct snd_soc_component *component; + bool ready; +}; + extern struct tty_ldisc_ops v253_ops; #endif diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index b0033bf9be3a..83c6cbd40804 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1681,9 +1681,9 @@ static void cx2072x_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id cx2072x_i2c_id[] = { - { "cx20721" }, - { "cx20723" }, - {} + { .name = "cx20721" }, + { .name = "cx20723" }, + { } }; MODULE_DEVICE_TABLE(i2c, cx2072x_i2c_id); diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index 94e59546c2fe..4df1a25e318b 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1238,7 +1238,7 @@ static int da7210_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id da7210_i2c_id[] = { - { "da7210" }, + { .name = "da7210" }, { } }; MODULE_DEVICE_TABLE(i2c, da7210_i2c_id); diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 19f69a523f22..4bf91ab2553a 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -1720,11 +1720,6 @@ static int da7213_set_component_pll(struct snd_soc_component *component, return _da7213_set_component_pll(component, pll_id, source, fref, fout); } -/* - * Select below from Sound Card, not Auto - * SND_SOC_DAIFMT_CBC_CFC - * SND_SOC_DAIFMT_CBP_CFP - */ static const u64 da7213_dai_formats = SND_SOC_POSSIBLE_DAIFMT_I2S | SND_SOC_POSSIBLE_DAIFMT_LEFT_J | @@ -2271,7 +2266,7 @@ static const struct dev_pm_ops da7213_pm = { }; static const struct i2c_device_id da7213_i2c_id[] = { - { "da7213" }, + { .name = "da7213" }, { } }; MODULE_DEVICE_TABLE(i2c, da7213_i2c_id); diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index 5c80839704c7..93bd045ed870 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -3282,8 +3282,8 @@ static int da7218_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id da7218_i2c_id[] = { - { "da7217", DA7217_DEV_ID }, - { "da7218", DA7218_DEV_ID }, + { .name = "da7217", .driver_data = DA7217_DEV_ID }, + { .name = "da7218", .driver_data = DA7218_DEV_ID }, { } }; MODULE_DEVICE_TABLE(i2c, da7218_i2c_id); diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index ec4059f381be..f0874d891e12 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -2712,7 +2712,7 @@ static int da7219_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id da7219_i2c_id[] = { - { "da7219", }, + { .name = "da7219" }, { } }; MODULE_DEVICE_TABLE(i2c, da7219_i2c_id); diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 140e449d3ef4..12d1ac98461e 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1547,7 +1547,7 @@ err: } static const struct i2c_device_id da732x_i2c_id[] = { - { "da7320"}, + { .name = "da7320" }, { } }; MODULE_DEVICE_TABLE(i2c, da732x_i2c_id); diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index a52276e32f2f..a88c13533145 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1513,7 +1513,7 @@ static int da9055_i2c_probe(struct i2c_client *i2c) * and PMIC, which must be different to operate together. */ static const struct i2c_device_id da9055_i2c_id[] = { - { "da9055-codec" }, + { .name = "da9055-codec" }, { } }; MODULE_DEVICE_TABLE(i2c, da9055_i2c_id); diff --git a/sound/soc/codecs/es8311.c b/sound/soc/codecs/es8311.c index 564af5c04dbb..6fe3ae41afbd 100644 --- a/sound/soc/codecs/es8311.c +++ b/sound/soc/codecs/es8311.c @@ -955,7 +955,7 @@ static int es8311_i2c_probe(struct i2c_client *i2c_client) } static const struct i2c_device_id es8311_id[] = { - { "es8311" }, + { .name = "es8311" }, { } }; MODULE_DEVICE_TABLE(i2c, es8311_id); diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index 9245c33700de..3abe77423f29 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -12,7 +12,6 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/mutex.h> #include <linux/regmap.h> #include <sound/pcm.h> @@ -895,8 +894,8 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client) } static const struct i2c_device_id es8316_i2c_id[] = { - {"es8316" }, - {} + { .name = "es8316" }, + { } }; MODULE_DEVICE_TABLE(i2c, es8316_i2c_id); diff --git a/sound/soc/codecs/es8323.c b/sound/soc/codecs/es8323.c index 605375b154c8..b926340256be 100644 --- a/sound/soc/codecs/es8323.c +++ b/sound/soc/codecs/es8323.c @@ -18,7 +18,6 @@ #include <linux/clk.h> #include <linux/delay.h> #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/regmap.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -787,7 +786,7 @@ static int es8323_i2c_probe(struct i2c_client *i2c_client) } static const struct i2c_device_id es8323_i2c_id[] = { - { "es8323" }, + { .name = "es8323" }, { } }; MODULE_DEVICE_TABLE(i2c, es8323_i2c_id); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 55a65ef99208..a79b2da35099 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -1347,8 +1347,8 @@ static void es8326_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id es8326_i2c_id[] = { - {"es8326" }, - {} + { .name = "es8326" }, + { } }; MODULE_DEVICE_TABLE(i2c, es8326_i2c_id); diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c index 56bfbe9261ce..36a4987e1a59 100644 --- a/sound/soc/codecs/es8328-i2c.c +++ b/sound/soc/codecs/es8328-i2c.c @@ -16,8 +16,8 @@ #include "es8328.h" static const struct i2c_device_id es8328_id[] = { - { "es8328" }, - { "es8388" }, + { .name = "es8328" }, + { .name = "es8388" }, { } }; MODULE_DEVICE_TABLE(i2c, es8328_id); diff --git a/sound/soc/codecs/es8375.c b/sound/soc/codecs/es8375.c index 0b9406e93c0e..e8747bc24433 100644 --- a/sound/soc/codecs/es8375.c +++ b/sound/soc/codecs/es8375.c @@ -752,7 +752,7 @@ static void es8375_i2c_shutdown(struct i2c_client *i2c) } static const struct i2c_device_id es8375_id[] = { - {"es8375"}, + { .name = "es8375" }, { } }; MODULE_DEVICE_TABLE(i2c, es8375_id); diff --git a/sound/soc/codecs/es8389.c b/sound/soc/codecs/es8389.c index 449d9574b03a..3484c87853cb 100644 --- a/sound/soc/codecs/es8389.c +++ b/sound/soc/codecs/es8389.c @@ -1015,7 +1015,7 @@ MODULE_DEVICE_TABLE(of, es8389_if_dt_ids); #endif static const struct i2c_device_id es8389_i2c_id[] = { - {"es8389"}, + { .name = "es8389" }, { } }; MODULE_DEVICE_TABLE(i2c, es8389_i2c_id); diff --git a/sound/soc/codecs/es9356.c b/sound/soc/codecs/es9356.c new file mode 100644 index 000000000000..1122455aab77 --- /dev/null +++ b/sound/soc/codecs/es9356.c @@ -0,0 +1,1143 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// es9356.c -- SoundWire codec driver +// +// Copyright(c) 2025 Everest Semiconductor Co., Ltd +// +// + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <linux/pm_runtime.h> +#include <sound/sdw.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> +#include <sound/sdca_function.h> +#include <sound/sdca_regmap.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <sound/jack.h> +#include <sound/sdca_asoc.h> +#include "es9356.h" + +struct es9356_sdw_priv { + struct sdw_slave *slave; + struct device *dev; + struct regmap *regmap; + struct snd_soc_component *component; + struct snd_soc_jack *hs_jack; + + /* lock for irq*/ + struct mutex disable_irq_lock; + + /* lock for pde*/ + struct mutex pde_lock; + + bool hw_init; + bool first_hw_init; + int jack_type; + bool disable_irq; + + struct delayed_work interrupt_handle_work; + struct delayed_work button_detect_work; + unsigned int sdca_status; +}; + +static int es9356_sdw_component_probe(struct snd_soc_component *component) +{ + struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component); + + es9356->component = component; + + return 0; +} + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -9600, 12, 0); +static const DECLARE_TLV_DB_SCALE(amic_gain_tlv, 0, 3, 0); +static const DECLARE_TLV_DB_SCALE(dmic_gain_tlv, 0, 6, 0); + +static const struct snd_kcontrol_new es9356_sdca_controls[] = { + SDCA_SINGLE_Q78_TLV("FU41 Left Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_L), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU41 Right Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_R), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU36 Left Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU36 Right Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU33 Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU33, ES9356_SDCA_CTL_FU_CH_GAIN, 0), + ES9356_GAIN_MIN, ES9356_AMIC_GAIN_MAX, ES9356_AMIC_GAIN_STEP, amic_gain_tlv), + SDCA_SINGLE_Q78_TLV("FU21 Left Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_L), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU21 Right Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_R), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU113 Left Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU113 Right Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_R), + ES9356_VOLUME_MIN, ES9356_VOLUME_MAX, ES9356_VOLUME_STEP, out_vol_tlv), + SDCA_SINGLE_Q78_TLV("FU11 Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU11, ES9356_SDCA_CTL_FU_CH_GAIN, 0), + ES9356_GAIN_MIN, ES9356_DMIC_GAIN_MAX, ES9356_DMIC_GAIN_STEP, dmic_gain_tlv), +}; + +static const char *const es9356_left_mux_txt[] = { + "Left", + "Right", +}; + +static const char *const es9356_right_mux_txt[] = { + "Right", + "Left", +}; + +static const struct soc_enum es9356_left_mux_enum = + SOC_ENUM_SINGLE(ES9356_DAC_SWAP, 1, + ARRAY_SIZE(es9356_left_mux_txt), es9356_left_mux_txt); +static const struct soc_enum es9356_right_mux_enum = + SOC_ENUM_SINGLE(ES9356_DAC_SWAP, 0, + ARRAY_SIZE(es9356_right_mux_txt), es9356_right_mux_txt); + +static const struct snd_kcontrol_new es9356_left_mux_controls = + SOC_DAPM_ENUM("Channel MUX", es9356_left_mux_enum); +static const struct snd_kcontrol_new es9356_right_mux_controls = + SOC_DAPM_ENUM("Channel MUX", es9356_right_mux_enum); + +static const struct snd_soc_dapm_widget es9356_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("MIC1"), + SND_SOC_DAPM_INPUT("PDM_DIN"), + + SND_SOC_DAPM_SUPPLY("DMIC Clock", ES9356_DMIC_GPIO, 1, 1, NULL, 0), + + SND_SOC_DAPM_AIF_IN("DP4RX", "DP4 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP1TX", "DP1 Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_PGA("IF DP3RXL", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("IF DP3RXR", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_MUX("Left Channel MUX", SND_SOC_NOPM, 0, 0, &es9356_left_mux_controls), + SND_SOC_DAPM_MUX("Right Channel MUX", SND_SOC_NOPM, 0, 0, &es9356_right_mux_controls), + + SND_SOC_DAPM_DAC("FU 21 Left", NULL, + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1), + SND_SOC_DAPM_DAC("FU 21 Right", NULL, + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1), + SND_SOC_DAPM_DAC("FU 41 Left", NULL, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1), + SND_SOC_DAPM_DAC("FU 41 Right", NULL, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1), + SND_SOC_DAPM_DAC("FU 113 Left", NULL, + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1), + SND_SOC_DAPM_DAC("FU 113 Right", NULL, + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1), + SND_SOC_DAPM_DAC("FU 36 Left", NULL, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_MUTE, CH_L), 0, 1), + SND_SOC_DAPM_DAC("FU 36 Right", NULL, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_MUTE, CH_R), 0, 1), +}; + +static const struct snd_soc_dapm_route es9356_audio_map[] = { + {"FU 36 Left", NULL, "MIC1"}, + {"FU 36 Right", NULL, "MIC1"}, + {"DP2TX", NULL, "FU 36 Left"}, + {"DP2TX", NULL, "FU 36 Right"}, + + {"PDM_DIN", NULL, "DMIC Clock"}, + {"FU 113 Left", NULL, "PDM_DIN"}, + {"FU 113 Right", NULL, "PDM_DIN"}, + {"DP1TX", NULL, "FU 113 Left"}, + {"DP1TX", NULL, "FU 113 Right"}, + + {"FU 41 Left", NULL, "DP4RX"}, + {"FU 41 Right", NULL, "DP4RX"}, + + {"IF DP3RXL", NULL, "DP3RX"}, + {"IF DP3RXR", NULL, "DP3RX"}, + + {"Left Channel MUX", "Left", "IF DP3RXL"}, + {"Left Channel MUX", "Right", "IF DP3RXR"}, + {"Right Channel MUX", "Left", "IF DP3RXL"}, + {"Right Channel MUX", "Right", "IF DP3RXR"}, + + {"FU 21 Left", NULL, "Left Channel MUX"}, + {"FU 21 Right", NULL, "Right Channel MUX"}, + + {"SPK", NULL, "FU 21 Left"}, + {"SPK", NULL, "FU 21 Right"}, + + {"HP", NULL, "FU 41 Left"}, + {"HP", NULL, "FU 41 Right"}, +}; + +static int es9356_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component); + int ret; + + es9356->hs_jack = hs_jack; + + /* we can only resume if the device was initialized at least once */ + if (!es9356->first_hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + /* pm_runtime not enabled yet */ + dev_info(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + if (es9356->hs_jack) + sdw_write_no_pm(es9356->slave, SDW_SCP_SDCA_INTMASK1, + (SDW_SCP_SDCA_INTMASK_SDCA_7 | SDW_SCP_SDCA_INTMASK_SDCA_5 | SDW_SCP_SDCA_INTMASK_SDCA_1)); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} +static const struct snd_soc_component_driver snd_soc_es9356_sdw_component = { + .probe = es9356_sdw_component_probe, + .controls = es9356_sdca_controls, + .num_controls = ARRAY_SIZE(es9356_sdca_controls), + .dapm_widgets = es9356_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(es9356_dapm_widgets), + .dapm_routes = es9356_audio_map, + .num_dapm_routes = ARRAY_SIZE(es9356_audio_map), + .set_jack = es9356_set_jack_detect, + .endianness = 1, +}; + +static int es9356_sdw_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void es9356_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int es9356_sdca_button(unsigned int *buffer) +{ + int cur_button = -1; + + if (*(buffer + 1) | *(buffer + 2)) + return -EINVAL; + switch (*buffer) { + case 0x00: + cur_button = 0; + break; + case 0x20: + cur_button = SND_JACK_BTN_4; + break; + case 0x10: + cur_button = SND_JACK_BTN_2; + break; + case 0x08: + cur_button = SND_JACK_BTN_1; + break; + case 0x02: + cur_button = SND_JACK_BTN_3; + break; + case 0x01: + cur_button = SND_JACK_BTN_0; + break; + default: + break; + } + + return cur_button; +} + +static int es9356_sdca_button_detect(struct es9356_sdw_priv *es9356) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + unsigned int button[3]; + int ret; + + ret = regmap_read(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, ES9356_SDCA_ENT_HID01, ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret < 0 || owner == 0x01) + return 0; + + ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID, &offset); + if (ret < 0) + goto button_det_end; + + for (idx = 0; idx < ARRAY_SIZE(button); idx++) { + ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID + offset + idx, &val); + if (ret < 0) + goto button_det_end; + button[idx] = val; + } + + btn_type = es9356_sdca_button(&button[0]); + +button_det_end: + if (owner == 0x00) + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, ES9356_SDCA_ENT_HID01, ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return btn_type; +} + +static int es9356_sdca_headset_detect(struct es9356_sdw_priv *es9356) +{ + unsigned int reg; + int ret; + + ret = regmap_read(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_DETECTED_MODE, 0), ®); + + if (ret < 0) + goto io_error; + + switch (reg) { + case 0x00: + es9356->jack_type = 0; + break; + case 0x03: + es9356->jack_type = SND_JACK_HEADPHONE; + break; + case 0x04: + es9356->jack_type = SND_JACK_HEADSET; + break; + default: + es9356->jack_type = 0; + return -1; + } + + if (reg) { + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_SELECTED_MODE, 0), reg); + regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0x75); + } else { + regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4); + } + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void es9356_interrupt_handler(struct work_struct *work) +{ + struct es9356_sdw_priv *es9356 = + container_of(work, struct es9356_sdw_priv, interrupt_handle_work.work); + int ret, btn_type = 0; + + if (!es9356->hs_jack) + return; + + if (!es9356->component->card || !es9356->component->card->instantiated) + return; + + /* Handling different types of interrupts based on the mask bit */ + if (es9356->sdca_status & SDW_SCP_SDCA_INT_SDCA_7) { + btn_type = es9356_sdca_button_detect(es9356); + if (btn_type < 0) + return; + } else { + ret = es9356_sdca_headset_detect(es9356); + if (ret < 0) + return; + } + + if (es9356->jack_type != SND_JACK_HEADSET) + btn_type = 0; + + snd_soc_jack_report(es9356->hs_jack, es9356->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4); + + if (btn_type) { + snd_soc_jack_report(es9356->hs_jack, es9356->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4); + mod_delayed_work(system_power_efficient_wq, + &es9356->button_detect_work, msecs_to_jiffies(280)); + } +} + +static void es9356_button_detect_handler(struct work_struct *work) +{ + struct es9356_sdw_priv *es9356 = + container_of(work, struct es9356_sdw_priv, button_detect_work.work); + int ret, idx, btn_type = 0; + unsigned int reg, offset; + unsigned int button[3]; + + /* Check headset */ + ret = regmap_read(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_DETECTED_MODE, 0), ®); + + if (ret < 0) + goto io_error; + + if (reg == 0x04) { + ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID, &offset); + if (ret < 0) + goto io_error; + for (idx = 0; idx < ARRAY_SIZE(button); idx++) { + ret = regmap_read(es9356->regmap, ES9356_BUF_ADDR_HID + offset + idx, ®); + if (ret < 0) + goto io_error; + button[idx] = reg; + } + btn_type = es9356_sdca_button(&button[0]); + if (btn_type < 0) + return; + } + + snd_soc_jack_report(es9356->hs_jack, es9356->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4); + + if (btn_type) { + snd_soc_jack_report(es9356->hs_jack, es9356->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | + SND_JACK_BTN_4); + mod_delayed_work(system_power_efficient_wq, + &es9356->button_detect_work, msecs_to_jiffies(280)); + } + + return; +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static int es9356_pde_transition_delay(struct es9356_sdw_priv *es9356, unsigned char func, + unsigned char entity, unsigned char ps) +{ + unsigned int retries = 10, val; + + /* waiting for Actual PDE becomes to PS0/PS3 */ + while (retries) { + regmap_read(es9356->regmap, + SDW_SDCA_CTL(func, entity, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0), &val); + if (val == ps) + return 1; + + usleep_range(1000, 1500); + retries--; + } + if (!retries) { + dev_dbg(&es9356->slave->dev, "%s PDE is NOT %s", __func__, ps?"PS3":"PS0"); + } + return 0; +} + +static int es9356_power_state(struct snd_soc_dai *dai, unsigned char ps, unsigned int *rate) +{ + struct snd_soc_component *component = dai->component; + struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + unsigned char func, cs_entity, pde_entity; + int ret; + + switch (dai->id) { + case ES9356_DMIC: + func = FUNC_NUM_MIC; + cs_entity = ES9356_SDCA_ENT_CS113; + pde_entity = ES9356_SDCA_ENT_PDE11; + break; + case ES9356_AMP: + func = FUNC_NUM_AMP; + cs_entity = ES9356_SDCA_ENT_CS21; + pde_entity = ES9356_SDCA_ENT_PDE23; + break; + case ES9356_JACK_IN: + func = FUNC_NUM_UAJ; + cs_entity = ES9356_SDCA_ENT_CS36; + pde_entity = ES9356_SDCA_ENT_PDE34; + break; + case ES9356_JACK_OUT: + func = FUNC_NUM_UAJ; + cs_entity = ES9356_SDCA_ENT_CS41; + pde_entity = ES9356_SDCA_ENT_PDE47; + break; + default: + return -EINVAL; + } + + /* power state changes are not independent across functions */ + mutex_lock(&es9356->pde_lock); + ret = es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps0:ps3); + if (ret) { + regmap_write(es9356->regmap, + SDW_SDCA_CTL(func, pde_entity, ES9356_SDCA_CTL_REQ_POWER_STATE, 0), ps?ps3:ps0); + es9356_pde_transition_delay(es9356, func, pde_entity, ps?ps3:ps0); + } else + dev_dbg(component->dev, "%s PDE is already %d\n", __func__, ps?ps0:ps3); + + mutex_unlock(&es9356->pde_lock); + + if (rate) + regmap_write(es9356->regmap, + SDW_SDCA_CTL(func, cs_entity, ES9356_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), *rate); + + return 0; +} + +static int es9356_sdw_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + unsigned char ps0 = 0x0; + unsigned int rate; + int ret; + + if (!sdw_stream) + return -EINVAL; + + if (!es9356->slave) + return -EINVAL; + + /* SoundWire specific configuration */ + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + + port_config.num = dai->id; + + ret = sdw_stream_add_slave(es9356->slave, &stream_config, + &port_config, 1, sdw_stream); + if (ret) { + dev_err(dai->dev, "Unable to configure port\n"); + return -EINVAL; + } + + switch (params_rate(params)) { + case 16000: + rate = ES9356_SDCA_RATE_16000HZ; + break; + case 44100: + rate = ES9356_SDCA_RATE_44100HZ; + break; + case 48000: + rate = ES9356_SDCA_RATE_48000HZ; + break; + case 96000: + rate = ES9356_SDCA_RATE_96000HZ; + break; + default: + dev_err(component->dev, "%s: Rate %d is not supported\n", + __func__, params_rate(params)); + return -EINVAL; + } + + ret = es9356_power_state(dai, ps0, &rate); + if (ret) { + dev_err(component->dev, "%s: Invalid dai id: %d\n", + __func__, dai->id); + return -EINVAL; + } + + return 0; +} + +static int es9356_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct es9356_sdw_priv *es9356 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + unsigned char ps3 = 0x3; + int ret; + + if (!es9356->slave) + return -EINVAL; + + sdw_stream_remove_slave(es9356->slave, sdw_stream); + + ret = es9356_power_state(dai, ps3, NULL); + if (ret) { + dev_err(component->dev, "%s: Invalid dai id: %d\n", + __func__, dai->id); + return -EINVAL; + } + + return 0; +} + +static const struct snd_soc_dai_ops es9356_sdw_ops = { + .hw_params = es9356_sdw_pcm_hw_params, + .hw_free = es9356_sdw_pcm_hw_free, + .set_stream = es9356_sdw_set_sdw_stream, + .shutdown = es9356_sdw_shutdown, +}; + +static struct snd_soc_dai_driver es9356_sdw_dai[] = { + { + .name = "es9356-sdp-aif4", + .id = ES9356_DMIC, + .capture = { + .stream_name = "DP1 Capture", + .channels_min = 1, + .channels_max = 2, + }, + .ops = &es9356_sdw_ops, + }, + { + .name = "es9356-sdp-aif2", + .id = ES9356_JACK_IN, + .capture = { + .stream_name = "DP2 Capture", + .channels_min = 1, + .channels_max = 2, + }, + .ops = &es9356_sdw_ops, + }, + { + .name = "es9356-sdp-aif3", + .id = ES9356_AMP, + .playback = { + .stream_name = "DP3 Playback", + .channels_min = 1, + .channels_max = 2, + }, + .ops = &es9356_sdw_ops, + }, + { + .name = "es9356-sdp-aif1", + .id = ES9356_JACK_OUT, + .playback = { + .stream_name = "DP4 Playback", + .channels_min = 1, + .channels_max = 2, + }, + .ops = &es9356_sdw_ops, + }, +}; + +static int es9356_sdca_mbq_size(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU33, ES9356_SDCA_CTL_FU_CH_GAIN, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU11, ES9356_SDCA_CTL_FU_CH_GAIN, 0): + return 2; + default: + return 1; + } +} + +static struct regmap_sdw_mbq_cfg es9356_mbq_config = { + .mbq_size = es9356_sdca_mbq_size, +}; + +static bool es9356_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case ES9356_BUF_ADDR_HID: + case ES9356_HID_BYTE2: + case ES9356_HID_BYTE3: + case ES9356_HID_BYTE4: + case SDW_SDCA_CTL(FUNC_NUM_HID, ES9356_SDCA_ENT_HID01, ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER, 0): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_DETECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_GE35, ES9356_SDCA_CTL_SELECTED_MODE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_PDE23, ES9356_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_PDE23, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_PDE11, ES9356_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_PDE11, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE47, ES9356_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE47, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE34, ES9356_SDCA_CTL_REQ_POWER_STATE, 0): + case SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_PDE34, ES9356_SDCA_CTL_ACTUAL_POWER_STATE, 0): + case ES9356_FLAGS_HP: + return true; + default: + return false; + } +} + +static const struct regmap_config es9356_sdca_regmap = { + .reg_bits = 32, + .val_bits = 16, + .volatile_reg = es9356_sdca_volatile_register, + .max_register = 0x45ffffff, + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static void es9356_register_init(struct es9356_sdw_priv *es9356) +{ + regmap_write(es9356->regmap, ES9356_STATE, 0x02); + regmap_write(es9356->regmap, ES9356_ENDPOINT_MODE, 0x24); + regmap_write(es9356->regmap, ES9356_PRE_DIV_CTL, 0x00); + regmap_write(es9356->regmap, ES9356_ADC_OSR, 0x18); + regmap_write(es9356->regmap, ES9356_ADC_OSRGAIN, 0x13); + regmap_write(es9356->regmap, ES9356_DAC_OSR, 0x16); + regmap_write(es9356->regmap, ES9356_CLK_CTL, 0x0f); + regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x01); + regmap_write(es9356->regmap, ES9356_CLK_SEL, 0x30); + + regmap_write(es9356->regmap, ES9356_DETCLK_CTL, 0x51); + regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x10); + regmap_write(es9356->regmap, ES9356_MICBIAS_CTL, 0x10); + regmap_write(es9356->regmap, ES9356_HPDETECT_CTL, 0x07); + regmap_write(es9356->regmap, ES9356_ADC_ANA, 0x30); + regmap_write(es9356->regmap, ES9356_PGA_CTL, 0xa8); + regmap_write(es9356->regmap, ES9356_ADC_INT, 0xaa); + regmap_write(es9356->regmap, ES9356_ADC_LP, 0x19); + regmap_write(es9356->regmap, ES9356_VMID1SEL, 0xbc); + regmap_write(es9356->regmap, ES9356_VMID_TIME, 0x0b); + regmap_write(es9356->regmap, ES9356_STATE_TIME, 0xbb); + regmap_write(es9356->regmap, ES9356_HP_SPK_TIME, 0x77); + regmap_write(es9356->regmap, ES9356_HP_DETECTTIME, 0xa4); + regmap_write(es9356->regmap, ES9356_MICBIAS_SEL, 0x15); + regmap_write(es9356->regmap, ES9356_KEY_PRESS_TIME, 0xff); + regmap_write(es9356->regmap, ES9356_KEY_RELEASE_TIME, 0xff); + regmap_write(es9356->regmap, ES9356_KEY_HOLD_TIME, 0x0f); + regmap_write(es9356->regmap, ES9356_BTSEL_REF, 0x00); + regmap_write(es9356->regmap, ES9356_KEYD_DETECT, 0x18); + regmap_write(es9356->regmap, ES9356_MICBIAS_RES, 0x03); + regmap_write(es9356->regmap, ES9356_BUTTON_CHARGE, 0x00); + regmap_write(es9356->regmap, ES9356_CALIBRATION_TIME, 0x13); + regmap_write(es9356->regmap, ES9356_CALIBRATION_SETTING, 0xf4); + + regmap_write(es9356->regmap, ES9356_SPK_VOLUME, 0x33); + regmap_write(es9356->regmap, ES9356_DAC_VROI, 0x01); + regmap_write(es9356->regmap, ES9356_DAC_LP, 0x00); + regmap_write(es9356->regmap, ES9356_HP_IBIAS, 0x04); + regmap_write(es9356->regmap, ES9356_HP_LP, 0x03); + regmap_write(es9356->regmap, ES9356_SPKLDO_CTL, 0x65); + regmap_write(es9356->regmap, ES9356_SPKBIAS_COMP, 0x09); + regmap_write(es9356->regmap, ES9356_VMID1STL, 0x00); + regmap_write(es9356->regmap, ES9356_VMID2STL, 0x00); + regmap_write(es9356->regmap, ES9356_VSEL, 0xfc); + + regmap_write(es9356->regmap, ES9356_IBIASGEN, 0x10); + regmap_write(es9356->regmap, ES9356_ADC_AMIC_CTL, 0x0d); + regmap_write(es9356->regmap, ES9356_STATE, 0x0e); + regmap_write(es9356->regmap, ES9356_CSM_RESET, 0x00); + regmap_write(es9356->regmap, ES9356_HP_TYPE, 0x08); + + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_FU113, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME); + + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_FU21, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME); + + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU41, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME); + + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_L), ES9356_DEFAULT_VOLUME); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_FU36, ES9356_SDCA_CTL_FU_VOLUME, CH_R), ES9356_DEFAULT_VOLUME); +} + +static int es9356_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev); + + if (es9356->hw_init) + return 0; + + es9356->disable_irq = false; + + regcache_cache_only(es9356->regmap, false); + + if (es9356->first_hw_init) { + regcache_cache_bypass(es9356->regmap, true); + } else { + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + es9356_register_init(es9356); + } + pm_runtime_get_noresume(&slave->dev); + + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT_XU12, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT_XU22, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_XU42, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT_XU36, ES9356_SDCA_CTL_SELECTED_MODE, 0), 0x01); + regmap_write(es9356->regmap, + SDW_SDCA_CTL(FUNC_NUM_UAJ, ES9356_SDCA_ENT0, ES9356_SDCA_CTL_FUNC_STATUS, 0), 0x40); + + if (es9356->first_hw_init) { + regcache_cache_bypass(es9356->regmap, false); + regcache_mark_dirty(es9356->regmap); + } else + es9356->first_hw_init = true; + + es9356->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + return 0; +} + +static int es9356_sdw_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) { + es9356->hw_init = false; + cancel_delayed_work_sync(&es9356->interrupt_handle_work); + cancel_delayed_work_sync(&es9356->button_detect_work); + regcache_cache_only(es9356->regmap, true); + } + + if (status == SDW_SLAVE_ATTACHED) { + if (es9356->hs_jack) + sdw_write_no_pm(es9356->slave, SDW_SCP_SDCA_INTMASK1, + (SDW_SCP_SDCA_INTMASK_SDCA_7 | SDW_SCP_SDCA_INTMASK_SDCA_5 | SDW_SCP_SDCA_INTMASK_SDCA_1)); + } + + if (es9356->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + return es9356_sdca_io_init(&slave->dev, slave); +} + +static int es9356_sdw_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->paging_support = true; + + /* + * first we need to allocate memory for set bits in port lists + * the port allocation is completely arbitrary: + * DP0 is not supported + * DP3 and DP4 is sink + * DP1 and DP2 is source + */ + prop->source_ports = BIT(1) | BIT(2); + prop->sink_ports = BIT(3) | BIT(4); + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), + GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), + GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + j++; + } + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int es9356_sdw_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev); + unsigned int sdca_cascade, scp_sdca_stat1 = 0; + int count = 0, retry = 3; + int ret, stat, reg; + + mutex_lock(&es9356->disable_irq_lock); + + ret = sdw_read_no_pm(es9356->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + es9356->sdca_status = ret; + + do { + reg = sdw_read_no_pm(es9356->slave, SDW_SCP_SDCA_INT1); + if (reg < 0) + goto io_error; + if (reg & SDW_SCP_SDCA_INTMASK_SDCA_1) { + ret = sdw_update_no_pm(es9356->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_1, SDW_SCP_SDCA_INT_SDCA_1); + if (ret < 0) + goto io_error; + } + + if (reg & SDW_SCP_SDCA_INTMASK_SDCA_5) { + ret = sdw_update_no_pm(es9356->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_5, SDW_SCP_SDCA_INT_SDCA_5); + if (ret < 0) + goto io_error; + } + + if (reg & SDW_SCP_SDCA_INTMASK_SDCA_7) { + ret = sdw_update_no_pm(es9356->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_7, SDW_SCP_SDCA_INT_SDCA_7); + if (ret < 0) + goto io_error; + } + + ret = sdw_read_no_pm(es9356->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(es9356->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & + (SDW_SCP_SDCA_INTMASK_SDCA_1 | SDW_SCP_SDCA_INTMASK_SDCA_5 | SDW_SCP_SDCA_INTMASK_SDCA_7); + + stat = scp_sdca_stat1 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + /* The 280 ms figure was determined through testing */ + if (status->sdca_cascade && !es9356->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &es9356->interrupt_handle_work, msecs_to_jiffies(280)); + + mutex_unlock(&es9356->disable_irq_lock); + return 0; + +io_error: + mutex_unlock(&es9356->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static const struct sdw_slave_ops es9356_sdw_slave_ops = { + .read_prop = es9356_sdw_read_prop, + .interrupt_callback = es9356_sdw_interrupt_callback, + .update_status = es9356_sdw_update_status, +}; + +static int es9356_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave *slave) +{ + struct es9356_sdw_priv *es9356; + int ret; + + es9356 = devm_kzalloc(dev, sizeof(*es9356), GFP_KERNEL); + if (!es9356) + return -ENOMEM; + + dev_set_drvdata(dev, es9356); + + es9356->slave = slave; + es9356->regmap = regmap; + mutex_init(&es9356->disable_irq_lock); + mutex_init(&es9356->pde_lock); + + regcache_cache_only(es9356->regmap, true); + + es9356->hw_init = false; + es9356->first_hw_init = false; + + INIT_DELAYED_WORK(&es9356->interrupt_handle_work, + es9356_interrupt_handler); + INIT_DELAYED_WORK(&es9356->button_detect_work, + es9356_button_detect_handler); + + ret = devm_snd_soc_register_component(dev, + &snd_soc_es9356_sdw_component, + es9356_sdw_dai, + ARRAY_SIZE(es9356_sdw_dai)); + if (ret) { + dev_err_probe(dev, ret, "Failed to register component\n"); + return ret; + } + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + pm_runtime_enable(dev); + + return 0; +} + +static int es9356_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_sdw_mbq_cfg(&slave->dev, slave, &es9356_sdca_regmap, &es9356_mbq_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return es9356_sdca_init(&slave->dev, regmap, slave); +} + +static void es9356_sdw_remove(struct sdw_slave *slave) +{ + struct es9356_sdw_priv *es9356 = dev_get_drvdata(&slave->dev); + + if (es9356->hw_init) { + cancel_delayed_work_sync(&es9356->interrupt_handle_work); + cancel_delayed_work_sync(&es9356->button_detect_work); + } + + if (es9356->first_hw_init) + pm_runtime_disable(&slave->dev); + + mutex_destroy(&es9356->disable_irq_lock); + mutex_destroy(&es9356->pde_lock); +} + +static const struct sdw_device_id es9356_sdw_id[] = { + SDW_SLAVE_ENTRY_EXT(0x04b3, 0x9356, 0x02, 0, 0), + SDW_SLAVE_ENTRY_EXT(0x04b3, 0x9356, 0x03, 0, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, es9356_sdw_id); + +static int es9356_sdca_dev_suspend(struct device *dev) +{ + struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); + + cancel_delayed_work_sync(&es9356->interrupt_handle_work); + cancel_delayed_work_sync(&es9356->button_detect_work); + + regcache_cache_only(es9356->regmap, true); + + return 0; +} + +static int es9356_sdca_dev_system_suspend(struct device *dev) +{ + struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); + + mutex_lock(&es9356->disable_irq_lock); + es9356->disable_irq = true; + mutex_unlock(&es9356->disable_irq_lock); + + return es9356_sdca_dev_suspend(dev); +} + +#define es9356_PROBE_TIMEOUT 2000 + +static int es9356_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct es9356_sdw_priv *es9356 = dev_get_drvdata(dev); + int ret; + + if (!slave->unattach_request) + es9356->disable_irq = false; + + ret = sdw_slave_wait_for_init(slave, es9356_PROBE_TIMEOUT); + if (ret) { + sdw_show_ping_status(slave->bus, true); + return ret; + } + + regcache_cache_only(es9356->regmap, false); + regcache_sync(es9356->regmap); + return 0; +} + +static const struct dev_pm_ops es9356_sdca_pm = { + SYSTEM_SLEEP_PM_OPS(es9356_sdca_dev_system_suspend, es9356_sdca_dev_resume) + RUNTIME_PM_OPS(es9356_sdca_dev_suspend, es9356_sdca_dev_resume, NULL) +}; + +static struct sdw_driver es9356_sdw_driver = { + .driver = { + .name = "es9356", + .pm = pm_ptr(&es9356_sdca_pm), + }, + .probe = es9356_sdw_probe, + .remove = es9356_sdw_remove, + .ops = &es9356_sdw_slave_ops, + .id_table = es9356_sdw_id, +}; +module_sdw_driver(es9356_sdw_driver); + +MODULE_IMPORT_NS("SND_SOC_SDCA"); +MODULE_DESCRIPTION("ASoC ES9356 SDCA SDW codec driver"); +MODULE_AUTHOR("Michael Zhang <zhangyi@everest-semi.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/es9356.h b/sound/soc/codecs/es9356.h new file mode 100644 index 000000000000..2a676f36590f --- /dev/null +++ b/sound/soc/codecs/es9356.h @@ -0,0 +1,208 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ES9356_H__ +#define __ES9356_H__ + +/*ES9356 Implementation-define*/ +#define ES9356_FLAGS_HP 0x2003 +#define ES9356_CSM_RESET 0x2020 +#define ES9356_FUC_RESET 0x2021 +#define ES9356_STATE 0x2022 +#define ES9356_VMID_TIME 0x2023 +#define ES9356_STATE_TIME 0x2024 +#define ES9356_HP_SPK_TIME 0x2025 +#define ES9356_WP_ENABLE 0x2026 +#define ES9356_DMIC_GPIO 0x2027 +#define ES9356_ENDPOINT_MODE 0x2028 + +/*HP DETECT*/ +#define ES9356_HP_TYPE 0x2029 +#define ES9356_HP_DETECTTIME 0x202A +#define ES9356_MICBIAS_SEL 0x202B +#define ES9356_KEY_PRESS_TIME 0x202C +#define ES9356_KEY_RELEASE_TIME 0x202D +#define ES9356_KEY_HOLD_TIME 0x202E +#define ES9356_BTSEL_REF 0x202F +#define ES9356_BUTTON_CHARGE 0x2030 + +#define ES9356_KEYD_DETECT 0x2031 +#define ES9356_DPEN_TIME 0x2032 +#define ES9356_TIMER_CHECK 0x2033 +#define ES9356_IBIASGEN 0x2041 +#define ES9356_VMID1SEL 0x2042 +#define ES9356_VMID1STL 0x2043 +#define ES9356_VMID2SEL 0x2044 +#define ES9356_VMID2STL 0x2045 +#define ES9356_VSEL 0x2046 +#define ES9356_MICBIAS_CTL 0x2047 +#define ES9356_HPDETECT_CTL 0x2048 +#define ES9356_MICBIAS_RES 0x2049 + +/*CLK*/ +#define ES9356_CLK_SEL 0x2050 +#define ES9356_CLK_CTL 0x2051 +#define ES9356_DETCLK_CTL 0x2052 +#define ES9356_CPCLK_CTL 0x2053 +#define ES9356_SPKCLK_CTL 0x2054 +#define ES9356_PRE_DIV_CTL 0x2055 +#define ES9356_DLL_MODE 0x2056 +#define ES9356_ANACLK_SEL 0x2057 +#define ES9356_OSRCLK_SEL 0x2058 +#define ES9356_DSPCLK_SEL 0x2059 +#define ES9356_SPK9M_MODE 0x205a + +/*ADC DIG CTL*/ +#define ES9356_DMIC_POL 0x2061 +#define ES9356_ADC_SWAP 0x2062 +#define ES9356_ADC_OSR 0x2063 +#define ES9356_ADC_OSRGAIN 0x2064 +#define ES9356_ADC_CLEARRAM 0x2065 +#define ES9356_ADC_RAMP 0x2066 +#define ES9356_ADC_HPF1 0x2067 +#define ES9356_ADC_HPF2 0x2068 +#define ES9356_ADC_ALC 0x206C +#define ES9356_ALC_LEVEL 0x206D +#define ES9356_ALC_RAMP_WINSIZE 0x206E + +/*ADC ANA CTL*/ +#define ES9356_ADC_REF_EN 0x2080 +#define ES9356_ADC_AMIC_CTL 0x2081 +#define ES9356_ADC_ANA 0x2082 +#define ES9356_PGA_CTL 0x2083 +#define ES9356_ADC_INT 0x2084 +#define ES9356_ADC_VCM 0x2085 +#define ES9356_ADC_VRPBIAS 0x2086 +#define ES9356_ADC_LP 0x2087 + +/*DAC DIG CTL*/ +#define ES9356_DAC_FSMODE 0x2090 +#define ES9356_DAC_OSR 0x2091 +#define ES9356_DAC_INV 0x2092 +#define ES9356_DAC_RAMP 0x2093 +#define ES9356_DAC_VPPSCALE 0x2094 +#define ES9356_DAC_SWAP 0x2097 +#define ES9356_SPKCMP_VPPSC 0x20A0 +#define ES9356_CALIBRATION_TIME 0x20A1 +#define ES9356_CALIBRATION_SETTING 0x20A2 +#define ES9356_DAC_OFFSET_LH 0x20A3 +#define ES9356_DAC_OFFSET_LL 0x20A4 +#define ES9356_DAC_OFFSET_RH 0x20A5 +#define ES9356_DAC_OFFSET_RL 0x20A6 + +/*DAC ANA CTL*/ +#define ES9356_DAC_REF_EN 0x20B0 +#define ES9356_DAC_ENABLE 0x20B1 +#define ES9356_DAC_VROI 0x20B2 +#define ES9356_DAC_LP 0x20B3 + +/*HP CTL*/ +#define ES9356_CHARGEPUMP_CTL 0x20C0 +#define ES9356_CPLDO_CTL 0x20C1 +#define ES9356_HP_REF_CTL 0x20C2 +#define ES9356_HP_IBIAS 0x20C3 +#define ES9356_HP_EN 0x20C4 +#define ES9356_HP_VOLUME 0x20C5 +#define ES9356_HP_LP 0x20C6 + +/*SPK CTL*/ +#define ES9356_SPKLDO_CTL 0x20D0 +#define ES9356_CLASSD_CTL 0x20D1 +#define ES9356_SPK_HBDG 0x20D5 +#define ES9356_SPK_VOLUME 0x20D7 +#define ES9356_SPK_SCP 0x20D8 +#define ES9356_SPK_DT 0x20D9 +#define ES9356_SPK_OTP 0x20DA +#define ES9356_SPKBIAS_COMP 0x20DB + +/* ES9356 SDCA Control - function number */ +#define FUNC_NUM_UAJ 0x01 +#define FUNC_NUM_MIC 0x02 +#define FUNC_NUM_AMP 0x03 +#define FUNC_NUM_HID 0x04 + +/* ES9356 SDCA entity */ +#define ES9356_SDCA_ENT0 0x00 +#define ES9356_SDCA_ENT_PDE11 0x03 +#define ES9356_SDCA_ENT_FU11 0x04 +#define ES9356_SDCA_ENT_XU12 0x05 +#define ES9356_SDCA_ENT_FU113 0x07 +#define ES9356_SDCA_ENT_CS113 0x09 +#define ES9356_SDCA_ENT_PPU11 0x0C + +#define ES9356_SDCA_ENT_CS21 0x02 +#define ES9356_SDCA_ENT_PPU21 0x03 +#define ES9356_SDCA_ENT_FU21 0X04 +#define ES9356_SDCA_ENT_XU22 0x06 +#define ES9356_SDCA_ENT_SAPU29 0x03 +#define ES9356_SDCA_ENT_PDE23 0x0B +#define ES9356_SDCA_ENT_HID01 0x01 + +#define ES9356_SDCA_ENT_CS41 0x02 +#define ES9356_SDCA_ENT_FU35 0x04 +#define ES9356_SDCA_ENT_XU42 0x06 +#define ES9356_SDCA_ENT_FU41 0x07 +#define ES9356_SDCA_ENT_PDE47 0x0E +#define ES9356_SDCA_ENT_IT33 0x0F +#define ES9356_SDCA_ENT_PDE34 0x10 +#define ES9356_SDCA_ENT_FU33 0x11 +#define ES9356_SDCA_ENT_XU36 0x13 +#define ES9356_SDCA_ENT_FU36 0x15 +#define ES9356_SDCA_ENT_CS36 0x17 +#define ES9356_SDCA_ENT_GE35 0x18 + +/* ES9356 SDCA control */ +#define ES9356_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define ES9356_SDCA_CTL_FU_MUTE 0x01 +#define ES9356_SDCA_CTL_FU_VOLUME 0x02 +#define ES9356_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define ES9356_SDCA_CTL_SELECTED_MODE 0x01 +#define ES9356_SDCA_CTL_DETECTED_MODE 0x02 +#define ES9356_SDCA_CTL_REQ_POWER_STATE 0x01 +#define ES9356_SDCA_CTL_FU_CH_GAIN 0x0b +#define ES9356_SDCA_CTL_FUNC_STATUS 0x10 +#define ES9356_SDCA_CTL_ACTUAL_POWER_STATE 0x10 +#define ES9356_SDCA_CTL_POSTURE_NUMBER 0x00 + +/* ES9356 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 +#define MBQ 0x2000 + +/* ES9356 HID*/ +#define ES9356_BUF_ADDR_HID 0x44000000 +#define ES9356_HID_BYTE2 0x44000001 +#define ES9356_HID_BYTE3 0x44000002 +#define ES9356_HID_BYTE4 0x44000003 + +/* ES9356 Volume Setting*/ +#define ES9356_VU_BASE 768 +#define ES9356_OFFSET_HIGH 0x07F8 +#define ES9356_OFFSET_LOW 0x0007 +#define ES9356_DEFAULT_VOLUME 0x00 +#define ES9356_VOLUME_STEP 32 +#define ES9356_VOLUME_MIN -768 +#define ES9356_VOLUME_MAX 285 +#define ES9356_AMIC_GAIN_STEP 768 +#define ES9356_DMIC_GAIN_STEP 1536 +#define ES9356_GAIN_MIN 0 +#define ES9356_AMIC_GAIN_MAX 10 +#define ES9356_DMIC_GAIN_MAX 3 + +enum { + ES9356_DMIC = 1, /* For dmic */ + ES9356_JACK_IN, /* For headset mic */ + ES9356_AMP, /* For speaker */ + ES9356_JACK_OUT, /* For headphone */ +}; + +enum { + ES9356_SDCA_RATE_16000HZ, + ES9356_SDCA_RATE_24000HZ, + ES9356_SDCA_RATE_32000HZ, + ES9356_SDCA_RATE_44100HZ, + ES9356_SDCA_RATE_48000HZ, + ES9356_SDCA_RATE_88200HZ, + ES9356_SDCA_RATE_96000HZ, +}; + +#endif diff --git a/sound/soc/codecs/framer-codec.c b/sound/soc/codecs/framer-codec.c index 6f57a3aeecc8..a87a78390172 100644 --- a/sound/soc/codecs/framer-codec.c +++ b/sound/soc/codecs/framer-codec.c @@ -238,15 +238,13 @@ static int framer_dai_startup(struct snd_pcm_substream *substream, return 0; } -static const u64 framer_dai_formats[] = { - SND_SOC_POSSIBLE_DAIFMT_DSP_B, -}; +static const u64 framer_dai_formats = SND_SOC_POSSIBLE_DAIFMT_DSP_B; static const struct snd_soc_dai_ops framer_dai_ops = { .startup = framer_dai_startup, .set_tdm_slot = framer_dai_set_tdm_slot, - .auto_selectable_formats = framer_dai_formats, - .num_auto_selectable_formats = ARRAY_SIZE(framer_dai_formats), + .auto_selectable_formats = &framer_dai_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver framer_dai_driver = { diff --git a/sound/soc/codecs/fs210x.c b/sound/soc/codecs/fs210x.c index eda716f817b5..5f381fe063e8 100644 --- a/sound/soc/codecs/fs210x.c +++ b/sound/soc/codecs/fs210x.c @@ -1557,9 +1557,9 @@ static void fs210x_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id fs210x_i2c_id[] = { - { "fs2104" }, - { "fs2105s" }, - {} + { .name = "fs2104" }, + { .name = "fs2105s" }, + { } }; MODULE_DEVICE_TABLE(i2c, fs210x_i2c_id); diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c index 2652fcf2a3a3..3220f9226e0b 100644 --- a/sound/soc/codecs/hdac_hdmi.c +++ b/sound/soc/codecs/hdac_hdmi.c @@ -911,12 +911,14 @@ static int hdac_hdmi_set_pin_port_mux(struct snd_kcontrol *kcontrol, struct hdac_device *hdev = dev_to_hdac_dev(dev); struct hdac_hdmi_priv *hdmi = hdev_to_hdmi_priv(hdev); struct hdac_hdmi_pcm *pcm; - const char *cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + const char *cvt_name; ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol); if (ret < 0) return ret; + cvt_name = e->texts[ucontrol->value.enumerated.item[0]]; + if (port == NULL) return -EINVAL; diff --git a/sound/soc/codecs/idt821034.c b/sound/soc/codecs/idt821034.c index 39bafefa6a18..084090ccef77 100644 --- a/sound/soc/codecs/idt821034.c +++ b/sound/soc/codecs/idt821034.c @@ -860,18 +860,17 @@ static int idt821034_dai_startup(struct snd_pcm_substream *substream, return 0; } -static const u64 idt821034_dai_formats[] = { +static const u64 idt821034_dai_formats = SND_SOC_POSSIBLE_DAIFMT_DSP_A | - SND_SOC_POSSIBLE_DAIFMT_DSP_B, -}; + SND_SOC_POSSIBLE_DAIFMT_DSP_B; static const struct snd_soc_dai_ops idt821034_dai_ops = { .startup = idt821034_dai_startup, .hw_params = idt821034_dai_hw_params, .set_tdm_slot = idt821034_dai_set_tdm_slot, .set_fmt = idt821034_dai_set_fmt, - .auto_selectable_formats = idt821034_dai_formats, - .num_auto_selectable_formats = ARRAY_SIZE(idt821034_dai_formats), + .auto_selectable_formats = &idt821034_dai_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver idt821034_dai_driver = { diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index b7a94631d77d..fa1c23dda4eb 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -1133,7 +1133,7 @@ static int isabelle_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id isabelle_i2c_id[] = { - { "isabelle" }, + { .name = "isabelle" }, { } }; MODULE_DEVICE_TABLE(i2c, isabelle_i2c_id); diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index 26cdb750cbca..06add7fb6737 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -128,7 +128,7 @@ static int lm4857_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id lm4857_i2c_id[] = { - { "lm4857" }, + { .name = "lm4857" }, { } }; MODULE_DEVICE_TABLE(i2c, lm4857_i2c_id); diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index 043030509795..6e2e292d143b 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1443,7 +1443,7 @@ static int lm49453_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id lm49453_i2c_id[] = { - { "lm49453" }, + { .name = "lm49453" }, { } }; MODULE_DEVICE_TABLE(i2c, lm49453_i2c_id); diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c index 528d5b167ecf..58a5798823d7 100644 --- a/sound/soc/codecs/lpass-va-macro.c +++ b/sound/soc/codecs/lpass-va-macro.c @@ -244,6 +244,11 @@ static const struct va_macro_data sm8250_va_data = { .version = LPASS_CODEC_VERSION_1_0, }; +static const struct va_macro_data sc7280_va_data = { + .has_swr_master = false, + .has_npl_clk = false, +}; + static const struct va_macro_data sm8450_va_data = { .has_swr_master = true, .has_npl_clk = true, @@ -1755,7 +1760,7 @@ static const struct dev_pm_ops va_macro_pm_ops = { }; static const struct of_device_id va_macro_dt_match[] = { - { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sm8250_va_data }, + { .compatible = "qcom,sc7280-lpass-va-macro", .data = &sc7280_va_data }, { .compatible = "qcom,sm6115-lpass-va-macro", .data = &sm8450_va_data }, { .compatible = "qcom,sm8250-lpass-va-macro", .data = &sm8250_va_data }, { .compatible = "qcom,sm8450-lpass-va-macro", .data = &sm8450_va_data }, diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index 7ad7a9fb7255..8621d9bdb9fd 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -213,7 +213,7 @@ static int max9768_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id max9768_i2c_id[] = { - { "max9768" }, + { .name = "max9768" }, { } }; MODULE_DEVICE_TABLE(i2c, max9768_i2c_id); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 9f40ca4b60d5..df438baf05dc 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -1722,8 +1722,8 @@ static const struct snd_soc_component_driver soc_component_dev_max98088 = { }; static const struct i2c_device_id max98088_i2c_id[] = { - { "max98088", MAX98088 }, - { "max98089", MAX98089 }, + { .name = "max98088", .driver_data = MAX98088 }, + { .name = "max98089", .driver_data = MAX98089 }, { } }; MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index 13a15459040f..da416329b038 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -2337,7 +2337,7 @@ static irqreturn_t max98090_interrupt(int irq, void *data) } /** - * max98090_mic_detect - Enable microphone detection via the MAX98090 IRQ + * max98090_set_jack - Enable microphone detection via the MAX98090 IRQ * * @component: MAX98090 component * @jack: jack to report detection events on @@ -2349,12 +2349,12 @@ static irqreturn_t max98090_interrupt(int irq, void *data) * * If no jack is supplied detection will be disabled. */ -int max98090_mic_detect(struct snd_soc_component *component, - struct snd_soc_jack *jack) +static int max98090_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *jack, void *data) { struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); - dev_dbg(component->dev, "max98090_mic_detect\n"); + dev_dbg(component->dev, "%s\n", __func__); max98090->jack = jack; if (jack) { @@ -2377,7 +2377,6 @@ int max98090_mic_detect(struct snd_soc_component *component, return 0; } -EXPORT_SYMBOL_GPL(max98090_mic_detect); #define MAX98090_RATES SNDRV_PCM_RATE_8000_96000 #define MAX98090_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) @@ -2554,6 +2553,7 @@ static const struct snd_soc_component_driver soc_component_dev_max98090 = { .remove = max98090_remove, .seq_notifier = max98090_seq_notifier, .set_bias_level = max98090_set_bias_level, + .set_jack = max98090_set_jack, .idle_bias_on = 1, .use_pmdown_time = 1, .endianness = 1, @@ -2572,8 +2572,8 @@ static const struct regmap_config max98090_regmap = { }; static const struct i2c_device_id max98090_i2c_id[] = { - { "max98090", MAX98090 }, - { "max98091", MAX98091 }, + { .name = "max98090", .driver_data = MAX98090 }, + { .name = "max98091", .driver_data = MAX98091 }, { } }; MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index 6ce8dd176e48..048af4a1376f 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -1543,7 +1543,4 @@ struct max98090_priv { bool shdn_pending; }; -int max98090_mic_detect(struct snd_soc_component *component, - struct snd_soc_jack *jack); - #endif diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index aae6423156e1..ced9bd4d94da 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2109,7 +2109,7 @@ static const struct snd_soc_component_driver soc_component_dev_max98095 = { }; static const struct i2c_device_id max98095_i2c_id[] = { - { "max98095", MAX98095 }, + { .name = "max98095", .driver_data = MAX98095 }, { } }; MODULE_DEVICE_TABLE(i2c, max98095_i2c_id); diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c index cc811f58c9d2..b0f8043fb9e2 100644 --- a/sound/soc/codecs/max98357a.c +++ b/sound/soc/codecs/max98357a.c @@ -10,7 +10,6 @@ #include <linux/err.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index 25af78ab30d5..099dc5bf6195 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -90,24 +90,15 @@ static int max98363_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct max98363_priv *max98363 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!max98363->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(MAX98363_PROBE_TIMEOUT)); - if (!time) { - dev_err(dev, "Initialization not complete, timed out\n"); - return -ETIMEDOUT; - } - -regmap_sync: + ret = sdw_slave_wait_for_init(slave, MAX98363_PROBE_TIMEOUT); + if (ret) + return ret; - slave->unattach_request = 0; regcache_cache_only(max98363->regmap, false); regcache_sync(max98363->regmap); diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index 852db211ba1e..c8d4c68af562 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -400,7 +400,7 @@ static int max98371_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max98371_i2c_id[] = { - { "max98371" }, + { .name = "max98371" }, { } }; diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index f58b8c8625a7..8805bd01153c 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -5,7 +5,6 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/of.h> #include <linux/pm.h> #include <linux/regmap.h> @@ -576,8 +575,8 @@ static int max98373_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max98373_i2c_id[] = { - { "max98373"}, - { }, + { .name = "max98373" }, + { } }; MODULE_DEVICE_TABLE(i2c, max98373_i2c_id); diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 16673440218c..8fe9c58e1a62 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -4,7 +4,6 @@ #include <linux/acpi.h> #include <linux/delay.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> @@ -266,25 +265,17 @@ static int max98373_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct max98373_priv *max98373 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!max98373->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(MAX98373_PROBE_TIMEOUT)); - if (!time) { - dev_err(dev, "Initialization not complete, timed out\n"); + ret = sdw_slave_wait_for_init(slave, MAX98373_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(max98373->regmap, false); regcache_sync(max98373->regmap); diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c index 076f15a9867e..a4c57152d25f 100644 --- a/sound/soc/codecs/max98388.c +++ b/sound/soc/codecs/max98388.c @@ -6,7 +6,6 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/regmap.h> @@ -977,8 +976,8 @@ static int max98388_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max98388_i2c_id[] = { - { "max98388"}, - { }, + { .name = "max98388" }, + { } }; MODULE_DEVICE_TABLE(i2c, max98388_i2c_id); diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 65f095c47191..2bbedf84ee5d 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1096,8 +1096,8 @@ static int max98390_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max98390_i2c_id[] = { - { "max98390"}, - {}, + { .name = "max98390" }, + { } }; MODULE_DEVICE_TABLE(i2c, max98390_i2c_id); diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index 18fd90227187..9c1d7213410c 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1871,9 +1871,9 @@ static int max98396_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max98396_i2c_id[] = { - { "max98396", CODEC_TYPE_MAX98396}, - { "max98397", CODEC_TYPE_MAX98397}, - { }, + { .name = "max98396", .driver_data = CODEC_TYPE_MAX98396 }, + { .name = "max98397", .driver_data = CODEC_TYPE_MAX98397 }, + { } }; MODULE_DEVICE_TABLE(i2c, max98396_i2c_id); diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index 1fcbc64a2771..9bd6a61212c1 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -321,7 +321,7 @@ static int max9850_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max9850_i2c_id[] = { - { "max9850" }, + { .name = "max9850" }, { } }; MODULE_DEVICE_TABLE(i2c, max9850_i2c_id); diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index c94142768c81..8b2620eaf9b0 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -363,7 +363,7 @@ MODULE_DEVICE_TABLE(of, max98504_of_match); #endif static const struct i2c_device_id max98504_i2c_id[] = { - { "max98504" }, + { .name = "max98504" }, { } }; MODULE_DEVICE_TABLE(i2c, max98504_i2c_id); diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index 5bc3d95ade5a..4fb98505db1a 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -734,8 +734,8 @@ static int max98520_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max98520_i2c_id[] = { - { "max98520"}, - { }, + { .name = "max98520" }, + { } }; MODULE_DEVICE_TABLE(i2c, max98520_i2c_id); diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 716d16daf7d7..0d7ac37850bb 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -709,7 +709,7 @@ static void max9860_remove(struct i2c_client *i2c) } static const struct i2c_device_id max9860_i2c_id[] = { - { "max9860", }, + { .name = "max9860" }, { } }; MODULE_DEVICE_TABLE(i2c, max9860_i2c_id); diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index 9cad9b698cf2..07a53ec2a18a 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -689,7 +689,7 @@ static int max9867_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max9867_i2c_id[] = { - { "max9867" }, + { .name = "max9867" }, { } }; MODULE_DEVICE_TABLE(i2c, max9867_i2c_id); diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 1bd0d4761ca6..7cd07b6f9dd6 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -151,7 +151,7 @@ static int max9877_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id max9877_i2c_id[] = { - { "max9877" }, + { .name = "max9877" }, { } }; MODULE_DEVICE_TABLE(i2c, max9877_i2c_id); diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index 124af6408d96..4302ab16a642 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -617,7 +617,7 @@ static int max98925_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id max98925_i2c_id[] = { - { "max98925" }, + { .name = "max98925" }, { } }; MODULE_DEVICE_TABLE(i2c, max98925_i2c_id); diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index ae962bda163e..5305e1f9d97f 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -565,7 +565,7 @@ err_out: } static const struct i2c_device_id max98926_i2c_id[] = { - { "max98926" }, + { .name = "max98926" }, { } }; MODULE_DEVICE_TABLE(i2c, max98926_i2c_id); diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 0e9b8970997c..65e6fdb30eec 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -873,8 +873,8 @@ static void max98927_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id max98927_i2c_id[] = { - { "max98927"}, - { }, + { .name = "max98927" }, + { } }; MODULE_DEVICE_TABLE(i2c, max98927_i2c_id); diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index fad0cc902346..8a14626d43ff 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -573,7 +573,7 @@ static int ml26124_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ml26124_i2c_id[] = { - { "ml26124" }, + { .name = "ml26124" }, { } }; MODULE_DEVICE_TABLE(i2c, ml26124_i2c_id); diff --git a/sound/soc/codecs/mt6351.c b/sound/soc/codecs/mt6351.c index 2a5e963fb2b5..1768c249650d 100644 --- a/sound/soc/codecs/mt6351.c +++ b/sound/soc/codecs/mt6351.c @@ -8,7 +8,6 @@ #include <linux/dma-mapping.h> #include <linux/platform_device.h> #include <linux/slab.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/delay.h> diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c index a787accb88e8..ed8cbc63fa77 100644 --- a/sound/soc/codecs/mt6358.c +++ b/sound/soc/codecs/mt6358.c @@ -6,7 +6,6 @@ // Author: KaiChieh Chuang <kaichieh.chuang@mediatek.com> #include <linux/platform_device.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/delay.h> #include <linux/kthread.h> diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index ef63fd113cb7..21741afc80ef 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -557,8 +557,8 @@ static const struct of_device_id __maybe_unused mt6660_of_id[] = { MODULE_DEVICE_TABLE(of, mt6660_of_id); static const struct i2c_device_id mt6660_i2c_id[] = { - {"mt6660" }, - {}, + { .name = "mt6660" }, + { } }; MODULE_DEVICE_TABLE(i2c, mt6660_i2c_id); diff --git a/sound/soc/codecs/nau8325.c b/sound/soc/codecs/nau8325.c index 58ef5c493835..236d94c29c24 100644 --- a/sound/soc/codecs/nau8325.c +++ b/sound/soc/codecs/nau8325.c @@ -877,7 +877,7 @@ err: } static const struct i2c_device_id nau8325_i2c_ids[] = { - { "nau8325" }, + { .name = "nau8325" }, { } }; MODULE_DEVICE_TABLE(i2c, nau8325_i2c_ids); diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index caf2edb23088..fefbd5722c00 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -965,7 +965,7 @@ static int nau8540_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id nau8540_i2c_ids[] = { - { "nau8540" }, + { .name = "nau8540" }, { } }; MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids); diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index 9870e62d372e..a050387dd485 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -896,9 +896,9 @@ static int nau8810_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id nau8810_i2c_id[] = { - { "nau8810" }, - { "nau8812" }, - { "nau8814" }, + { .name = "nau8810" }, + { .name = "nau8812" }, + { .name = "nau8814" }, { } }; MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id); diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index ffb526de0021..c45c5b864ea4 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1968,7 +1968,7 @@ static int nau8821_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id nau8821_i2c_ids[] = { - { "nau8821" }, + { .name = "nau8821" }, { } }; MODULE_DEVICE_TABLE(i2c, nau8821_i2c_ids); diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index a11759f85eac..830164e991a7 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -19,6 +19,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/slab.h> #include <sound/core.h> #include <sound/pcm.h> @@ -108,6 +109,10 @@ static const struct reg_default nau8822_reg_defaults[] = { { NAU8822_REG_OUTPUT_TIEOFF, 0x0000 }, }; +static const char * const nau8822_supply_names[NAU8822_NUM_SUPPLIES] = { + "vdda", "vddb", "vddc", "vddspk", +}; + static bool nau8822_readable_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -1056,6 +1061,7 @@ static int nau8822_suspend(struct snd_soc_component *component) struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); snd_soc_dapm_force_bias_level(dapm, SND_SOC_BIAS_OFF); + regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies); regcache_mark_dirty(nau8822->regmap); @@ -1066,6 +1072,15 @@ static int nau8822_resume(struct snd_soc_component *component) { struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + int ret = regulator_bulk_enable(NAU8822_NUM_SUPPLIES, nau8822->supplies); + + if (ret) { + dev_err(component->dev, + "Failed to enable regulators: %d\n", ret); + return ret; + } + + fsleep(100); regcache_sync(nau8822->regmap); @@ -1153,7 +1168,7 @@ static int nau8822_i2c_probe(struct i2c_client *i2c) { struct device *dev = &i2c->dev; struct nau8822 *nau8822 = dev_get_platdata(dev); - int ret; + int ret, i; if (!nau8822) { nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL); @@ -1167,6 +1182,13 @@ static int nau8822_i2c_probe(struct i2c_client *i2c) return dev_err_probe(&i2c->dev, PTR_ERR(nau8822->mclk), "Error getting mclk\n"); + for (i = 0; i < NAU8822_NUM_SUPPLIES; i++) + nau8822->supplies[i].supply = nau8822_supply_names[i]; + + ret = devm_regulator_bulk_get(dev, NAU8822_NUM_SUPPLIES, nau8822->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config); if (IS_ERR(nau8822->regmap)) { ret = PTR_ERR(nau8822->regmap); @@ -1175,25 +1197,42 @@ static int nau8822_i2c_probe(struct i2c_client *i2c) } nau8822->dev = dev; + ret = regulator_bulk_enable(NAU8822_NUM_SUPPLIES, nau8822->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + fsleep(100); + /* Reset the codec */ ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00); if (ret != 0) { dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); - return ret; + goto err_reg; } ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822, &nau8822_dai, 1); if (ret != 0) { dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); - return ret; + goto err_reg; } return 0; + +err_reg: + regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies); + return ret; +} + +static void nau8822_i2c_remove(struct i2c_client *i2c) +{ + struct nau8822 *nau8822 = i2c_get_clientdata(i2c); + + regulator_bulk_disable(NAU8822_NUM_SUPPLIES, nau8822->supplies); } static const struct i2c_device_id nau8822_i2c_id[] = { - { "nau8822" }, + { .name = "nau8822" }, { } }; MODULE_DEVICE_TABLE(i2c, nau8822_i2c_id); @@ -1212,6 +1251,7 @@ static struct i2c_driver nau8822_i2c_driver = { .of_match_table = of_match_ptr(nau8822_of_match), }, .probe = nau8822_i2c_probe, + .remove = nau8822_i2c_remove, .id_table = nau8822_i2c_id, }; module_i2c_driver(nau8822_i2c_driver); diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h index 13fe0a091e9e..24799c7b5931 100644 --- a/sound/soc/codecs/nau8822.h +++ b/sound/soc/codecs/nau8822.h @@ -211,6 +211,8 @@ struct nau8822_pll { int freq_out; }; +#define NAU8822_NUM_SUPPLIES 4 + /* Codec Private Data */ struct nau8822 { struct device *dev; @@ -219,6 +221,7 @@ struct nau8822 { struct nau8822_pll pll; int sysclk; int div_id; + struct regulator_bulk_data supplies[NAU8822_NUM_SUPPLIES]; }; #endif /* __NAU8822_H__ */ diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 6ce763762443..426a488ff2a3 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -2020,7 +2020,7 @@ static int nau8824_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id nau8824_i2c_ids[] = { - { "nau8824" }, + { .name = "nau8824" }, { } }; MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids); diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index dd3528537ae4..ae83b778bf93 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -712,8 +712,8 @@ static void nau8825_xtalk_measure(struct nau8825 *nau8825) /* In left headphone IMM state, read out left headphone * impedance measure result, and delay some time to wait * detection sine wave output finish. Then, we can calculate - * the cross talk suppresstion side tone according to the L/R - * headphone imedance. + * the cross talk suppression side tone according to the L/R + * headphone impedance. */ regmap_read(nau8825->regmap, NAU8825_REG_IMM_RMS_L, &nau8825->imp_rms[NAU8825_XTALK_HPL_R2L]); @@ -2237,7 +2237,7 @@ static void nau8825_component_remove(struct snd_soc_component *component) { struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); - /* Cancel and reset cross tak suppresstion detection funciton */ + /* Cancel and reset cross talk suppression detection function */ nau8825_xtalk_cancel(nau8825); } @@ -2651,7 +2651,7 @@ static int nau8825_set_bias_level(struct snd_soc_component *component, /* ground HPL/HPR, MICGRND1/2 */ regmap_update_bits(nau8825->regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf); - /* Cancel and reset cross talk detection funciton */ + /* Cancel and reset cross talk detection function */ nau8825_xtalk_cancel(nau8825); /* Turn off all interruptions before system shutdown. Keep the * interruption quiet before resume setup completes. @@ -2930,11 +2930,8 @@ static int nau8825_i2c_probe(struct i2c_client *i2c) &nau8825_dai, 1); } -static void nau8825_i2c_remove(struct i2c_client *client) -{} - static const struct i2c_device_id nau8825_i2c_ids[] = { - { "nau8825" }, + { .name = "nau8825" }, { } }; MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids); @@ -2962,7 +2959,6 @@ static struct i2c_driver nau8825_driver = { .acpi_match_table = ACPI_PTR(nau8825_acpi_match), }, .probe = nau8825_i2c_probe, - .remove = nau8825_i2c_remove, .id_table = nau8825_i2c_ids, }; module_i2c_driver(nau8825_driver); diff --git a/sound/soc/codecs/ntp8835.c b/sound/soc/codecs/ntp8835.c index 2b93bea11752..5837b9379fee 100644 --- a/sound/soc/codecs/ntp8835.c +++ b/sound/soc/codecs/ntp8835.c @@ -454,8 +454,8 @@ static int ntp8835_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ntp8835_i2c_id[] = { - { "ntp8835" }, - {} + { .name = "ntp8835" }, + { } }; MODULE_DEVICE_TABLE(i2c, ntp8835_i2c_id); diff --git a/sound/soc/codecs/ntp8918.c b/sound/soc/codecs/ntp8918.c index 5593d48ef696..a18d79aa80aa 100644 --- a/sound/soc/codecs/ntp8918.c +++ b/sound/soc/codecs/ntp8918.c @@ -370,8 +370,8 @@ static int ntp8918_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ntp8918_i2c_id[] = { - { "ntp8918" }, - {} + { .name = "ntp8918" }, + { } }; MODULE_DEVICE_TABLE(i2c, ntp8918_i2c_id); diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index f4e5f3133f2b..cb923cecb47f 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -290,8 +290,8 @@ static const struct snd_soc_component_driver soc_component_dev_pcm1681 = { }; static const struct i2c_device_id pcm1681_i2c_id[] = { - {"pcm1681"}, - {} + { .name = "pcm1681" }, + { } }; MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id); diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c index abadf4f8ed5e..4f1bb13fd4c3 100644 --- a/sound/soc/codecs/pcm1789-i2c.c +++ b/sound/soc/codecs/pcm1789-i2c.c @@ -41,7 +41,7 @@ MODULE_DEVICE_TABLE(of, pcm1789_of_match); #endif static const struct i2c_device_id pcm1789_i2c_ids[] = { - { "pcm1789" }, + { .name = "pcm1789" }, { } }; MODULE_DEVICE_TABLE(i2c, pcm1789_i2c_ids); diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c index effc1dd6df22..5337bcc7c62c 100644 --- a/sound/soc/codecs/pcm179x-i2c.c +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -38,7 +38,7 @@ MODULE_DEVICE_TABLE(of, pcm179x_of_match); #endif static const struct i2c_device_id pcm179x_i2c_ids[] = { - { "pcm179x" }, + { .name = "pcm179x" }, { } }; MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids); diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c index a50f9f6e39c1..3bd9d557e3c2 100644 --- a/sound/soc/codecs/pcm186x-i2c.c +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -23,10 +23,10 @@ static const struct of_device_id pcm186x_of_match[] = { MODULE_DEVICE_TABLE(of, pcm186x_of_match); static const struct i2c_device_id pcm186x_i2c_id[] = { - { "pcm1862", PCM1862 }, - { "pcm1863", PCM1863 }, - { "pcm1864", PCM1864 }, - { "pcm1865", PCM1865 }, + { .name = "pcm1862", .driver_data = PCM1862 }, + { .name = "pcm1863", .driver_data = PCM1863 }, + { .name = "pcm1864", .driver_data = PCM1864 }, + { .name = "pcm1865", .driver_data = PCM1865 }, { } }; MODULE_DEVICE_TABLE(i2c, pcm186x_i2c_id); diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c index ff18c74b616c..dd24027836b2 100644 --- a/sound/soc/codecs/pcm3168a-i2c.c +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -10,7 +10,6 @@ #include <linux/i2c.h> #include <linux/init.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <sound/soc.h> @@ -33,7 +32,7 @@ static void pcm3168a_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id pcm3168a_i2c_id[] = { - { "pcm3168a", }, + { .name = "pcm3168a" }, { } }; MODULE_DEVICE_TABLE(i2c, pcm3168a_i2c_id); diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c index c8617a488b11..cb6a6f08f2f8 100644 --- a/sound/soc/codecs/pcm3168a.c +++ b/sound/soc/codecs/pcm3168a.c @@ -564,12 +564,6 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, static const u64 pcm3168a_dai_formats[] = { /* - * Select below from Sound Card, not here - * SND_SOC_DAIFMT_CBC_CFC - * SND_SOC_DAIFMT_CBP_CFP - */ - - /* * First Priority */ SND_SOC_POSSIBLE_DAIFMT_I2S | @@ -581,6 +575,8 @@ static const u64 pcm3168a_dai_formats[] = { * see * pcm3168a_hw_params() */ + SND_SOC_POSSIBLE_DAIFMT_I2S | + SND_SOC_POSSIBLE_DAIFMT_LEFT_J | SND_SOC_POSSIBLE_DAIFMT_RIGHT_J | SND_SOC_POSSIBLE_DAIFMT_DSP_A | SND_SOC_POSSIBLE_DAIFMT_DSP_B, @@ -799,7 +795,6 @@ int pcm3168a_probe(struct device *dev, struct regmap *regmap) pm_runtime_set_active(dev); pm_runtime_enable(dev); - pm_runtime_idle(dev); memcpy(pcm3168a->dai_drv, pcm3168a_dais, sizeof(pcm3168a->dai_drv)); ret = devm_snd_soc_register_component(dev, &pcm3168a_driver, @@ -822,15 +817,6 @@ err_clk: } EXPORT_SYMBOL_GPL(pcm3168a_probe); -static void pcm3168a_disable(struct device *dev) -{ - struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); - - regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), - pcm3168a->supplies); - clk_disable_unprepare(pcm3168a->scki); -} - void pcm3168a_remove(struct device *dev) { struct pcm3168a_priv *pcm3168a = dev_get_drvdata(dev); @@ -842,10 +828,12 @@ void pcm3168a_remove(struct device *dev) * The asserted level of GPIO_ACTIVE_LOW is LOW. */ gpiod_set_value_cansleep(pcm3168a->gpio_rst, 1); + pm_runtime_disable(dev); -#ifndef CONFIG_PM - pcm3168a_disable(dev); -#endif + if (!pm_runtime_status_suspended(dev)) { + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); + clk_disable_unprepare(pcm3168a->scki); + } } EXPORT_SYMBOL_GPL(pcm3168a_remove); @@ -900,13 +888,15 @@ static int pcm3168a_rt_suspend(struct device *dev) regcache_cache_only(pcm3168a->regmap, true); - pcm3168a_disable(dev); + regulator_bulk_disable(ARRAY_SIZE(pcm3168a->supplies), pcm3168a->supplies); + clk_disable_unprepare(pcm3168a->scki); return 0; } EXPORT_GPL_DEV_PM_OPS(pcm3168a_pm_ops) = { RUNTIME_PM_OPS(pcm3168a_rt_suspend, pcm3168a_rt_resume, NULL) + SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) }; MODULE_DESCRIPTION("PCM3168A codec driver"); diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index a1d849b0c50f..836865d11d22 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -35,13 +35,13 @@ static void pcm512x_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id pcm512x_i2c_id[] = { - { "pcm5121", }, - { "pcm5122", }, - { "pcm5141", }, - { "pcm5142", }, - { "pcm5242", }, - { "tas5754", }, - { "tas5756", }, + { .name = "pcm5121" }, + { .name = "pcm5122" }, + { .name = "pcm5141" }, + { .name = "pcm5142" }, + { .name = "pcm5242" }, + { .name = "tas5754" }, + { .name = "tas5756" }, { } }; MODULE_DEVICE_TABLE(i2c, pcm512x_i2c_id); diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c index fdef98ce52f1..fe3b5011fa16 100644 --- a/sound/soc/codecs/pcm512x.c +++ b/sound/soc/codecs/pcm512x.c @@ -633,7 +633,7 @@ static int pcm512x_dai_startup_slave(struct snd_pcm_substream *substream, struct regmap *regmap = pcm512x->regmap; if (IS_ERR(pcm512x->sclk)) { - dev_info(dev, "No SCLK, using BCLK: %ld\n", + dev_info_once(dev, "No SCLK, using BCLK: %ld\n", PTR_ERR(pcm512x->sclk)); /* Disable reporting of missing SCLK as an error */ diff --git a/sound/soc/codecs/pcm6240.c b/sound/soc/codecs/pcm6240.c index 78b21fbfad50..4ac4448ac3c8 100644 --- a/sound/soc/codecs/pcm6240.c +++ b/sound/soc/codecs/pcm6240.c @@ -27,28 +27,28 @@ #include "pcm6240.h" static const struct i2c_device_id pcmdevice_i2c_id[] = { - { "adc3120", ADC3120 }, - { "adc5120", ADC5120 }, - { "adc6120", ADC6120 }, - { "dix4192", DIX4192 }, - { "pcm1690", PCM1690 }, - { "pcm3120", PCM3120 }, - { "pcm3140", PCM3140 }, - { "pcm5120", PCM5120 }, - { "pcm5140", PCM5140 }, - { "pcm6120", PCM6120 }, - { "pcm6140", PCM6140 }, - { "pcm6240", PCM6240 }, - { "pcm6260", PCM6260 }, - { "pcm9211", PCM9211 }, - { "pcmd3140", PCMD3140 }, - { "pcmd3180", PCMD3180 }, - { "pcmd512x", PCMD512X }, - { "taa5212", TAA5212 }, - { "taa5412", TAA5412 }, - { "tad5212", TAD5212 }, - { "tad5412", TAD5412 }, - {} + { .name = "adc3120", .driver_data = ADC3120 }, + { .name = "adc5120", .driver_data = ADC5120 }, + { .name = "adc6120", .driver_data = ADC6120 }, + { .name = "dix4192", .driver_data = DIX4192 }, + { .name = "pcm1690", .driver_data = PCM1690 }, + { .name = "pcm3120", .driver_data = PCM3120 }, + { .name = "pcm3140", .driver_data = PCM3140 }, + { .name = "pcm5120", .driver_data = PCM5120 }, + { .name = "pcm5140", .driver_data = PCM5140 }, + { .name = "pcm6120", .driver_data = PCM6120 }, + { .name = "pcm6140", .driver_data = PCM6140 }, + { .name = "pcm6240", .driver_data = PCM6240 }, + { .name = "pcm6260", .driver_data = PCM6260 }, + { .name = "pcm9211", .driver_data = PCM9211 }, + { .name = "pcmd3140", .driver_data = PCMD3140 }, + { .name = "pcmd3180", .driver_data = PCMD3180 }, + { .name = "pcmd512x", .driver_data = PCMD512X }, + { .name = "taa5212", .driver_data = TAA5212 }, + { .name = "taa5412", .driver_data = TAA5412 }, + { .name = "tad5212", .driver_data = TAD5212 }, + { .name = "tad5412", .driver_data = TAD5412 }, + { } }; MODULE_DEVICE_TABLE(i2c, pcmdevice_i2c_id); @@ -1230,15 +1230,11 @@ static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt, int *status) { struct pcmdevice_priv *pcm_dev = (struct pcmdevice_priv *)ctxt; - struct pcmdevice_config_info *cfg_info; + struct pcmdevice_config_info *cfg_info = NULL; struct pcmdevice_block_data **bk_da; + char cfg_name[64] = {}; unsigned int config_offset = 0, i; - - cfg_info = kzalloc_obj(struct pcmdevice_config_info); - if (!cfg_info) { - *status = -ENOMEM; - goto out; - } + unsigned int nblocks; if (pcm_dev->regbin.fw_hdr.binary_version_num >= 0x105) { if (config_offset + 64 > (int)config_size) { @@ -1247,7 +1243,7 @@ static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt, "%s: cfg_name out of boundary\n", __func__); goto out; } - memcpy(cfg_info->cfg_name, &config_data[config_offset], 64); + memcpy(cfg_name, &config_data[config_offset], 64); config_offset += 64; } @@ -1257,16 +1253,17 @@ static struct pcmdevice_config_info *pcmdevice_add_config(void *ctxt, __func__); goto out; } - cfg_info->nblocks = - get_unaligned_be32(&config_data[config_offset]); + nblocks = get_unaligned_be32(&config_data[config_offset]); config_offset += 4; - bk_da = cfg_info->blk_data = kzalloc_objs(struct pcmdevice_block_data *, - cfg_info->nblocks); - if (!bk_da) { + cfg_info = kzalloc_flex(*cfg_info, blk_data, nblocks); + if (!cfg_info) { *status = -ENOMEM; goto out; } + cfg_info->nblocks = nblocks; + memcpy(cfg_info->cfg_name, cfg_name, sizeof(cfg_info->cfg_name)); + bk_da = cfg_info->blk_data; cfg_info->real_nblocks = 0; for (i = 0; i < cfg_info->nblocks; i++) { if (config_offset + 12 > config_size) { @@ -1449,14 +1446,11 @@ static void pcmdevice_config_info_remove(void *ctxt) for (i = 0; i < regbin->ncfgs; i++) { if (!cfg_info[i]) continue; - if (cfg_info[i]->blk_data) { - for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) { - if (!cfg_info[i]->blk_data[j]) - continue; - kfree(cfg_info[i]->blk_data[j]->regdata); - kfree(cfg_info[i]->blk_data[j]); - } - kfree(cfg_info[i]->blk_data); + for (j = 0; j < (int)cfg_info[i]->real_nblocks; j++) { + if (!cfg_info[i]->blk_data[j]) + continue; + kfree(cfg_info[i]->blk_data[j]->regdata); + kfree(cfg_info[i]->blk_data[j]); } kfree(cfg_info[i]); } diff --git a/sound/soc/codecs/pcm6240.h b/sound/soc/codecs/pcm6240.h index 2d8f9e798139..e27afd33910c 100644 --- a/sound/soc/codecs/pcm6240.h +++ b/sound/soc/codecs/pcm6240.h @@ -199,7 +199,7 @@ struct pcmdevice_config_info { unsigned int nblocks; unsigned int real_nblocks; unsigned char active_dev; - struct pcmdevice_block_data **blk_data; + struct pcmdevice_block_data *blk_data[] __counted_by(nblocks); }; struct pcmdevice_regbin { diff --git a/sound/soc/codecs/peb2466.c b/sound/soc/codecs/peb2466.c index 2d5163c15d0d..2d71d204d8fa 100644 --- a/sound/soc/codecs/peb2466.c +++ b/sound/soc/codecs/peb2466.c @@ -817,18 +817,17 @@ static int peb2466_dai_startup(struct snd_pcm_substream *substream, &peb2466_sample_bits_constr); } -static const u64 peb2466_dai_formats[] = { +static const u64 peb2466_dai_formats = SND_SOC_POSSIBLE_DAIFMT_DSP_A | - SND_SOC_POSSIBLE_DAIFMT_DSP_B, -}; + SND_SOC_POSSIBLE_DAIFMT_DSP_B; static const struct snd_soc_dai_ops peb2466_dai_ops = { .startup = peb2466_dai_startup, .hw_params = peb2466_dai_hw_params, .set_tdm_slot = peb2466_dai_set_tdm_slot, .set_fmt = peb2466_dai_set_fmt, - .auto_selectable_formats = peb2466_dai_formats, - .num_auto_selectable_formats = ARRAY_SIZE(peb2466_dai_formats), + .auto_selectable_formats = &peb2466_dai_formats, + .num_auto_selectable_formats = 1, }; static struct snd_soc_dai_driver peb2466_dai_driver = { diff --git a/sound/soc/codecs/pm4125.c b/sound/soc/codecs/pm4125.c index 1f0a3f5389f1..29655175ea28 100644 --- a/sound/soc/codecs/pm4125.c +++ b/sound/soc/codecs/pm4125.c @@ -1309,17 +1309,12 @@ static int pm4125_irq_init(struct pm4125_priv *pm4125, struct device *dev) static int pm4125_soc_codec_probe(struct snd_soc_component *component) { struct pm4125_priv *pm4125 = snd_soc_component_get_drvdata(component); - struct sdw_slave *tx_sdw_dev = pm4125->tx_sdw_dev; struct device *dev = component->dev; - unsigned long time_left; int i, ret; - time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, - msecs_to_jiffies(5000)); - if (!time_left) { - dev_err(dev, "soundwire device init timeout\n"); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(pm4125->tx_sdw_dev, 5000); + if (ret) + return ret; snd_soc_component_init_regmap(component, pm4125->regmap); ret = pm_runtime_resume_and_get(dev); diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index 9697aefc6e03..5871b5a81975 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -425,7 +425,6 @@ static int rk3328_platform_probe(struct platform_device *pdev) struct rk3328_codec_priv *rk3328; struct regmap *grf; void __iomem *base; - int ret = 0; rk3328 = devm_kzalloc(&pdev->dev, sizeof(*rk3328), GFP_KERNEL); if (!rk3328) @@ -441,14 +440,13 @@ static int rk3328_platform_probe(struct platform_device *pdev) regmap_write(grf, RK3328_GRF_SOC_CON2, (BIT(14) << 16 | BIT(14))); - ret = of_property_read_u32(rk3328_np, "spk-depop-time-ms", - &rk3328->spk_depop_time); - if (ret < 0) { + if (of_property_read_u32(rk3328_np, "spk-depop-time-ms", + &rk3328->spk_depop_time)) { dev_info(&pdev->dev, "spk_depop_time use default value.\n"); rk3328->spk_depop_time = 200; } - rk3328->mute = gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH); + rk3328->mute = devm_gpiod_get_optional(&pdev->dev, "mute", GPIOD_OUT_HIGH); if (IS_ERR(rk3328->mute)) return PTR_ERR(rk3328->mute); /* @@ -461,57 +459,31 @@ static int rk3328_platform_probe(struct platform_device *pdev) regmap_write(grf, RK3328_GRF_SOC_CON10, BIT(17) | BIT(1)); } - rk3328->mclk = devm_clk_get(&pdev->dev, "mclk"); + rk3328->mclk = devm_clk_get_enabled(&pdev->dev, "mclk"); if (IS_ERR(rk3328->mclk)) return PTR_ERR(rk3328->mclk); - ret = clk_prepare_enable(rk3328->mclk); - if (ret) - return ret; clk_set_rate(rk3328->mclk, INITIAL_FREQ); - rk3328->pclk = devm_clk_get(&pdev->dev, "pclk"); - if (IS_ERR(rk3328->pclk)) { - dev_err(&pdev->dev, "can't get acodec pclk\n"); - ret = PTR_ERR(rk3328->pclk); - goto err_unprepare_mclk; - } - - ret = clk_prepare_enable(rk3328->pclk); - if (ret < 0) { - dev_err(&pdev->dev, "failed to enable acodec pclk\n"); - goto err_unprepare_mclk; - } + rk3328->pclk = devm_clk_get_enabled(&pdev->dev, "pclk"); + if (IS_ERR(rk3328->pclk)) + return dev_err_probe(&pdev->dev, PTR_ERR(rk3328->pclk), + "failed to get or enable acodec pclk\n"); base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) { - ret = PTR_ERR(base); - goto err_unprepare_pclk; - } + if (IS_ERR(base)) + return PTR_ERR(base); rk3328->regmap = devm_regmap_init_mmio(&pdev->dev, base, &rk3328_codec_regmap_config); - if (IS_ERR(rk3328->regmap)) { - ret = PTR_ERR(rk3328->regmap); - goto err_unprepare_pclk; - } + if (IS_ERR(rk3328->regmap)) + return PTR_ERR(rk3328->regmap); platform_set_drvdata(pdev, rk3328); - ret = devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328, + return devm_snd_soc_register_component(&pdev->dev, &soc_codec_rk3328, rk3328_dai, ARRAY_SIZE(rk3328_dai)); - if (ret) - goto err_unprepare_pclk; - - return 0; - -err_unprepare_pclk: - clk_disable_unprepare(rk3328->pclk); - -err_unprepare_mclk: - clk_disable_unprepare(rk3328->mclk); - return ret; } static const struct of_device_id rk3328_codec_of_match[] __maybe_unused = { diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index 03f31d9d916e..15488a059283 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -2201,7 +2201,7 @@ MODULE_DEVICE_TABLE(acpi, rt1011_acpi_match); #endif static const struct i2c_device_id rt1011_i2c_id[] = { - { "rt1011" }, + { .name = "rt1011" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1011_i2c_id); diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index ca1ed9d5a24e..ff0c08ff610e 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -1094,7 +1094,7 @@ static const struct regmap_config rt1015_regmap = { }; static const struct i2c_device_id rt1015_i2c_id[] = { - { "rt1015" }, + { .name = "rt1015" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1015_i2c_id); diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index 9f86f071fca8..11c8c45574a1 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -608,7 +608,7 @@ static const struct regmap_config rt1016_regmap = { }; static const struct i2c_device_id rt1016_i2c_id[] = { - { "rt1016" }, + { .name = "rt1016" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1016_i2c_id); diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index 148b36173a25..95405cea8143 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/pm_runtime.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <sound/core.h> @@ -773,25 +772,17 @@ static int rt1017_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt1017->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT1017_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + ret = sdw_slave_wait_for_init(slave, RT1017_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt1017->regmap, false); regcache_sync(rt1017->regmap); diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index 86539c6f6cc1..370521149ac8 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -540,7 +540,7 @@ static const struct regmap_config rt1019_regmap = { }; static const struct i2c_device_id rt1019_i2c_id[] = { - { "rt1019" }, + { .name = "rt1019" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id); diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index 26b7382f97ef..2d5fad76cf52 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -981,8 +981,8 @@ MODULE_DEVICE_TABLE(acpi, rt1305_acpi_match); #endif static const struct i2c_device_id rt1305_i2c_id[] = { - { "rt1305" }, - { "rt1306" }, + { .name = "rt1305" }, + { .name = "rt1306" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1305_i2c_id); diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index e077d096bc23..2f30497498c7 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/pm_runtime.h> -#include <linux/mod_devicetable.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_registers.h> @@ -768,25 +767,17 @@ static int rt1308_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt1308->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT1308_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + ret = sdw_slave_wait_for_init(slave, RT1308_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt1308->regmap, false); regcache_sync_region(rt1308->regmap, 0xc000, 0xcfff); diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index df50b38c24b9..630946cd7cdf 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -795,7 +795,7 @@ MODULE_DEVICE_TABLE(acpi, rt1308_acpi_match); #endif static const struct i2c_device_id rt1308_i2c_id[] = { - { "rt1308" }, + { .name = "rt1308" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1308_i2c_id); diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 20fc1579eb9c..ca318dbd946e 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/pm_runtime.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <sound/core.h> @@ -745,25 +744,17 @@ static int rt1316_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt1316->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT1316_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + ret = sdw_slave_wait_for_init(slave, RT1316_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt1316->regmap, false); regcache_sync(rt1316->regmap); diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index d28f1afe68f1..c038ac0e3b76 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/pm_runtime.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/dmi.h> @@ -821,23 +820,15 @@ static int rt1318_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt1318->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT1318_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(slave, RT1318_PROBE_TIMEOUT); + if (ret) + return ret; -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt1318->regmap, false); regcache_sync(rt1318->regmap); diff --git a/sound/soc/codecs/rt1318.c b/sound/soc/codecs/rt1318.c index a80643099644..d13fd0e14125 100644 --- a/sound/soc/codecs/rt1318.c +++ b/sound/soc/codecs/rt1318.c @@ -1139,7 +1139,7 @@ static const struct regmap_config rt1318_regmap = { }; static const struct i2c_device_id rt1318_i2c_id[] = { - { "rt1318" }, + { .name = "rt1318" }, { } }; MODULE_DEVICE_TABLE(i2c, rt1318_i2c_id); diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index 192faa431b5e..1e930b27c67a 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/pm_runtime.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> #include <linux/dmi.h> @@ -3053,23 +3052,15 @@ static int rt1320_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt1320_sdw_priv *rt1320 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt1320->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT1320_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(slave, RT1320_PROBE_TIMEOUT); + if (ret) + return ret; -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt1320->regmap, false); regcache_sync(rt1320->regmap); regcache_cache_only(rt1320->mbq_regmap, false); diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index bba714020c70..63b5fc439773 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1098,8 +1098,8 @@ MODULE_DEVICE_TABLE(of, rt274_of_match); #endif static const struct i2c_device_id rt274_i2c_id[] = { - {"rt274"}, - {} + { .name = "rt274" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt274_i2c_id); diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index 195658f626cc..ded0ea332480 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1077,9 +1077,9 @@ static const struct regmap_config rt286_regmap = { }; static const struct i2c_device_id rt286_i2c_id[] = { - {"rt286"}, - {"rt288"}, - {} + { .name = "rt286" }, + { .name = "rt288" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt286_i2c_id); diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index 7d532a5a5f73..5414a1712b57 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1138,8 +1138,8 @@ static const struct regmap_config rt298_regmap = { }; static const struct i2c_device_id rt298_i2c_id[] = { - {"rt298"}, - {} + { .name = "rt298" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt298_i2c_id); diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index 649b44b790b0..00a4a208d2fa 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1200,7 +1200,7 @@ static const struct regmap_config rt5514_regmap = { }; static const struct i2c_device_id rt5514_i2c_id[] = { - { "rt5514" }, + { .name = "rt5514" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id); diff --git a/sound/soc/codecs/rt5575-spi.c b/sound/soc/codecs/rt5575-spi.c index 9a349965435b..d5b3a57c8866 100644 --- a/sound/soc/codecs/rt5575-spi.c +++ b/sound/soc/codecs/rt5575-spi.c @@ -17,7 +17,7 @@ struct rt5575_spi_burst_write { u8 cmd; - u32 addr; + __le32 addr; u8 data[RT5575_SPI_BUF_LEN]; u8 dummy; } __packed; diff --git a/sound/soc/codecs/rt5575.c b/sound/soc/codecs/rt5575.c index 24e41af29689..5c3e60eaa6a4 100644 --- a/sound/soc/codecs/rt5575.c +++ b/sound/soc/codecs/rt5575.c @@ -325,7 +325,7 @@ static int rt5575_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id rt5575_i2c_id[] = { - { "rt5575" }, + { .name = "rt5575" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5575_i2c_id); diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index fb9cf127e3ff..46ee412dc81d 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1321,7 +1321,7 @@ static const struct regmap_config rt5616_regmap = { }; static const struct i2c_device_id rt5616_i2c_id[] = { - { "rt5616" }, + { .name = "rt5616" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5616_i2c_id); @@ -1386,9 +1386,6 @@ static int rt5616_i2c_probe(struct i2c_client *i2c) rt5616_dai, ARRAY_SIZE(rt5616_dai)); } -static void rt5616_i2c_remove(struct i2c_client *i2c) -{} - static void rt5616_i2c_shutdown(struct i2c_client *client) { struct rt5616_priv *rt5616 = i2c_get_clientdata(client); @@ -1403,7 +1400,6 @@ static struct i2c_driver rt5616_i2c_driver = { .of_match_table = of_match_ptr(rt5616_of_match), }, .probe = rt5616_i2c_probe, - .remove = rt5616_i2c_remove, .shutdown = rt5616_i2c_shutdown, .id_table = rt5616_i2c_id, }; diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 19c6d8f760d9..ed1233973eb8 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1671,8 +1671,8 @@ static const struct snd_soc_component_driver soc_component_dev_rt5631 = { }; static const struct i2c_device_id rt5631_i2c_id[] = { - { "rt5631" }, - { "alc5631" }, + { .name = "rt5631" }, + { .name = "alc5631" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id); @@ -1722,16 +1722,12 @@ static int rt5631_i2c_probe(struct i2c_client *i2c) return ret; } -static void rt5631_i2c_remove(struct i2c_client *client) -{} - static struct i2c_driver rt5631_i2c_driver = { .driver = { .name = "rt5631", .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), }, - .probe = rt5631_i2c_probe, - .remove = rt5631_i2c_remove, + .probe = rt5631_i2c_probe, .id_table = rt5631_i2c_id, }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index f6c6294e1588..74fd05176eff 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2397,7 +2397,7 @@ static void rt5640_jack_work(struct work_struct *work) * disabled the OVCD IRQ, the IRQ pin will stay high and as * we react to edges, we miss the unplug event -> recheck. */ - queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0); } } @@ -2410,7 +2410,8 @@ static irqreturn_t rt5640_irq(int irq, void *data) delay = 100; if (rt5640->jack) - mod_delayed_work(system_long_wq, &rt5640->jack_work, delay); + mod_delayed_work(system_dfl_long_wq, &rt5640->jack_work, + delay); return IRQ_HANDLED; } @@ -2419,7 +2420,7 @@ static irqreturn_t rt5640_jd_gpio_irq(int irq, void *data) { struct rt5640_priv *rt5640 = data; - queue_delayed_work(system_long_wq, &rt5640->jack_work, + queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, msecs_to_jiffies(JACK_SETTLE_TIME)); return IRQ_HANDLED; @@ -2553,10 +2554,12 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640->jd_gpio = jack_data->jd_gpio; rt5640->jd_gpio_irq = gpiod_to_irq(rt5640->jd_gpio); - ret = request_irq(rt5640->jd_gpio_irq, rt5640_jd_gpio_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "rt5640-jd-gpio", rt5640); - if (ret) { + ret = request_any_context_irq(rt5640->jd_gpio_irq, + rt5640_jd_gpio_irq, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING, + "rt5640-jd-gpio", rt5640); + if (ret < 0) { dev_warn(component->dev, "Failed to request jd GPIO IRQ %d: %d\n", rt5640->jd_gpio_irq, ret); rt5640_disable_jack_detect(component); @@ -2568,10 +2571,10 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, if (jack_data && jack_data->use_platform_clock) rt5640->use_platform_clock = jack_data->use_platform_clock; - ret = request_irq(rt5640->irq, rt5640_irq, - IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, - "rt5640", rt5640); - if (ret) { + ret = request_any_context_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "rt5640", rt5640); + if (ret < 0) { dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640_disable_jack_detect(component); return; @@ -2579,7 +2582,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component, rt5640->irq_requested = true; /* sync initial jack state */ - queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0); } static const struct snd_soc_dapm_route rt5640_hda_jack_dapm_routes[] = { @@ -2622,9 +2625,9 @@ static void rt5640_enable_hda_jack_detect( rt5640->jack = jack; - ret = request_irq(rt5640->irq, rt5640_irq, - IRQF_TRIGGER_RISING, "rt5640", rt5640); - if (ret) { + ret = request_any_context_irq(rt5640->irq, rt5640_irq, + IRQF_TRIGGER_RISING, "rt5640", rt5640); + if (ret < 0) { dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret); rt5640->jack = NULL; return; @@ -2632,7 +2635,7 @@ static void rt5640_enable_hda_jack_detect( rt5640->irq_requested = true; /* sync initial jack state */ - queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0); snd_soc_dapm_add_routes(dapm, rt5640_hda_jack_dapm_routes, ARRAY_SIZE(rt5640_hda_jack_dapm_routes)); @@ -2860,7 +2863,7 @@ static int rt5640_resume(struct snd_soc_component *component) } enable_irq(rt5640->irq); - queue_delayed_work(system_long_wq, &rt5640->jack_work, 0); + queue_delayed_work(system_dfl_long_wq, &rt5640->jack_work, 0); } return 0; @@ -2958,9 +2961,9 @@ static const struct regmap_config rt5640_regmap = { }; static const struct i2c_device_id rt5640_i2c_id[] = { - { "rt5640" }, - { "rt5639" }, - { "rt5642" }, + { .name = "rt5640" }, + { .name = "rt5639" }, + { .name = "rt5642" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5640_i2c_id); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index f7701b8d0d3c..e9819653b30d 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -83,6 +83,8 @@ static const struct reg_sequence rt5650_init_list[] = { {RT5645_PWR_ANLG1, 0x02}, {RT5645_IL_CMD3, 0x6728}, {RT5645_PR_BASE + 0x3a, 0x0000}, + {RT5645_CLSD_OUT_CTRL1, 0x4059}, + {RT5645_GEN_CTRL3, 0x0200}, }; static const struct reg_default rt5645_reg[] = { @@ -1855,13 +1857,9 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w, RT5645_PWR_CLS_D_L, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | RT5645_PWR_CLS_D_L); - snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, - RT5645_DET_CLK_MASK, RT5645_DET_CLK_MODE1); break; case SND_SOC_DAPM_PRE_PMD: - snd_soc_component_update_bits(component, RT5645_GEN_CTRL3, - RT5645_DET_CLK_MASK, RT5645_DET_CLK_DIS); snd_soc_component_write(component, RT5645_EQ_CTRL2, 0); snd_soc_component_update_bits(component, RT5645_PWR_DIG1, RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R | @@ -3646,8 +3644,8 @@ static const struct regmap_config temp_regmap = { }; static const struct i2c_device_id rt5645_i2c_id[] = { - { "rt5645" }, - { "rt5650" }, + { .name = "rt5645" }, + { .name = "rt5650" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id); diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h index bef74b29fd54..a5bfe9861b8b 100644 --- a/sound/soc/codecs/rt5645.h +++ b/sound/soc/codecs/rt5645.h @@ -118,6 +118,7 @@ #define RT5645_A_JD_CTRL1 0x94 #define RT5645_VAD_CTRL4 0x9d #define RT5645_CLSD_OUT_CTRL 0xa0 +#define RT5645_CLSD_OUT_CTRL1 0xa1 /* Function - Digital */ #define RT5645_ADC_EQ_CTRL1 0xae #define RT5645_ADC_EQ_CTRL2 0xaf diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index 23c4bf3da298..5c729135f71b 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2202,7 +2202,7 @@ MODULE_DEVICE_TABLE(acpi, rt5651_acpi_match); #endif static const struct i2c_device_id rt5651_i2c_id[] = { - { "rt5651" }, + { .name = "rt5651" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5651_i2c_id); diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 3590ebd41c27..3097ee6d4e89 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -3814,8 +3814,8 @@ static const struct regmap_config rt5659_regmap = { }; static const struct i2c_device_id rt5659_i2c_id[] = { - { "rt5658" }, - { "rt5659" }, + { .name = "rt5658" }, + { .name = "rt5659" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5659_i2c_id); diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 84cdfb810c66..edf2e3e346e4 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1225,7 +1225,7 @@ static const struct regmap_config rt5660_regmap = { }; static const struct i2c_device_id rt5660_i2c_id[] = { - { "rt5660" }, + { .name = "rt5660" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index eee1c98cc4aa..262d3bba1f3d 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3307,8 +3307,8 @@ static const struct regmap_config temp_regmap = { }; static const struct i2c_device_id rt5663_i2c_id[] = { - { "rt5663" }, - {} + { .name = "rt5663" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id); diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 38fb3a277e26..48f57cd0920d 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4534,8 +4534,8 @@ static const struct regmap_config rt5665_regmap = { }; static const struct i2c_device_id rt5665_i2c_id[] = { - {"rt5665"}, - {} + { .name = "rt5665" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt5665_i2c_id); diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index c551696ae11a..fed6de40b8c8 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2334,8 +2334,8 @@ static const struct regmap_config rt5668_regmap = { }; static const struct i2c_device_id rt5668_i2c_id[] = { - {"rt5668b"}, - {} + { .name = "rt5668b" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt5668_i2c_id); diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index cb5d03bf4c7f..1d120b5dc2be 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2876,9 +2876,9 @@ static const struct regmap_config rt5670_regmap = { }; static const struct i2c_device_id rt5670_i2c_id[] = { - { "rt5670" }, - { "rt5671" }, - { "rt5672" }, + { .name = "rt5670" }, + { .name = "rt5671" }, + { .name = "rt5672" }, { } }; MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id); diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 60a93c3fe2e7..ac084ca008f3 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -5210,7 +5210,7 @@ static const struct acpi_device_id rt5677_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5677_acpi_match); static const struct i2c_device_id rt5677_i2c_id[] = { - { "rt5677", RT5677 }, + { .name = "rt5677", .driver_data = RT5677 }, { } }; MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id); diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index e556a365adc8..286cf0d17968 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -324,8 +324,8 @@ static const struct acpi_device_id rt5682_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5682_acpi_match); static const struct i2c_device_id rt5682_i2c_id[] = { - {"rt5682"}, - {} + { .name = "rt5682" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt5682_i2c_id); diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index fc464538ceff..dec8c2147d68 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -754,7 +754,7 @@ static int rt5682_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt5682_priv *rt5682 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt5682->first_hw_init) return 0; @@ -766,20 +766,14 @@ static int rt5682_dev_resume(struct device *dev) rt5682->disable_irq = false; } mutex_unlock(&rt5682->disable_irq_lock); - goto regmap_sync; } - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT5682_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + ret = sdw_slave_wait_for_init(slave, RT5682_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt5682->sdw_regmap, false); regcache_cache_only(rt5682->regmap, false); regcache_sync(rt5682->regmap); diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c index d39f8e4f3474..4b82e07d3b2c 100644 --- a/sound/soc/codecs/rt5682.c +++ b/sound/soc/codecs/rt5682.c @@ -2929,20 +2929,14 @@ static int rt5682_probe(struct snd_soc_component *component) { struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component); struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); - struct sdw_slave *slave; - unsigned long time; + int ret; rt5682->component = component; if (rt5682->is_sdw) { - slave = rt5682->slave; - time = wait_for_completion_timeout( - &slave->initialization_complete, - msecs_to_jiffies(RT5682_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(rt5682->slave, RT5682_PROBE_TIMEOUT); + if (ret) + return ret; } snd_soc_dapm_disable_pin(dapm, "MICBIAS"); diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 98de94a79260..3624067950c0 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -3325,8 +3325,8 @@ static const struct acpi_device_id rt5682s_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, rt5682s_acpi_match); static const struct i2c_device_id rt5682s_i2c_id[] = { - {"rt5682s"}, - {} + { .name = "rt5682s" }, + { } }; MODULE_DEVICE_TABLE(i2c, rt5682s_i2c_id); diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 9ce36a66fae1..a451d5d1f8ab 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> -#include <linux/mod_devicetable.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_registers.h> @@ -522,25 +521,17 @@ static int rt700_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt700_priv *rt700 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt700->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT700_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + ret = sdw_slave_wait_for_init(slave, RT700_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt700->regmap, false); regcache_sync_region(rt700->regmap, 0x3000, 0x8fff); regcache_sync_region(rt700->regmap, 0x752010, 0x75206b); diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 49dacceddf81..e028a1c3a9ac 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> -#include <linux/mod_devicetable.h> #include <linux/soundwire/sdw_registers.h> #include <linux/module.h> #include <linux/pm_runtime.h> @@ -438,7 +437,7 @@ static int rt711_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt711_sdca_priv *rt711 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt711->first_hw_init) return 0; @@ -451,20 +450,14 @@ static int rt711_sdca_dev_resume(struct device *dev) rt711->disable_irq = false; } mutex_unlock(&rt711->disable_irq_lock); - goto regmap_sync; } - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT711_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt711->regmap, false); regcache_sync(rt711->regmap); regcache_cache_only(rt711->mbq_regmap, false); diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 72ddf4cebdf3..a0c6a9efa840 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> -#include <linux/mod_devicetable.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_registers.h> @@ -530,7 +529,7 @@ static int rt711_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt711_priv *rt711 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt711->first_hw_init) return 0; @@ -542,18 +541,12 @@ static int rt711_dev_resume(struct device *dev) rt711->disable_irq = false; } mutex_unlock(&rt711->disable_irq_lock); - goto regmap_sync; } - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT711_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(slave, RT711_PROBE_TIMEOUT); + if (ret) + return ret; -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt711->regmap, false); regcache_sync_region(rt711->regmap, 0x3000, 0x8fff); regcache_sync_region(rt711->regmap, 0x752009, 0x752091); diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 4d83544ef204..85779547653e 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -7,7 +7,6 @@ // #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/pm_runtime.h> #include <linux/soundwire/sdw_registers.h> #include <linux/slab.h> @@ -905,26 +904,17 @@ static int rt712_sdca_dmic_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt712->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT712_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", - __func__); + ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt712->regmap, false); regcache_sync(rt712->regmap); regcache_cache_only(rt712->mbq_regmap, false); diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 8c82887174db..70d661ce2ef2 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/soundwire/sdw_registers.h> @@ -450,7 +449,7 @@ static int rt712_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt712->first_hw_init) return 0; @@ -464,20 +463,14 @@ static int rt712_sdca_dev_resume(struct device *dev) rt712->disable_irq = false; } mutex_unlock(&rt712->disable_irq_lock); - goto regmap_sync; } - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT712_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + ret = sdw_slave_wait_for_init(slave, RT712_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt712->regmap, false); regcache_sync(rt712->regmap); regcache_cache_only(rt712->mbq_regmap, false); diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c index 4796fce084ff..d6353af07380 100644 --- a/sound/soc/codecs/rt712-sdca.c +++ b/sound/soc/codecs/rt712-sdca.c @@ -1843,6 +1843,15 @@ static void rt712_sdca_vb_io_init(struct rt712_sdca_priv *rt712) } } +static void rt712_sdca_reset(struct rt712_sdca_priv *rt712) +{ + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_REG, + RT712_PARA_VERB_CTL, RT712_HIDDEN_REG_SW_RESET, + RT712_HIDDEN_REG_SW_RESET); + rt712_sdca_index_update_bits(rt712, RT712_VENDOR_HDA_CTL, + RT712_HDA_LEGACY_RESET_CTL, 0x1, 0x1); +} + int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) { struct rt712_sdca_priv *rt712 = dev_get_drvdata(dev); @@ -1870,6 +1879,8 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave) pm_runtime_get_noresume(&slave->dev); + rt712_sdca_reset(rt712); + rt712_sdca_index_read(rt712, RT712_VENDOR_REG, RT712_JD_PRODUCT_NUM, &val); rt712->hw_id = (val & 0xf000) >> 12; rt712->version_id = (val & 0x0f00) >> 8; diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index 968bc183b8d8..1b183b21a4b8 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -8,7 +8,6 @@ #include <linux/delay.h> #include <linux/device.h> -#include <linux/mod_devicetable.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_registers.h> @@ -224,25 +223,17 @@ static int rt715_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt715_sdca_priv *rt715 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt715->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT715_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, SDW_SDCA_CTL(FUN_JACK_CODEC, RT715_SDCA_ST_EN, RT715_SDCA_ST_CTRL, diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 49c91d015be4..f5ec348a9628 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -9,7 +9,6 @@ */ #include <linux/delay.h> #include <linux/device.h> -#include <linux/mod_devicetable.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> #include <linux/soundwire/sdw_registers.h> @@ -501,25 +500,17 @@ static int rt715_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt715_priv *rt715 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt715->first_hw_init) return 0; - if (!slave->unattach_request) - goto regmap_sync; - - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT715_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "%s: Initialization not complete, timed out\n", __func__); + ret = sdw_slave_wait_for_init(slave, RT715_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt715->regmap, false); regcache_sync_region(rt715->regmap, 0x3000, 0x8fff); regcache_sync_region(rt715->regmap, 0x752039, 0x752039); diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 6eb8512975b8..041b381e582b 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -9,7 +9,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/pm_runtime.h> #include <linux/soundwire/sdw_registers.h> @@ -489,7 +488,7 @@ static int rt721_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt721_sdca_priv *rt721 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt721->first_hw_init) return 0; @@ -502,20 +501,14 @@ static int rt721_sdca_dev_resume(struct device *dev) rt721->disable_irq = false; } mutex_unlock(&rt721->disable_irq_lock); - goto regmap_sync; } - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT721_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + ret = sdw_slave_wait_for_init(slave, RT721_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt721->regmap, false); regcache_sync(rt721->regmap); regcache_cache_only(rt721->mbq_regmap, false); diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index 0a5b3ffa90da..e68aa0350a5b 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -9,7 +9,6 @@ #include <linux/delay.h> #include <linux/device.h> #include <linux/module.h> -#include <linux/mod_devicetable.h> #include <linux/pm_runtime.h> #include <linux/soundwire/sdw_registers.h> @@ -19,7 +18,9 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) { switch (reg) { - case 0x2f01 ... 0x2f0a: + case 0x22f0 ... 0x22f1: + case 0x2f01 ... 0x2f0c: + case 0x2f21 ... 0x2f24: case 0x2f35 ... 0x2f36: case 0x2f50 ... 0x2f52: case 0x2f54: @@ -84,6 +85,21 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0): case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + case 0x44011000 ... 0x440115ff: + case 0x44012000: + case 0x44012021: + case 0x44012022: + case 0x44012025: + case 0x44021000 ... 0x440211ff: + case 0x44022000: + case 0x44022019: + case 0x4402201a: + case 0x4402201d: + case 0x44041000 ... 0x440415ff: + case 0x44042000: + case 0x44042019: + case 0x4404201a: + case 0x4404201d: return 1; case 0x2000000 ... 0x2000024: case 0x2000029 ... 0x200004a: @@ -95,8 +111,10 @@ static int rt722_sdca_mbq_size(struct device *dev, unsigned int reg) case 0x200007f: case 0x2000082 ... 0x200008e: case 0x2000090 ... 0x2000094: + case 0x20000b1: + case 0x20000b4: case 0x3110000: - case 0x5300000 ... 0x5300002: + case 0x5300000 ... 0x5300300: case 0x5400002: case 0x5600000 ... 0x5600007: case 0x5700000 ... 0x5700004: @@ -175,6 +193,7 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_ACTUAL_POWER_STATE, 0): case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: case 0x2000000: + case 0x2000007: case 0x200000d: case 0x2000019: case 0x2000020: @@ -186,6 +205,21 @@ static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) case 0x3110000: case 0x5800003: case 0x5810000: + case 0x44011000 ... 0x440115ff: + case 0x44012000: + case 0x44012021: + case 0x44012022: + case 0x44012025: + case 0x44021000 ... 0x440211ff: + case 0x44022000: + case 0x44022019: + case 0x4402201a: + case 0x4402201d: + case 0x44041000 ... 0x440415ff: + case 0x44042000: + case 0x44042019: + case 0x4404201a: + case 0x4404201d: return true; default: return false; @@ -501,7 +535,7 @@ static int rt722_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); - unsigned long time; + int ret; if (!rt722->first_hw_init) return 0; @@ -514,20 +548,14 @@ static int rt722_sdca_dev_resume(struct device *dev) rt722->disable_irq = false; } mutex_unlock(&rt722->disable_irq_lock); - goto regmap_sync; } - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(RT722_PROBE_TIMEOUT)); - if (!time) { - dev_err(&slave->dev, "Initialization not complete, timed out\n"); + ret = sdw_slave_wait_for_init(slave, RT722_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - - return -ETIMEDOUT; + return ret; } -regmap_sync: - slave->unattach_request = 0; regcache_cache_only(rt722->regmap, false); regcache_sync(rt722->regmap); return 0; diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c index 79b8b7e70a33..1b6729f363fc 100644 --- a/sound/soc/codecs/rt722-sdca.c +++ b/sound/soc/codecs/rt722-sdca.c @@ -7,19 +7,21 @@ // #include <linux/bitops.h> -#include <sound/core.h> #include <linux/delay.h> +#include <linux/dmi.h> +#include <linux/firmware.h> #include <linux/init.h> -#include <sound/initval.h> -#include <sound/jack.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/moduleparam.h> -#include <sound/pcm.h> #include <linux/pm_runtime.h> -#include <sound/pcm_params.h> -#include <linux/soundwire/sdw_registers.h> #include <linux/slab.h> +#include <linux/soundwire/sdw_registers.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> @@ -344,6 +346,280 @@ static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, return 0; } +static int rt722_cae_load(struct rt722_sdca_priv *rt722) +{ + struct device *dev = &rt722->slave->dev; + static const char func_tag[] = "FUNC"; + static const char xu_tag[] = "XU"; + const char *dmi_vendor, *dmi_product, *dmi_sku; + char *cae_filename; + const struct firmware *cae_fw = NULL; + unsigned int cae_st_spk, cae_st_hp, cae_st_mic; + unsigned int func, value; + unsigned int combined_val; + unsigned int addr, size; + unsigned int fw_offset; + unsigned char mbq_high_val = 0; + unsigned char *param_data; + unsigned char *fw_data; + char tag[5]; + char *space; + int v_len, p_len, s_len; + int ret = 0, i; + int retry = 50; + + dmi_vendor = dmi_get_system_info(DMI_SYS_VENDOR); + dmi_product = dmi_get_system_info(DMI_PRODUCT_NAME); + dmi_sku = dmi_get_system_info(DMI_PRODUCT_SKU); + + if (!dmi_vendor || !dmi_product || !dmi_sku) { + dev_warn(dev, "%s: Incomplete DMI info\n", __func__); + return -EINVAL; + } + space = strchr(dmi_vendor, ' '); + v_len = space ? space - dmi_vendor : strlen(dmi_vendor); + + space = strchr(dmi_product, ' '); + p_len = space ? space - dmi_product : strlen(dmi_product); + + space = strchr(dmi_sku, ' '); + s_len = space ? space - dmi_sku : strlen(dmi_sku); + + cae_filename = kasprintf(GFP_KERNEL, + "realtek/rt722/rt722_RAE_%.*s_%.*s_%.*s.dat", + v_len, dmi_vendor, + p_len, dmi_product, + s_len, dmi_sku); + if (!cae_filename) + return -ENOMEM; + dev_dbg(dev, "%s: try to load CAE file %s\n", __func__, cae_filename); + + regmap_write(rt722->regmap, RT722_SPK_CAE_PARAM1, 0x5f); + regmap_write(rt722->regmap, RT722_HP_CAE_PARAM39, 0x5f); + regmap_write(rt722->regmap, RT722_MIC_CAE_PARAM39, 0x5f); + usleep_range(50000, 60000); + + request_firmware(&cae_fw, cae_filename, dev); + kfree(cae_filename); + if (!cae_fw) { + dev_err(dev, "%s: Failed to load CAE firmware\n", __func__); + return -ENOENT; + } + + regmap_read(rt722->regmap, RT722_SPK_CAE_PARAM38, &cae_st_spk); + regmap_read(rt722->regmap, RT722_HP_CAE_PARAM68, &cae_st_hp); + regmap_read(rt722->regmap, RT722_MIC_CAE_PARAM99, &cae_st_mic); + cae_st_spk &= 0x80; + cae_st_hp &= 0x80; + cae_st_mic &= 0x80; + + dev_dbg(dev, "%s(%d) spk_crc:%x, hp_crc:%x, mic_crc:%x\n", + __func__, __LINE__, cae_st_spk, cae_st_hp, cae_st_mic); + + if (cae_st_spk) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_SPK, 0x0008, 0x0008); + else if (cae_st_hp) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_HP, 0x0008, 0x0008); + else if (cae_st_mic) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_DMIC, 0x0008, 0x0008); + + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x8000); + + regmap_update_bits(rt722->regmap, RT722_SPK_CAE_PARAM34, 0x1, 0x0); + regmap_update_bits(rt722->regmap, RT722_HP_CAE_PARAM64, 0x1, 0x0); + regmap_update_bits(rt722->regmap, RT722_MIC_CAE_PARAM95, 0x1, 0x0); + + while (--retry) { + regmap_read(rt722->regmap, RT722_SPK_CAE_PARAM35, &cae_st_spk); + regmap_read(rt722->regmap, RT722_HP_CAE_PARAM65, &cae_st_hp); + regmap_read(rt722->regmap, RT722_MIC_CAE_PARAM96, &cae_st_mic); + dev_dbg(dev, "%s(%d) cae_st_spk:%x, cae_st_hp:%x, cae_st_mic:%x\n", + __func__, __LINE__, cae_st_spk, cae_st_hp, cae_st_mic); + if ((cae_st_spk & 0x40) && (cae_st_hp & 0x40) && (cae_st_mic & 0x40)) + break; + usleep_range(1000, 1100); + } + + if (!retry && !((cae_st_spk & 0x40) && (cae_st_hp & 0x40) + && (cae_st_mic & 0x40))) { + dev_err(dev, "%s: CAE is not ready to be loaded.\n", __func__); + ret = -ETIMEDOUT; + goto out_release; + } + + dev_dbg(dev, "%s, cae_fw size=0x%zx, start\n", __func__, cae_fw->size); + + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_AMIC, 0x8000); + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_DMIC, 0x8004); + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_HP, 0x8074); + rt722_sdca_index_write(rt722, RT722_VENDOR_EQ_CAE, + RT722_EQ_CTRL_SPK, 0xa074); + + regcache_cache_bypass(rt722->regmap, true); + for (fw_offset = 0; fw_offset < cae_fw->size;) { + + if (fw_offset + 12 > cae_fw->size) { + dev_err(dev, "%s: Unexpected end of firmware\n", __func__); + ret = -EINVAL; + goto verify_abort; + } + + fw_data = (unsigned char *)&cae_fw->data[fw_offset]; + memcpy(tag, fw_data, 4); + tag[4] = '\0'; + + if (strcmp(tag, xu_tag) == 0) { + dev_dbg(dev, "%s: This is a XU tag", __func__); + memcpy(&addr, (fw_data + 4), 4); + memcpy(&size, (fw_data + 8), 4); + + if (size == 0 || size > cae_fw->size - fw_offset - 12) { + dev_err(dev, "%s: Invalid payload size: %u\n", __func__, size); + ret = -EINVAL; + goto verify_abort; + } + + param_data = (unsigned char *)(fw_data + 12); + + dev_dbg(dev, "%s: addr=0x%x, size=0x%x\n", __func__, addr, size); + + if ((addr <= 0x05302300 && addr >= 0x05300000) || + (addr <= 0x020020b4 && addr >= 0x020000b1)) { + if (addr & BIT(13)) { + mbq_high_val = param_data[0]; + dev_dbg(dev, "MBQ: High Byte 0x%02x\n", mbq_high_val); + fw_offset += (size + 12); + + continue; + } else { + regcache_cache_bypass(rt722->regmap, false); + combined_val = (mbq_high_val << 8) | param_data[0]; + if (addr == 0x20000b1 || addr == 0x20000b4) + combined_val |= (0x2 << 8); + ret = regmap_write(rt722->regmap, addr, combined_val); + if (ret) { + dev_err(dev, + "MBQ fail: addr=0x%x ret=%d\n", addr, ret); + regcache_cache_bypass(rt722->regmap, true); + goto verify_abort; + } + + dev_dbg(dev, "MBQ-reg=0x%x, value=0x%x\n", + addr, combined_val); + + fw_offset += (size + 12); + regcache_cache_bypass(rt722->regmap, true); + continue; + } + } + + for (i = 0; i < size; i++) { + ret = regmap_write(rt722->regmap, addr + i, param_data[i]); + if (ret) { + dev_err(dev, + "CAE write fail: addr=0x%x ret=%d\n", + addr + i, ret); + goto verify_abort; + } + } + fw_offset += (size + 12); + } else if (strcmp(tag, func_tag) == 0) { + dev_dbg(dev, "%s: This is a FUNC tag", __func__); + + memcpy(&func, (fw_data + 4), 4); + memcpy(&value, (fw_data + 8), 4); + dev_dbg(dev, "%s: func=0x%x, value=0x%x\n", + __func__, func, value); + + if (func == 1) + msleep(value); + + fw_offset += 12; + } else { + dev_err(dev, "%s: No XU/FUNC tag at fw_offset=0x%x\n", + __func__, fw_offset); + ret = -EINVAL; + goto verify_abort; + } + } + + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x0000); + regcache_cache_bypass(rt722->regmap, false); + rt722->cae_update_done = 1; + dev_dbg(dev, "%s: CAE FW update done.\n", __func__); + release_firmware(cae_fw); + return 0; + +verify_abort: + regcache_cache_bypass(rt722->regmap, false); + if (!ret) + ret = -EIO; +out_release: + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_REG, + RT722_MISC_CTRL1, 0x8000, 0x0000); + release_firmware(cae_fw); + dev_err(dev, "%s: CAE FW update aborted (ret=%d).\n", __func__, ret); + return ret; +} + +static int rt722_cae_update_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + int ret, changed = 0; + + if (!rt722->hw_init) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (ucontrol->value.integer.value[0]) { + if (snd_soc_dapm_get_bias_level(dapm) == SND_SOC_BIAS_OFF) { + ret = rt722_cae_load(rt722); + if (ret) { + dev_err(component->dev, "CAE load failed: %d\n", ret); + goto out; + } else + changed = 1; + } + } else { + if (rt722->cae_update_done) { + rt722->cae_update_done = 0; + changed = 1; + } + } + +out: + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return ret < 0 ? ret : changed; +} + +static int rt722_cae_update_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = rt722->cae_update_done; + + return 0; +} + + /* For SDCA control DAC/ADC Gain */ static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) @@ -517,6 +793,61 @@ static int rt722_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol, return changed; } +static int rt722_sdca_set_fu06_playback_ctl(struct rt722_sdca_priv *rt722) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt722->fu06_dapm_mute || rt722->fu06_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt722->fu06_dapm_mute || rt722->fu06_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt722_sdca_fu06_playback_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt722->fu06_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt722->fu06_mixer_r_mute; + return 0; +} + +static int rt722_sdca_fu06_playback_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int err, changed = 0; + + if (rt722->fu06_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt722->fu06_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt722->fu06_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt722->fu06_mixer_r_mute = !ucontrol->value.integer.value[1]; + + err = rt722_sdca_set_fu06_playback_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722) { int err; @@ -718,6 +1049,8 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = { RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0, rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv), /* AMP playback settings */ + SOC_DOUBLE_EXT("FU06 Playback Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt722_sdca_fu06_playback_get, rt722_sdca_fu06_playback_put), SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L), @@ -738,6 +1071,9 @@ static const struct snd_kcontrol_new rt722_sdca_controls[] = { RT722_SDCA_CTL_FU_CH_GAIN, CH_01), rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, 4, 3, boost_vol_tlv), + /* CAE firmware update */ + SOC_SINGLE_EXT("CAE Update", SND_SOC_NOPM, 0, 1, 0, + rt722_cae_update_get, rt722_cae_update_put), }; static const char * const adc22_mux_text[] = { @@ -807,27 +1143,17 @@ static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w, struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); - unsigned char unmute = 0x0, mute = 0x1; switch (event) { case SND_SOC_DAPM_POST_PMU: - regmap_write(rt722->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, - RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); - regmap_write(rt722->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, - RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + rt722->fu06_dapm_mute = false; break; case SND_SOC_DAPM_PRE_PMD: - regmap_write(rt722->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, - RT722_SDCA_CTL_FU_MUTE, CH_L), mute); - regmap_write(rt722->regmap, - SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, - RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + rt722->fu06_dapm_mute = true; break; } - return 0; + + return rt722_sdca_set_fu06_playback_ctl(rt722); } static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w, @@ -1324,9 +1650,12 @@ int rt722_sdca_init(struct device *dev, struct regmap *regmap, struct sdw_slave rt722->first_hw_init = false; rt722->fu1e_dapm_mute = true; rt722->fu0f_dapm_mute = true; + rt722->fu06_dapm_mute = true; + rt722->fu06_mixer_l_mute = rt722->fu06_mixer_r_mute = false; rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true; rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] = rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true; + rt722->cae_update_done = 0; return devm_snd_soc_register_component(dev, &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h index 823abee9ab76..b654b14334d4 100644 --- a/sound/soc/codecs/rt722-sdca.h +++ b/sound/soc/codecs/rt722-sdca.h @@ -36,10 +36,15 @@ struct rt722_sdca_priv { bool fu0f_dapm_mute; bool fu0f_mixer_l_mute; bool fu0f_mixer_r_mute; + /* For AMP */ + bool fu06_dapm_mute; + bool fu06_mixer_l_mute; + bool fu06_mixer_r_mute; /* For DMIC */ bool fu1e_dapm_mute; bool fu1e_mixer_mute[4]; int hw_vid; + int cae_update_done; }; struct rt722_sdca_dmic_kctrl_priv { @@ -51,6 +56,7 @@ struct rt722_sdca_dmic_kctrl_priv { /* NID */ #define RT722_VENDOR_REG 0x20 +#define RT722_VENDOR_EQ_CAE 0x53 #define RT722_VENDOR_CALI 0x58 #define RT722_VENDOR_SPK_EFUSE 0x5c #define RT722_VENDOR_IMS_DRE 0x5b @@ -60,6 +66,7 @@ struct rt722_sdca_dmic_kctrl_priv { /* Index (NID:20h) */ #define RT722_JD_PRODUCT_NUM 0x00 #define RT722_ANALOG_BIAS_CTL3 0x04 +#define RT722_MISC_CTRL1 0x07 #define RT722_JD_CTRL1 0x09 #define RT722_LDO2_3_CTL1 0x0e #define RT722_LDO1_CTL 0x1a @@ -75,6 +82,12 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_SW_CONFIG1 0x8a #define RT722_SW_CONFIG2 0x8b +/* Index (NID:53h) */ +#define RT722_EQ_CTRL_SPK 0x00 +#define RT722_EQ_CTRL_HP 0x100 +#define RT722_EQ_CTRL_DMIC 0x200 +#define RT722_EQ_CTRL_AMIC 0x300 + /* Index (NID:58h) */ #define RT722_DAC_DC_CALI_CTL0 0x00 #define RT722_DAC_DC_CALI_CTL1 0x01 @@ -152,6 +165,20 @@ struct rt722_sdca_dmic_kctrl_priv { #define RT722_BUF_ADDR_HID1 0x44030000 #define RT722_BUF_ADDR_HID2 0x44030020 +/* RT722 CAE parameter settings */ +#define RT722_SPK_CAE_PARAM1 0x44012000 +#define RT722_SPK_CAE_PARAM34 0x44012021 +#define RT722_SPK_CAE_PARAM35 0x44012022 +#define RT722_SPK_CAE_PARAM38 0x44012025 +#define RT722_HP_CAE_PARAM39 0x44022000 +#define RT722_HP_CAE_PARAM64 0x44022019 +#define RT722_HP_CAE_PARAM65 0x4402201a +#define RT722_HP_CAE_PARAM68 0x4402201d +#define RT722_MIC_CAE_PARAM39 0x44042000 +#define RT722_MIC_CAE_PARAM95 0x44042019 +#define RT722_MIC_CAE_PARAM96 0x4404201a +#define RT722_MIC_CAE_PARAM99 0x4404201d + /* RT722 SDCA Control - function number */ #define FUNC_NUM_JACK_CODEC 0x01 #define FUNC_NUM_MIC_ARRAY 0x02 diff --git a/sound/soc/codecs/rt9123.c b/sound/soc/codecs/rt9123.c index 84fd3d6861de..07eaf275c9e9 100644 --- a/sound/soc/codecs/rt9123.c +++ b/sound/soc/codecs/rt9123.c @@ -13,7 +13,6 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pm_runtime.h> diff --git a/sound/soc/codecs/rt9123p.c b/sound/soc/codecs/rt9123p.c index d509659e735b..584fcb78cd3f 100644 --- a/sound/soc/codecs/rt9123p.c +++ b/sound/soc/codecs/rt9123p.c @@ -9,7 +9,6 @@ #include <linux/err.h> #include <linux/gpio/consumer.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> diff --git a/sound/soc/codecs/rtq9124.c b/sound/soc/codecs/rtq9124.c index 186904b31434..2a041894bc0c 100644 --- a/sound/soc/codecs/rtq9124.c +++ b/sound/soc/codecs/rtq9124.c @@ -12,7 +12,6 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/of.h> #include <linux/pm_runtime.h> diff --git a/sound/soc/codecs/rtq9128.c b/sound/soc/codecs/rtq9128.c index 14a2c0723d33..573200e5062f 100644 --- a/sound/soc/codecs/rtq9128.c +++ b/sound/soc/codecs/rtq9128.c @@ -11,7 +11,6 @@ #include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/kernel.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/pm_runtime.h> #include <linux/property.h> diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c index b7e6546f1b5a..93f3fd1882a5 100644 --- a/sound/soc/codecs/sdw-mockup.c +++ b/sound/soc/codecs/sdw-mockup.c @@ -8,7 +8,6 @@ // #include <linux/device.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/soundwire/sdw.h> #include <linux/soundwire/sdw_type.h> diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index 320312f8db92..59642673b4cb 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1809,8 +1809,8 @@ static void sgtl5000_i2c_shutdown(struct i2c_client *client) } static const struct i2c_device_id sgtl5000_id[] = { - {"sgtl5000"}, - {}, + { .name = "sgtl5000" }, + { } }; MODULE_DEVICE_TABLE(i2c, sgtl5000_id); diff --git a/sound/soc/codecs/sigmadsp.c b/sound/soc/codecs/sigmadsp.c index 3343dbb63d2b..2e08fde3989c 100644 --- a/sound/soc/codecs/sigmadsp.c +++ b/sound/soc/codecs/sigmadsp.c @@ -35,7 +35,7 @@ struct sigmadsp_control { struct snd_kcontrol *kcontrol; bool is_readback; bool cached; - uint8_t cache[]; + u8 cache[] __counted_by(num_bytes); }; struct sigmadsp_data { @@ -223,7 +223,7 @@ static int sigma_fw_load_control(struct sigmadsp *sigmadsp, return -EINVAL; num_bytes = le16_to_cpu(ctrl_chunk->num_bytes); - ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL); + ctrl = kzalloc_flex(*ctrl, cache, num_bytes); if (!ctrl) return -ENOMEM; diff --git a/sound/soc/codecs/simple-amplifier.c b/sound/soc/codecs/simple-amplifier.c index d306c585b52b..ca53b08c0b33 100644 --- a/sound/soc/codecs/simple-amplifier.c +++ b/sound/soc/codecs/simple-amplifier.c @@ -1,25 +1,100 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017 BayLibre, SAS. - * Author: Jerome Brunet <jbrunet@baylibre.com> + * Support for gpio amplifier + * Copyright 2026 CS GROUP France + * Author: Herve Codina <herve.codina@bootlin.com> + * + * Basic simple amplifier driver + * Copyright (c) 2017 BayLibre, SAS. + * Author: Jerome Brunet <jbrunet@baylibre.com> */ +#include <linux/bitmap.h> +#include <linux/bits.h> #include <linux/gpio/consumer.h> +#include <linux/math.h> +#include <linux/minmax.h> #include <linux/module.h> +#include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <linux/slab.h> #include <sound/soc.h> +#include <linux/sort.h> +#include <sound/tlv.h> -#define DRV_NAME "simple-amplifier" +struct simple_amp_single { + struct gpio_desc *gpio; + bool is_inverted; + int kctrl_val; + const char *control_name; +}; + +struct simple_amp_point { + u32 gpio_val; + int gain_db; +}; + +struct simple_amp_range { + unsigned int nb_points; + struct simple_amp_point min; + struct simple_amp_point max; +}; + +struct simple_amp_ranges { + unsigned int nb_ranges; + struct simple_amp_range *tab_ranges; +}; + +struct simple_amp_labels { + unsigned int nb_labels; + const char **tab_labels; +}; + +enum simple_amp_mode { + SIMPLE_AMP_MODE_NONE, + SIMPLE_AMP_MODE_RANGES, + SIMPLE_AMP_MODE_LABELS, +}; + +struct simple_amp_multi { + struct gpio_descs *gpios; + u32 kctrl_val; + u32 kctrl_max; + const char *control_name; + unsigned int *tlv_array; + enum simple_amp_mode mode; + union { + struct simple_amp_ranges ranges; + struct simple_amp_labels labels; + }; +}; + +struct simple_amp_data { + unsigned int supports; +#define SIMPLE_AUDIO_SUPPORT_PGA BIT(0) +#define SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES BIT(1) +#define SIMPLE_AUDIO_SUPPORT_MUTE BIT(2) +#define SIMPLE_AUDIO_SUPPORT_BYPASS BIT(3) + + const struct snd_soc_dapm_widget *dapm_widgets; + unsigned int num_dapm_widgets; + const struct snd_soc_dapm_route *dapm_routes; + unsigned int num_dapm_routes; +}; struct simple_amp { + const struct simple_amp_data *data; struct gpio_desc *gpiod_enable; + struct simple_amp_single mute; + struct simple_amp_single bypass; + struct simple_amp_multi gain; }; -static int drv_event(struct snd_soc_dapm_widget *w, - struct snd_kcontrol *control, int event) +static int simple_amp_power_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *control, int event) { struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm); - struct simple_amp *priv = snd_soc_component_get_drvdata(c); + struct simple_amp *simple_amp = snd_soc_component_get_drvdata(c); int val; switch (event) { @@ -34,7 +109,7 @@ static int drv_event(struct snd_soc_dapm_widget *w, return -EINVAL; } - gpiod_set_value_cansleep(priv->gpiod_enable, val); + gpiod_set_value_cansleep(simple_amp->gpiod_enable, val); return 0; } @@ -42,7 +117,7 @@ static int drv_event(struct snd_soc_dapm_widget *w, static const struct snd_soc_dapm_widget simple_amp_dapm_widgets[] = { SND_SOC_DAPM_INPUT("INL"), SND_SOC_DAPM_INPUT("INR"), - SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event, + SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, simple_amp_power_event, (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)), SND_SOC_DAPM_OUTPUT("OUTL"), SND_SOC_DAPM_OUTPUT("OUTR"), @@ -58,47 +133,836 @@ static const struct snd_soc_dapm_route simple_amp_dapm_routes[] = { { "OUTR", NULL, "DRV" }, }; +static const struct snd_soc_dapm_widget simple_amp_mono_pga_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, simple_amp_power_event, + (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd", 0, 0), +}; + +static const struct snd_soc_dapm_route simple_amp_mono_pga_dapm_routes[] = { + { "PGA", NULL, "IN" }, + { "PGA", NULL, "vdd" }, + { "OUT", NULL, "PGA" }, +}; + +static const struct snd_soc_dapm_widget simple_amp_stereo_pga_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("INL"), + SND_SOC_DAPM_INPUT("INR"), + SND_SOC_DAPM_OUTPUT("OUTL"), + SND_SOC_DAPM_OUTPUT("OUTR"), + SND_SOC_DAPM_PGA_E("PGA", SND_SOC_NOPM, 0, 0, NULL, 0, simple_amp_power_event, + (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)), + SND_SOC_DAPM_REGULATOR_SUPPLY("vdd", 0, 0), +}; + +static const struct snd_soc_dapm_route simple_amp_stereo_pga_dapm_routes[] = { + { "PGA", NULL, "INL" }, + { "PGA", NULL, "INR" }, + { "PGA", NULL, "vdd" }, + { "OUTL", NULL, "PGA" }, + { "OUTR", NULL, "PGA" }, +}; + +static int simple_amp_single_kctrl_write_gpio(struct simple_amp_single *single, + int kctrl_val) +{ + int gpio_val; + + gpio_val = single->is_inverted ? !kctrl_val : kctrl_val; + + return gpiod_set_value_cansleep(single->gpio, gpio_val); +} + +static int simple_amp_single_kctrl_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + return 0; +} + +static int simple_amp_single_kctrl_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct simple_amp_single *single = (struct simple_amp_single *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = single->kctrl_val; + + return 0; +} + +static int simple_amp_single_kctrl_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct simple_amp_single *single = (struct simple_amp_single *)kcontrol->private_value; + int kctrl_val; + int err; + + kctrl_val = ucontrol->value.integer.value[0] ? 1 : 0; + + if (kctrl_val == single->kctrl_val) + return 0; + + err = simple_amp_single_kctrl_write_gpio(single, kctrl_val); + if (err) + return err; + + single->kctrl_val = kctrl_val; + + return 1; /* The value changed */ +} + +static int simple_amp_single_add_kcontrol(struct snd_soc_component *component, + struct simple_amp_single *single) +{ + struct snd_kcontrol_new control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = single->control_name, + .info = simple_amp_single_kctrl_info, + .get = simple_amp_single_kctrl_get, + .put = simple_amp_single_kctrl_put, + .private_value = (unsigned long)single, + }; + int ret; + + /* Be consistent between single->kctrl_val value and the GPIO value */ + ret = simple_amp_single_kctrl_write_gpio(single, single->kctrl_val); + if (ret) + return ret; + + return snd_soc_add_component_controls(component, &control, 1); +} + +static u32 simple_amp_multi_ranges_kctrl_to_gpio(u32 kctrl_val, + struct simple_amp_ranges *ranges) +{ + struct simple_amp_range *range; + u32 index = kctrl_val; + unsigned int i; + + for (i = 0; i < ranges->nb_ranges; i++) { + range = &ranges->tab_ranges[i]; + + if (index < range->nb_points) + return (range->max.gpio_val >= range->min.gpio_val) ? + range->min.gpio_val + index : + range->min.gpio_val - index; + + index -= range->nb_points; + } + + /* + * Given index out of possible ranges. This is shouldn't happen. + * Signal the issue and return the maximum value + */ + WARN(1, "kctrl_val %u out of ranges\n", kctrl_val); + return ranges->tab_ranges[ranges->nb_ranges - 1].max.gpio_val; +} + +static int simple_amp_multi_kctrl_write_gpios(struct simple_amp_multi *multi, + u32 kctrl_val) +{ + DECLARE_BITMAP(bm, 32); + u32 gpio_val; + + if (kctrl_val > multi->kctrl_max) + return -EINVAL; + + if (multi->mode == SIMPLE_AMP_MODE_RANGES) + gpio_val = simple_amp_multi_ranges_kctrl_to_gpio(kctrl_val, + &multi->ranges); + else + gpio_val = kctrl_val; + + bitmap_from_arr32(bm, &gpio_val, multi->gpios->ndescs); + + return gpiod_multi_set_value_cansleep(multi->gpios, bm); +} + +static int simple_amp_multi_kctrl_int_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value; + + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = multi->kctrl_max; + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + return 0; +} + +static int simple_amp_multi_kctrl_int_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value; + + ucontrol->value.integer.value[0] = multi->kctrl_val; + return 0; +} + +static int simple_amp_multi_kctrl_int_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value; + u32 kctrl_val; + int ret; + + kctrl_val = ucontrol->value.integer.value[0]; + + if (kctrl_val == multi->kctrl_val) + return 0; + + ret = simple_amp_multi_kctrl_write_gpios(multi, kctrl_val); + if (ret) + return ret; + + multi->kctrl_val = kctrl_val; + + return 1; /* The value changed */ +} + +static int simple_amp_multi_kctrl_enum_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value; + + return snd_ctl_enum_info(uinfo, 1, multi->labels.nb_labels, + multi->labels.tab_labels); +} + +static int simple_amp_multi_kctrl_enum_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value; + + ucontrol->value.enumerated.item[0] = multi->kctrl_val; + return 0; +} + +static int simple_amp_multi_kctrl_enum_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct simple_amp_multi *multi = (struct simple_amp_multi *)kcontrol->private_value; + u32 kctrl_val; + int ret; + + kctrl_val = ucontrol->value.enumerated.item[0]; + + if (kctrl_val == multi->kctrl_val) + return 0; + + ret = simple_amp_multi_kctrl_write_gpios(multi, kctrl_val); + if (ret) + return ret; + + multi->kctrl_val = kctrl_val; + + return 1; /* The value changed */ +} + +static unsigned int *simple_amp_alloc_tlv_ranges(const struct simple_amp_ranges *ranges) +{ + unsigned int index; + unsigned int *tlv; + unsigned int *t; + unsigned int i; + + tlv = kzalloc_objs(*tlv, 2 + ranges->nb_ranges * 6, GFP_KERNEL); + if (!tlv) + return NULL; + + t = tlv; + + /* Fill first TLV */ + *t++ = SNDRV_CTL_TLVT_DB_RANGE; /* Tag */ + *t++ = ranges->nb_ranges * 6 * sizeof(*tlv); /* Len */ + /* Ranges are sorted from lower to higher value */ + index = 0; + for (i = 0; i < ranges->nb_ranges; i++) { + /* Fill range item i */ + *t++ = index; /* min */ + index += ranges->tab_ranges[i].nb_points; + *t++ = index - 1; /* max */ + *t++ = SNDRV_CTL_TLVT_DB_MINMAX; /* Tag */ + *t++ = 2 * sizeof(*tlv); /* Len */ + *t++ = ranges->tab_ranges[i].min.gain_db; /* min_dB */ + *t++ = ranges->tab_ranges[i].max.gain_db; /* max_dB */ + } + + return tlv; +} + +static int simple_amp_multi_add_kcontrol(struct snd_soc_component *component, + struct simple_amp_multi *multi) +{ + struct snd_kcontrol_new control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = multi->control_name, + .info = simple_amp_multi_kctrl_int_info, + .get = simple_amp_multi_kctrl_int_get, + .put = simple_amp_multi_kctrl_int_put, + .private_value = (unsigned long)multi, + }; + int ret; + + switch (multi->mode) { + case SIMPLE_AMP_MODE_RANGES: + multi->tlv_array = simple_amp_alloc_tlv_ranges(&multi->ranges); + if (!multi->tlv_array) + return -ENOMEM; + + control.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | + SNDRV_CTL_ELEM_ACCESS_READWRITE; + control.tlv.p = multi->tlv_array; + break; + + case SIMPLE_AMP_MODE_LABELS: + /* Use enumerated values */ + control.info = simple_amp_multi_kctrl_enum_info; + control.get = simple_amp_multi_kctrl_enum_get; + control.put = simple_amp_multi_kctrl_enum_put; + break; + + case SIMPLE_AMP_MODE_NONE: + /* Already set control configuration is enough */ + break; + + default: + return -EINVAL; + } + + /* Be consistent between multi->kctrl_val value and the GPIOs value */ + ret = simple_amp_multi_kctrl_write_gpios(multi, multi->kctrl_val); + if (ret) + goto err_free_tlv_array; + + ret = snd_soc_add_component_controls(component, &control, 1); + if (ret) + goto err_free_tlv_array; + + return 0; + +err_free_tlv_array: + kfree(multi->tlv_array); + return ret; +} + +static int simple_amp_add_basic_dapm(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component); + struct device *dev = component->dev; + int ret; + + /* Add basic dapm widgets and routes */ + ret = snd_soc_dapm_new_controls(dapm, simple_amp->data->dapm_widgets, + simple_amp->data->num_dapm_widgets); + if (ret) { + dev_err(dev, "Failed to add basic dapm widgets (%d)\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(dapm, simple_amp->data->dapm_routes, + simple_amp->data->num_dapm_routes); + if (ret) { + dev_err(dev, "Failed to add basic dapm routes (%d)\n", ret); + return ret; + } + + return 0; +} + +struct simple_amp_supply { + const char *prop_name; + const struct snd_soc_dapm_widget dapm_widget; + const struct snd_soc_dapm_route dapm_route; +}; + +static const struct simple_amp_supply simple_amp_supplies[] = { + { + .prop_name = "vddio-supply", + .dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vddio", 0, 0), + .dapm_route = { "PGA", NULL, "vddio" }, + }, { + .prop_name = "vdda1-supply", + .dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vdda1", 0, 0), + .dapm_route = { "PGA", NULL, "vdda1" }, + }, { + .prop_name = "vdda2-supply", + .dapm_widget = SND_SOC_DAPM_REGULATOR_SUPPLY("vdda2", 0, 0), + .dapm_route = { "PGA", NULL, "vdda2" }, + }, + { /* End of list */} +}; + +static int simple_amp_add_power_supplies(struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); + struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component); + const struct simple_amp_supply *supply; + struct device *dev = component->dev; + int ret; + + /* + * Those additional power supplies are attached to the PGA. + * If PGA is not supported, simply skipped them. + */ + if (!(simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_PGA)) { + dev_err(dev, "Extra power supplied need PGA\n"); + return -EINVAL; + } + + supply = simple_amp_supplies; + do { + if (!of_property_present(dev->of_node, supply->prop_name)) + continue; + + ret = snd_soc_dapm_new_controls(dapm, &supply->dapm_widget, 1); + if (ret) { + dev_err(dev, "Failed to add control for '%s' (%d)\n", + supply->prop_name, ret); + return ret; + } + ret = snd_soc_dapm_add_routes(dapm, &supply->dapm_route, 1); + if (ret) { + dev_err(dev, "Failed to add route for '%s' (%d)\n", + supply->prop_name, ret); + return ret; + } + } while ((++supply)->prop_name); + + return 0; +} + +static int simple_amp_component_probe(struct snd_soc_component *component) +{ + struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component); + int ret; + + /* Add basic dapm widgets and routes */ + ret = simple_amp_add_basic_dapm(component); + if (ret) + return ret; + + /* Add additional power supplies */ + if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES) { + ret = simple_amp_add_power_supplies(component); + if (ret) + return ret; + } + + if (simple_amp->mute.gpio) { + /* + * The name of the GPIO used is mute. According to this name, 1 + * means muted and 0 means un-muted. + * + * An inversion is expected by ALSA. Indeed from ALSA point of + * view, 1 means 'on' (un-muted) and 0 means 'off' (muted). + */ + simple_amp->mute.is_inverted = true; + simple_amp->mute.kctrl_val = 1; /* Un-muted */ + ret = simple_amp_single_add_kcontrol(component, &simple_amp->mute); + if (ret) + return ret; + } + + if (simple_amp->bypass.gpio) { + ret = simple_amp_single_add_kcontrol(component, &simple_amp->bypass); + if (ret) + return ret; + } + + if (simple_amp->gain.gpios) { + ret = simple_amp_multi_add_kcontrol(component, &simple_amp->gain); + if (ret) + return ret; + } + + return 0; +} + +static void simple_amp_component_remove(struct snd_soc_component *component) +{ + struct simple_amp *simple_amp = snd_soc_component_get_drvdata(component); + + kfree(simple_amp->gain.tlv_array); + simple_amp->gain.tlv_array = NULL; +} + static const struct snd_soc_component_driver simple_amp_component_driver = { - .dapm_widgets = simple_amp_dapm_widgets, - .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets), - .dapm_routes = simple_amp_dapm_routes, - .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes), + .probe = simple_amp_component_probe, + .remove = simple_amp_component_remove, }; +static int simple_amp_parse_single_gpio(struct device *dev, + struct simple_amp_single *single, + const char *gpio_property) +{ + /* Start with the inactive value */ + single->is_inverted = false; + single->kctrl_val = 0; + single->gpio = devm_gpiod_get_optional(dev, gpio_property, GPIOD_OUT_LOW); + if (IS_ERR(single->gpio)) + return dev_err_probe(dev, PTR_ERR(single->gpio), + "Failed to get '%s' gpio\n", + gpio_property); + return 0; +} + +static int simple_amp_cmp_ranges(const void *a, const void *b) +{ + const struct simple_amp_range *a_range = a; + const struct simple_amp_range *b_range = b; + + /* Ranges a and b don't overlap. This has been already checked */ + + return a_range->min.gain_db - b_range->max.gain_db; +} + +static int simple_amp_check_new_range(const struct simple_amp_range *new_range, + const struct simple_amp_range *tab_ranges, + unsigned int nb_ranges) +{ + unsigned int i; + + for (i = 0; i < nb_ranges; i++) { + /* Check for range overlaps */ + if (new_range->min.gain_db >= tab_ranges[i].min.gain_db && + new_range->min.gain_db <= tab_ranges[i].max.gain_db) + return -EINVAL; + + if (new_range->max.gain_db >= tab_ranges[i].min.gain_db && + new_range->max.gain_db <= tab_ranges[i].max.gain_db) + return -EINVAL; + + if (new_range->min.gain_db <= tab_ranges[i].min.gain_db && + new_range->max.gain_db >= tab_ranges[i].max.gain_db) + return -EINVAL; + } + return 0; +} + +static int simple_amp_parse_ranges(struct device *dev, + struct simple_amp_multi *multi, + const char *ranges_property) +{ + struct simple_amp_ranges *ranges = &multi->ranges; + struct simple_amp_range *range; + struct device_node *np = dev->of_node; + struct simple_amp_point first_point; + unsigned int max_gpio_val; + unsigned int i; + int ret; + u32 u; + s32 s; + + max_gpio_val = (1 << multi->gpios->ndescs) - 1; + + ret = of_property_count_u32_elems(np, ranges_property); + if (ret < 0) + return ret; + + /* The ranges array cannot be empty */ + if (ret == 0) + return -EINVAL; + /* + * One range item is composed of 2 points and each point is composed of + * 2 values. + */ + if (ret % 4) + return -EINVAL; + + ranges->nb_ranges = ret / 4; + + /* The worst case is one range per possible gpio value */ + if (ranges->nb_ranges > max_gpio_val + 1) + return -EINVAL; + + ranges->tab_ranges = devm_kcalloc(dev, ranges->nb_ranges, + sizeof(*ranges->tab_ranges), + GFP_KERNEL); + if (!ranges->tab_ranges) + return -ENOMEM; + + multi->kctrl_max = 0; + for (i = 0; i < ranges->nb_ranges; i++) { + range = &ranges->tab_ranges[i]; + + /* First gpios value */ + ret = of_property_read_u32_index(np, ranges_property, i * 4, &u); + if (ret) + return ret; + if (u > max_gpio_val) + return -EINVAL; + + range->min.gpio_val = u; + + /* First Gain value */ + ret = of_property_read_s32_index(np, ranges_property, i * 4 + 1, &s); + if (ret) + return ret; + + range->min.gain_db = s; + + /* Second gpios value */ + ret = of_property_read_u32_index(np, ranges_property, i * 4 + 2, &u); + if (ret) + return ret; + if (u > max_gpio_val) + return -EINVAL; + + range->max.gpio_val = u; + + /* Second Gain value */ + ret = of_property_read_s32_index(np, ranges_property, i * 4 + 3, &s); + if (ret) + return ret; + + range->max.gain_db = s; + + /* Save the first point for later usage */ + if (i == 0) + first_point = range->min; + + /* Fix min and max if needed */ + if (range->min.gain_db > range->max.gain_db) + swap(range->min, range->max); + + ret = simple_amp_check_new_range(range, ranges->tab_ranges, i); + if (ret) + return ret; + + range->nb_points = abs_diff(range->min.gpio_val, + range->max.gpio_val) + 1; + + multi->kctrl_max += range->nb_points; + } + + multi->kctrl_max -= 1; + + /* Sort the tab_range array by gain_db value */ + sort(ranges->tab_ranges, ranges->nb_ranges, sizeof(*ranges->tab_ranges), + simple_amp_cmp_ranges, NULL); + + /* + * multi->kctrl_val is the index in tab_ranges. + * + * Choose to have the initial amplification value set to the first point + * available in the first range available in the tab_ranges array before + * sorting. + * + * This first point has been identified before sorting. Search for it in + * the sorted array in order to set the multi->kctrl_val initial value. + */ + multi->kctrl_val = 0; + for (i = 0; i < ranges->nb_ranges; i++) { + range = &ranges->tab_ranges[i]; + + if (range->min.gpio_val == first_point.gpio_val && + range->min.gain_db == first_point.gain_db) + break; + + multi->kctrl_val += range->nb_points; + + if (range->max.gpio_val == first_point.gpio_val && + range->max.gain_db == first_point.gain_db) { + multi->kctrl_val--; + break; + } + } + + return 0; +} + +static int simple_amp_parse_labels(struct device *dev, + struct simple_amp_multi *multi, + const char *labels_property) +{ + struct simple_amp_labels *labels = &multi->labels; + struct device_node *np = dev->of_node; + int ret; + + ret = of_property_count_strings(np, labels_property); + if (ret < 0) + return ret; + + /* The labels array cannot be empty */ + if (ret == 0) + return -EINVAL; + + labels->nb_labels = ret; + if (labels->nb_labels > (1 << multi->gpios->ndescs)) + return -EINVAL; + + labels->tab_labels = devm_kcalloc(dev, labels->nb_labels, + sizeof(*labels->tab_labels), + GFP_KERNEL); + if (!labels->tab_labels) + return -ENOMEM; + + multi->kctrl_max = labels->nb_labels - 1; + multi->kctrl_val = 0; + + return of_property_read_string_array(np, labels_property, labels->tab_labels, + labels->nb_labels); +} + +static int simple_amp_parse_multi_gpio(struct device *dev, + struct simple_amp_multi *multi, + const char *gpios_property, + const char *ranges_property, + const char *labels_property) +{ + struct device_node *np = dev->of_node; + int ret; + + /* Start with the value 0 (GPIO inactive). Can be changed later */ + multi->kctrl_val = 0; + multi->gpios = devm_gpiod_get_array_optional(dev, gpios_property, GPIOD_OUT_LOW); + if (IS_ERR(multi->gpios)) + return dev_err_probe(dev, PTR_ERR(multi->gpios), + "Failed to get '%s' gpios\n", + gpios_property); + if (!multi->gpios) + return 0; + + if (multi->gpios->ndescs > 16) + return dev_err_probe(dev, -EINVAL, + "Number of '%s' gpios limited to 16\n", + gpios_property); + + /* Set default value for the kctrl_max. Can be changed later */ + multi->kctrl_max = (1 << multi->gpios->ndescs) - 1; + + multi->mode = SIMPLE_AMP_MODE_NONE; + if (of_property_present(np, ranges_property)) { + ret = simple_amp_parse_ranges(dev, multi, ranges_property); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to parse '%s'\n", + ranges_property); + multi->mode = SIMPLE_AMP_MODE_RANGES; + } else if (of_property_present(np, labels_property)) { + ret = simple_amp_parse_labels(dev, multi, labels_property); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to parse '%s'\n", + labels_property); + + multi->mode = SIMPLE_AMP_MODE_LABELS; + } + + return 0; +} + static int simple_amp_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct simple_amp *priv; + struct simple_amp *simple_amp; + int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (priv == NULL) + simple_amp = devm_kzalloc(dev, sizeof(*simple_amp), GFP_KERNEL); + if (!simple_amp) return -ENOMEM; - platform_set_drvdata(pdev, priv); + platform_set_drvdata(pdev, simple_amp); - priv->gpiod_enable = devm_gpiod_get_optional(dev, "enable", - GPIOD_OUT_LOW); - if (IS_ERR(priv->gpiod_enable)) - return dev_err_probe(dev, PTR_ERR(priv->gpiod_enable), + simple_amp->data = of_device_get_match_data(dev); + if (!simple_amp->data) + return -EINVAL; + + simple_amp->gpiod_enable = devm_gpiod_get_optional(dev, "enable", + GPIOD_OUT_LOW); + if (IS_ERR(simple_amp->gpiod_enable)) + return dev_err_probe(dev, PTR_ERR(simple_amp->gpiod_enable), "Failed to get 'enable' gpio"); + if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_MUTE) { + ret = simple_amp_parse_single_gpio(dev, &simple_amp->mute, "mute"); + if (ret) + return ret; + } + + if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_BYPASS) { + ret = simple_amp_parse_single_gpio(dev, &simple_amp->bypass, "bypass"); + if (ret) + return ret; + } + + if (simple_amp->data->supports & SIMPLE_AUDIO_SUPPORT_PGA) { + ret = simple_amp_parse_multi_gpio(dev, &simple_amp->gain, "gain", + "gain-ranges", "gain-labels"); + if (ret) + return ret; + } + + /* Set controls name */ + simple_amp->gain.control_name = "Volume"; + simple_amp->mute.control_name = "Switch"; + simple_amp->bypass.control_name = "Bypass Switch"; + + if (simple_amp->gain.mode == SIMPLE_AMP_MODE_LABELS) { + /* + * The gain widget control will use enumerated values. + * + * Having just "Voltage" and "Switch" widget names with + * enumerated values and boolean value can confuse ALSA in terms + * of possible values (strings). + * + * Make things clear and avoid the just "Switch" name in that + * case. + */ + simple_amp->mute.control_name = "Out Switch"; + } + return devm_snd_soc_register_component(dev, &simple_amp_component_driver, NULL, 0); } -#ifdef CONFIG_OF +static const struct simple_amp_data simple_audio_amplifier_data = { + .dapm_widgets = simple_amp_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_amp_dapm_widgets), + .dapm_routes = simple_amp_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_amp_dapm_routes), +}; + +static const struct simple_amp_data simple_audio_mono_pga_data = { + .supports = SIMPLE_AUDIO_SUPPORT_PGA | + SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES | + SIMPLE_AUDIO_SUPPORT_MUTE | + SIMPLE_AUDIO_SUPPORT_BYPASS, + .dapm_widgets = simple_amp_mono_pga_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_amp_mono_pga_dapm_widgets), + .dapm_routes = simple_amp_mono_pga_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_amp_mono_pga_dapm_routes), +}; + +static const struct simple_amp_data simple_audio_stereo_pga_data = { + .supports = SIMPLE_AUDIO_SUPPORT_PGA | + SIMPLE_AUDIO_SUPPORT_POWER_SUPPLIES | + SIMPLE_AUDIO_SUPPORT_MUTE | + SIMPLE_AUDIO_SUPPORT_BYPASS, + .dapm_widgets = simple_amp_stereo_pga_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(simple_amp_stereo_pga_dapm_widgets), + .dapm_routes = simple_amp_stereo_pga_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(simple_amp_stereo_pga_dapm_routes), +}; + static const struct of_device_id simple_amp_ids[] = { - { .compatible = "dioo,dio2125", }, - { .compatible = "simple-audio-amplifier", }, + { .compatible = "dioo,dio2125", .data = &simple_audio_amplifier_data}, + { .compatible = "simple-audio-amplifier", .data = &simple_audio_amplifier_data}, + { .compatible = "gpio-audio-amp-mono", .data = &simple_audio_mono_pga_data}, + { .compatible = "gpio-audio-amp-stereo", .data = &simple_audio_stereo_pga_data}, { } }; MODULE_DEVICE_TABLE(of, simple_amp_ids); -#endif static struct platform_driver simple_amp_driver = { .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(simple_amp_ids), + .name = "simple-amplifier", + .of_match_table = simple_amp_ids, }, .probe = simple_amp_probe, }; @@ -107,4 +971,5 @@ module_platform_driver(simple_amp_driver); MODULE_DESCRIPTION("ASoC Simple Audio Amplifier driver"); MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>"); +MODULE_AUTHOR("Herve Codina <herve.codina@bootlin.com>"); MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index 06de2b4fce5e..7fce60de5e5f 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -7,7 +7,6 @@ // Auther: Gyuhwa Park <gyuhwa.park@irondevice.com> // Kiseok Jo <kiseok.jo@irondevice.com> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -1782,8 +1781,8 @@ static void sma1303_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sma1303_i2c_id[] = { - {"sma1303"}, - {} + { .name = "sma1303" }, + { } }; MODULE_DEVICE_TABLE(i2c, sma1303_i2c_id); diff --git a/sound/soc/codecs/sma1307.c b/sound/soc/codecs/sma1307.c index 5850bf6e71ca..c52fe95b30c6 100644 --- a/sound/soc/codecs/sma1307.c +++ b/sound/soc/codecs/sma1307.c @@ -1592,6 +1592,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) struct sma1307_priv *sma1307 = container_of(work, struct sma1307_priv, check_fault_work.work); unsigned int status1_val, status2_val; + char volume[sizeof("VOLUME=0x12345678")]; char *envp[3] = { NULL, NULL, NULL }; if (sma1307->tsdw_cnt) @@ -1607,7 +1608,7 @@ static void sma1307_check_fault_worker(struct work_struct *work) if (~status1_val & SMA1307_OT1_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT1(Over Temperature Level 1)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1"); + envp[0] = "STATUS=OT1"; if (sma1307->sw_ot1_prot) { /* Volume control (Current Volume -3dB) */ if ((sma1307->cur_vol + 6) <= 0xFA) { @@ -1615,8 +1616,9 @@ static void sma1307_check_fault_worker(struct work_struct *work) regmap_write(sma1307->regmap, SMA1307_0A_SPK_VOL, sma1307->cur_vol); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + snprintf(volume, sizeof(volume), + "VOLUME=0x%02X", sma1307->cur_vol); + envp[1] = volume; } } sma1307->tsdw_cnt++; @@ -1625,48 +1627,53 @@ static void sma1307_check_fault_worker(struct work_struct *work) SMA1307_0A_SPK_VOL, sma1307->init_vol); sma1307->tsdw_cnt = 0; sma1307->cur_vol = sma1307->init_vol; - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT1_CLEAR"); - envp[1] = kasprintf(GFP_KERNEL, - "VOLUME=0x%02X", sma1307->cur_vol); + envp[0] = "STATUS=OT1_CLEAR"; + snprintf(volume, sizeof(volume), "VOLUME=0x%02X", + sma1307->cur_vol); + envp[1] = volume; } if (~status1_val & SMA1307_OT2_OK_STATUS) { dev_crit(sma1307->dev, "%s: OT2(Over Temperature Level 2)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OT2"); + envp[0] = "STATUS=OT2"; + envp[1] = NULL; } if (status1_val & SMA1307_UVLO_STATUS) { dev_crit(sma1307->dev, "%s: UVLO(Under Voltage Lock Out)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=UVLO"); + envp[0] = "STATUS=UVLO"; + envp[1] = NULL; } if (status1_val & SMA1307_OVP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OVP_BST(Over Voltage Protection)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OVP_BST"); + envp[0] = "STATUS=OVP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_SPK_STATUS) { dev_crit(sma1307->dev, "%s: OCP_SPK(Over Current Protect SPK)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_SPK"); + envp[0] = "STATUS=OCP_SPK"; + envp[1] = NULL; } if (status2_val & SMA1307_OCP_BST_STATUS) { dev_crit(sma1307->dev, "%s: OCP_BST(Over Current Protect Boost)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=OCP_BST"); + envp[0] = "STATUS=OCP_BST"; + envp[1] = NULL; } if (status2_val & SMA1307_CLK_MON_STATUS) { dev_crit(sma1307->dev, "%s: CLK_FAULT(No clock input)\n", __func__); - envp[0] = kasprintf(GFP_KERNEL, "STATUS=CLK_FAULT"); + envp[0] = "STATUS=CLK_FAULT"; + envp[1] = NULL; } if (envp[0] != NULL) { if (kobject_uevent_env(sma1307->kobj, KOBJ_CHANGE, envp)) dev_err(sma1307->dev, "%s: Error sending uevent\n", __func__); - kfree(envp[0]); - kfree(envp[1]); } if (sma1307->check_fault_status) { @@ -2008,8 +2015,8 @@ static void sma1307_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id sma1307_i2c_id[] = { - { "sma1307a" }, - { "sma1307aq" }, + { .name = "sma1307a" }, + { .name = "sma1307aq" }, { } }; diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c index 55f00ce7c718..4157f7787801 100644 --- a/sound/soc/codecs/src4xxx-i2c.c +++ b/sound/soc/codecs/src4xxx-i2c.c @@ -6,7 +6,6 @@ // Author: Matt Flax <flatmax@flatmax.com> #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/regmap.h> @@ -19,7 +18,7 @@ static int src4xxx_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id src4xxx_i2c_ids[] = { - { "src4392" }, + { .name = "src4392" }, { } }; MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids); diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 9008e5416004..5192569ba6a8 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -794,7 +794,7 @@ MODULE_DEVICE_TABLE(of, ssm2518_dt_ids); #endif static const struct i2c_device_id ssm2518_i2c_ids[] = { - { "ssm2518" }, + { .name = "ssm2518" }, { } }; MODULE_DEVICE_TABLE(i2c, ssm2518_i2c_ids); diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index 49c74cba17c7..23570d0a2f53 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -26,9 +26,9 @@ static int ssm2602_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id ssm2602_i2c_id[] = { - { "ssm2602", SSM2602 }, - { "ssm2603", SSM2602 }, - { "ssm2604", SSM2604 }, + { .name = "ssm2602", .driver_data = SSM2602 }, + { .name = "ssm2603", .driver_data = SSM2602 }, + { .name = "ssm2604", .driver_data = SSM2604 }, { } }; MODULE_DEVICE_TABLE(i2c, ssm2602_i2c_id); diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 15f88624faeb..8415dd163edd 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -472,7 +472,7 @@ static int ssm4567_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id ssm4567_i2c_ids[] = { - { "ssm4567" }, + { .name = "ssm4567" }, { } }; MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids); diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index b9f9784f5164..652c6e3a9e63 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -1154,9 +1154,9 @@ static int sta32x_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id sta32x_i2c_id[] = { - { "sta326" }, - { "sta328" }, - { "sta329" }, + { .name = "sta326" }, + { .name = "sta328" }, + { .name = "sta329" }, { } }; MODULE_DEVICE_TABLE(i2c, sta32x_i2c_id); diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index 71af82b099c0..99c7f7ac807b 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1236,11 +1236,8 @@ static int sta350_i2c_probe(struct i2c_client *i2c) return ret; } -static void sta350_i2c_remove(struct i2c_client *client) -{} - static const struct i2c_device_id sta350_i2c_id[] = { - { "sta350" }, + { .name = "sta350" }, { } }; MODULE_DEVICE_TABLE(i2c, sta350_i2c_id); @@ -1250,8 +1247,7 @@ static struct i2c_driver sta350_i2c_driver = { .name = "sta350", .of_match_table = of_match_ptr(st350_dt_ids), }, - .probe = sta350_i2c_probe, - .remove = sta350_i2c_remove, + .probe = sta350_i2c_probe, .id_table = sta350_i2c_id, }; diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 946aa6a4e57c..7d57999f9cb1 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -361,7 +361,7 @@ static int sta529_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id sta529_i2c_id[] = { - { "sta529" }, + { .name = "sta529" }, { } }; MODULE_DEVICE_TABLE(i2c, sta529_i2c_id); diff --git a/sound/soc/codecs/tac5xx2-sdw.c b/sound/soc/codecs/tac5xx2-sdw.c new file mode 100644 index 000000000000..ace06f5ab58c --- /dev/null +++ b/sound/soc/codecs/tac5xx2-sdw.c @@ -0,0 +1,2039 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments TAC5XX2 Audio Smart Amplifier +// +// Copyright (C) 2025 Texas Instruments Incorporated +// https://www.ti.com +// +// Author: Niranjan H Y <niranjan.hy@ti.com> + +#include <linux/err.h> +#include <linux/firmware.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <linux/time.h> +#include <linux/unaligned.h> +#include <sound/pcm_params.h> +#include <sound/sdw.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/sdca_asoc.h> +#include <sound/sdca_function.h> +#include <sound/sdca_regmap.h> +#include <sound/jack.h> + +#include "tac5xx2.h" + +#define TAC5XX2_PROBE_TIMEOUT_MS 3000 +#define TAC5XX2_FW_CACHE_TIMEOUT_MS 300 + +#define TAC5XX2_DEVICE_RATES (SNDRV_PCM_RATE_44100 | \ + SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | \ + SNDRV_PCM_RATE_88200) +#define TAC5XX2_DEVICE_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE) +/* Define channel constants */ +#define TAC_CHANNEL_LEFT 1 +#define TAC_CHANNEL_RIGHT 2 +#define TAC_JACK_MONO_CS 2 + +#define TAC_MUTE_REG(func, fu, ch) \ + SDW_SDCA_CTL(TAC_FUNCTION_ID_##func, TAC_SDCA_ENT_##fu, \ + TAC_SDCA_CHANNEL_MUTE, TAC_CHANNEL_##ch) +#define TAC_USAGE_REG(func, ent) \ + SDW_SDCA_CTL(TAC_FUNCTION_ID_##func, TAC_SDCA_ENT_##ent, \ + TAC_SDCA_CTL_USAGE, 0) +#define TAC_XU_BYPASS_REG(func, xu) \ + SDW_SDCA_CTL(TAC_FUNCTION_ID_##func, TAC_SDCA_ENT_##xu, \ + TAC_SDCA_CTL_XU_BYPASS, 0) + +/* mute registers */ +#define FU21_L_MUTE_REG TAC_MUTE_REG(SA, FU21, LEFT) +#define FU21_R_MUTE_REG TAC_MUTE_REG(SA, FU21, RIGHT) +#define FU23_L_MUTE_REG TAC_MUTE_REG(SA, FU23, LEFT) +#define FU23_R_MUTE_REG TAC_MUTE_REG(SA, FU23, RIGHT) +#define FU26_MUTE_REG TAC_MUTE_REG(SA, FU26, LEFT) +#define FU11_L_MUTE_REG TAC_MUTE_REG(SM, FU11, LEFT) +#define FU11_R_MUTE_REG TAC_MUTE_REG(SM, FU11, RIGHT) +#define FU113_L_MUTE_REG TAC_MUTE_REG(SM, FU113, LEFT) +#define FU113_R_MUTE_REG TAC_MUTE_REG(SM, FU113, RIGHT) +#define FU41_L_MUTE_REG TAC_MUTE_REG(UAJ, FU41, LEFT) +#define FU41_R_MUTE_REG TAC_MUTE_REG(UAJ, FU41, RIGHT) +#define FU36_MUTE_REG TAC_MUTE_REG(UAJ, FU36, RIGHT) + +/* it/ot usage */ +#define IT11_USAGE_REG TAC_USAGE_REG(SM, IT11) +#define IT41_USAGE_REG TAC_USAGE_REG(UAJ, IT41) +#define IT33_USAGE_REG TAC_USAGE_REG(UAJ, IT33) +#define OT113_USAGE_REG TAC_USAGE_REG(SM, OT113) +#define OT45_USAGE_REG TAC_USAGE_REG(UAJ, OT45) +#define OT36_USAGE_REG TAC_USAGE_REG(UAJ, OT36) + +/* xu bypass */ +#define XU12_BYPASS_REG TAC_XU_BYPASS_REG(SM, XU12) +#define XU42_BYPASS_REG TAC_XU_BYPASS_REG(UAJ, XU42) + +#define TAC_DSP_ALGO_STATUS TAC_REG_SDW(0, 3, 12) +#define TAC_DSP_ALGO_STATUS_RUNNING 0x20 +#define TAC_FW_HDR_SIZE 88 +#define TAC_FW_FILE_HDR 20 +#define TAC_MAX_FW_CHUNKS 512 + +struct tac_fw_hdr { + u32 size; + u32 version_offset; + u32 plt_id; + u32 ppc3_ver; + u64 timestamp; + u8 ddc_name[64]; +}; + +/* Firmware file/chunk structure */ +struct tac_fw_file { + u32 vendor_id; + u32 file_id; + u32 version; + u32 length; + u32 dest_addr; + u8 *fw_data; +}; + +/* TLV for volume control */ +static const DECLARE_TLV_DB_SCALE(tac5xx2_amp_tlv, 0, 50, 0); +static const DECLARE_TLV_DB_SCALE(tac5xx2_dvc_tlv, -7200, 50, 0); + +/* Q7.8 volume control parameters: range -72dB to +6dB, step 0.5dB */ +#define TAC_DVC_STEP 128 /* 0.5 dB in Q7.8 format */ +#define TAC_DVC_MIN (-144) /* -72 dB / 0.5 dB step */ +#define TAC_DVC_MAX 12 /* +6 dB / 0.5 dB step */ + +/* TAC-specific stereo volume control macro using SDW_SDCA_CTL (single control for L/R) */ +#define TAC_DOUBLE_Q78_TLV(name, func_id, ent_id) \ + SDCA_DOUBLE_Q78_TLV(name, \ + SDW_SDCA_CTL(TAC_FUNCTION_ID_##func_id, TAC_SDCA_ENT_##ent_id, \ + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), \ + SDW_SDCA_CTL(TAC_FUNCTION_ID_##func_id, TAC_SDCA_ENT_##ent_id, \ + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), \ + TAC_DVC_MIN, TAC_DVC_MAX, TAC_DVC_STEP, tac5xx2_dvc_tlv) + +struct tac5xx2_prv { + struct snd_soc_component *component; + struct sdw_slave *sdw_peripheral; + struct sdca_function_data *sa_func_data; + struct sdca_function_data *sm_func_data; + struct sdca_function_data *uaj_func_data; + struct sdca_function_data *hid_func_data; + enum sdw_slave_status status; + struct regmap *regmap; + struct device *dev; + bool hw_init; + bool first_hw_init_done; + u32 part_id; + struct snd_soc_jack *hs_jack; + int jack_type; + /* Custom fw binary. UMP File Download is not used. */ + unsigned int fw_file_cnt; + struct tac_fw_file *fw_files; + struct completion fw_caching_complete; + bool fw_dl_success; + u8 fw_binaryname[64]; +}; + +static const struct reg_default tac_reg_default[] = { + {TAC_SW_RESET, 0x0}, + {TAC_SLEEP_MODEZ, 0x0}, + {TAC_FEATURE_PDZ, 0x0}, + {TAC_TX_CH_EN, 0xf0}, + {TAC_REG_SDW(0, 0, 0x5), 0xcf}, + {TAC_REG_SDW(0, 0, 0x6), 0xa}, + {TAC_REG_SDW(0, 0, 0x7), 0x0}, + {TAC_REG_SDW(0, 0, 0x8), 0xfe}, + {TAC_REG_SDW(0, 0, 0x9), 0x9}, + {TAC_REG_SDW(0, 0, 0xa), 0x28}, + {TAC_REG_SDW(0, 0, 0xb), 0x1}, + {TAC_REG_SDW(0, 0, 0xc), 0x11}, + {TAC_REG_SDW(0, 0, 0xd), 0x11}, + {TAC_REG_SDW(0, 0, 0xe), 0x61}, + {TAC_REG_SDW(0, 0, 0xf), 0x0}, + {TAC_REG_SDW(0, 0, 0x10), 0x50}, + {TAC_REG_SDW(0, 0, 0x11), 0x70}, + {TAC_REG_SDW(0, 0, 0x12), 0x60}, + {TAC_REG_SDW(0, 0, 0x13), 0x28}, + {TAC_REG_SDW(0, 0, 0x14), 0x0}, + {TAC_REG_SDW(0, 0, 0x15), 0x18}, + {TAC_REG_SDW(0, 0, 0x16), 0x20}, + {TAC_REG_SDW(0, 0, 0x17), 0x0}, + {TAC_REG_SDW(0, 0, 0x18), 0x18}, + {TAC_REG_SDW(0, 0, 0x19), 0x54}, + {TAC_REG_SDW(0, 0, 0x1a), 0x8}, + {TAC_REG_SDW(0, 0, 0x1b), 0x0}, + {TAC_REG_SDW(0, 0, 0x1c), 0x30}, + {TAC_REG_SDW(0, 0, 0x1d), 0x0}, + {TAC_REG_SDW(0, 0, 0x1e), 0x0}, + {TAC_REG_SDW(0, 0, 0x1f), 0x0}, + {TAC_REG_SDW(0, 0, 0x20), 0x0}, + {TAC_REG_SDW(0, 0, 0x21), 0x20}, + {TAC_REG_SDW(0, 0, 0x22), 0x21}, + {TAC_REG_SDW(0, 0, 0x23), 0x22}, + {TAC_REG_SDW(0, 0, 0x24), 0x23}, + {TAC_REG_SDW(0, 0, 0x25), 0x4}, + {TAC_REG_SDW(0, 0, 0x26), 0x5}, + {TAC_REG_SDW(0, 0, 0x27), 0x6}, + {TAC_REG_SDW(0, 0, 0x28), 0x7}, + {TAC_REG_SDW(0, 0, 0x29), 0x0}, + {TAC_REG_SDW(0, 0, 0x2a), 0x0}, + {TAC_REG_SDW(0, 0, 0x2b), 0x0}, + {TAC_REG_SDW(0, 0, 0x2c), 0x20}, + {TAC_REG_SDW(0, 0, 0x2d), 0x21}, + {TAC_REG_SDW(0, 0, 0x2e), 0x2}, + {TAC_REG_SDW(0, 0, 0x2f), 0x3}, + {TAC_REG_SDW(0, 0, 0x30), 0x4}, + {TAC_REG_SDW(0, 0, 0x31), 0x5}, + {TAC_REG_SDW(0, 0, 0x32), 0x6}, + {TAC_REG_SDW(0, 0, 0x33), 0x7}, + {TAC_REG_SDW(0, 0, 0x34), 0x0}, + {TAC_REG_SDW(0, 0, 0x35), 0x90}, + {TAC_REG_SDW(0, 0, 0x36), 0x80}, + {TAC_REG_SDW(0, 0, 0x37), 0x0}, + {TAC_REG_SDW(0, 0, 0x39), 0x0}, + {TAC_REG_SDW(0, 0, 0x3a), 0x90}, + {TAC_REG_SDW(0, 0, 0x3b), 0x80}, + {TAC_REG_SDW(0, 0, 0x3c), 0x0}, + {TAC_REG_SDW(0, 0, 0x3e), 0x0}, + {TAC_REG_SDW(0, 0, 0x3f), 0x90}, + {TAC_REG_SDW(0, 0, 0x40), 0x80}, + {TAC_REG_SDW(0, 0, 0x41), 0x0}, + {TAC_REG_SDW(0, 0, 0x43), 0x90}, + {TAC_REG_SDW(0, 0, 0x44), 0x80}, + {TAC_REG_SDW(0, 0, 0x45), 0x0}, + {TAC_REG_SDW(0, 0, 0x47), 0x90}, + {TAC_REG_SDW(0, 0, 0x48), 0x80}, + {TAC_REG_SDW(0, 0, 0x49), 0x0}, + {TAC_REG_SDW(0, 0, 0x4b), 0x90}, + {TAC_REG_SDW(0, 0, 0x4c), 0x80}, + {TAC_REG_SDW(0, 0, 0x4d), 0x0}, + {TAC_REG_SDW(0, 0, 0x4f), 0x31}, + {TAC_REG_SDW(0, 0, 0x50), 0x0}, + {TAC_REG_SDW(0, 0, 0x51), 0x0}, + {TAC_REG_SDW(0, 0, 0x52), 0x90}, + {TAC_REG_SDW(0, 0, 0x53), 0x80}, + {TAC_REG_SDW(0, 0, 0x55), 0x90}, + {TAC_REG_SDW(0, 0, 0x56), 0x80}, + {TAC_REG_SDW(0, 0, 0x58), 0x90}, + {TAC_REG_SDW(0, 0, 0x59), 0x80}, + {TAC_REG_SDW(0, 0, 0x5b), 0x90}, + {TAC_REG_SDW(0, 0, 0x5c), 0x80}, + {TAC_REG_SDW(0, 0, 0x5e), 0x8}, + {TAC_REG_SDW(0, 0, 0x5f), 0x8}, + {TAC_REG_SDW(0, 0, 0x60), 0x0}, + {TAC_REG_SDW(0, 0, 0x61), 0x0}, + {TAC_REG_SDW(0, 0, 0x62), 0xff}, + {TAC_REG_SDW(0, 0, 0x63), 0xc0}, + {TAC_REG_SDW(0, 0, 0x64), 0x5}, + {TAC_REG_SDW(0, 0, 0x65), 0x3}, + {TAC_REG_SDW(0, 0, 0x66), 0x0}, + {TAC_REG_SDW(0, 0, 0x67), 0x0}, + {TAC_REG_SDW(0, 0, 0x68), 0x0}, + {TAC_REG_SDW(0, 0, 0x69), 0x8}, + {TAC_REG_SDW(0, 0, 0x6a), 0x0}, + {TAC_REG_SDW(0, 0, 0x6b), 0xa0}, + {TAC_REG_SDW(0, 0, 0x6c), 0x18}, + {TAC_REG_SDW(0, 0, 0x6d), 0x18}, + {TAC_REG_SDW(0, 0, 0x6e), 0x18}, + {TAC_REG_SDW(0, 0, 0x6f), 0x18}, + {TAC_REG_SDW(0, 0, 0x70), 0x88}, + {TAC_REG_SDW(0, 0, 0x71), 0xff}, + {TAC_REG_SDW(0, 0, 0x72), 0x0}, + {TAC_REG_SDW(0, 0, 0x73), 0x31}, + {TAC_REG_SDW(0, 0, 0x74), 0xc0}, + {TAC_REG_SDW(0, 0, 0x75), 0x0}, + {TAC_REG_SDW(0, 0, 0x76), 0x0}, + {TAC_REG_SDW(0, 0, 0x77), 0x0}, + {TAC_REG_SDW(0, 0, 0x78), 0x0}, + {TAC_REG_SDW(0, 0, 0x7b), 0x0}, + {TAC_REG_SDW(0, 0, 0x7c), 0xd0}, + {TAC_REG_SDW(0, 0, 0x7d), 0x0}, + {TAC_REG_SDW(0, 0, 0x7e), 0x0}, + {TAC_REG_SDW(0, 1, 0x1), 0x0}, + {TAC_REG_SDW(0, 1, 0x2), 0x0}, + {TAC_REG_SDW(0, 1, 0x3), 0x0}, + {TAC_REG_SDW(0, 1, 0x4), 0x4}, + {TAC_REG_SDW(0, 1, 0x5), 0x0}, + {TAC_REG_SDW(0, 1, 0x6), 0x0}, + {TAC_REG_SDW(0, 1, 0x7), 0x0}, + {TAC_REG_SDW(0, 1, 0x8), 0x0}, + {TAC_REG_SDW(0, 1, 0x9), 0x0}, + {TAC_REG_SDW(0, 1, 0xa), 0x0}, + {TAC_REG_SDW(0, 1, 0xb), 0x1}, + {TAC_REG_SDW(0, 1, 0xc), 0x0}, + {TAC_REG_SDW(0, 1, 0xd), 0x0}, + {TAC_REG_SDW(0, 1, 0xe), 0x0}, + {TAC_REG_SDW(0, 1, 0xf), 0x8}, + {TAC_REG_SDW(0, 1, 0x10), 0x0}, + {TAC_REG_SDW(0, 1, 0x11), 0x0}, + {TAC_REG_SDW(0, 1, 0x12), 0x1}, + {TAC_REG_SDW(0, 1, 0x13), 0x0}, + {TAC_REG_SDW(0, 1, 0x14), 0x0}, + {TAC_REG_SDW(0, 1, 0x15), 0x0}, + {TAC_REG_SDW(0, 1, 0x16), 0x0}, + {TAC_REG_SDW(0, 1, 0x17), 0x0}, + {TAC_REG_SDW(0, 1, 0x18), 0x0}, + {TAC_REG_SDW(0, 1, 0x19), 0x0}, + {TAC_REG_SDW(0, 1, 0x1a), 0x0}, + {TAC_REG_SDW(0, 1, 0x1b), 0x0}, + {TAC_REG_SDW(0, 1, 0x1c), 0x0}, + {TAC_REG_SDW(0, 1, 0x1d), 0x0}, + {TAC_REG_SDW(0, 1, 0x1e), 0x2}, + {TAC_REG_SDW(0, 1, 0x1f), 0x8}, + {TAC_REG_SDW(0, 1, 0x20), 0x9}, + {TAC_REG_SDW(0, 1, 0x21), 0xa}, + {TAC_REG_SDW(0, 1, 0x22), 0xb}, + {TAC_REG_SDW(0, 1, 0x23), 0xc}, + {TAC_REG_SDW(0, 1, 0x24), 0xd}, + {TAC_REG_SDW(0, 1, 0x25), 0xe}, + {TAC_REG_SDW(0, 1, 0x26), 0xf}, + {TAC_REG_SDW(0, 1, 0x27), 0x8}, + {TAC_REG_SDW(0, 1, 0x28), 0x9}, + {TAC_REG_SDW(0, 1, 0x29), 0xa}, + {TAC_REG_SDW(0, 1, 0x2a), 0xb}, + {TAC_REG_SDW(0, 1, 0x2b), 0xc}, + {TAC_REG_SDW(0, 1, 0x2c), 0xd}, + {TAC_REG_SDW(0, 1, 0x2d), 0xe}, + {TAC_REG_SDW(0, 1, 0x2e), 0xf}, + {TAC_REG_SDW(0, 1, 0x2f), 0x0}, + {TAC_REG_SDW(0, 1, 0x30), 0x0}, + {TAC_REG_SDW(0, 1, 0x31), 0x0}, + {TAC_REG_SDW(0, 1, 0x32), 0x0}, + {TAC_REG_SDW(0, 1, 0x33), 0x0}, + {TAC_REG_SDW(0, 1, 0x34), 0x0}, + {TAC_REG_SDW(0, 1, 0x35), 0x0}, + {TAC_REG_SDW(0, 1, 0x36), 0x0}, + {TAC_REG_SDW(0, 1, 0x37), 0x0}, + {TAC_REG_SDW(0, 1, 0x38), 0x98}, + {TAC_REG_SDW(0, 1, 0x39), 0x0}, + {TAC_REG_SDW(0, 1, 0x3a), 0x0}, + {TAC_REG_SDW(0, 1, 0x3b), 0x0}, + {TAC_REG_SDW(0, 1, 0x3c), 0x1}, + {TAC_REG_SDW(0, 1, 0x3d), 0x2}, + {TAC_REG_SDW(0, 1, 0x3e), 0x3}, + {TAC_REG_SDW(0, 1, 0x3f), 0x4}, + {TAC_REG_SDW(0, 1, 0x40), 0x5}, + {TAC_REG_SDW(0, 1, 0x41), 0x6}, + {TAC_REG_SDW(0, 1, 0x42), 0x7}, + {TAC_REG_SDW(0, 1, 0x43), 0x0}, + {TAC_REG_SDW(0, 1, 0x44), 0x0}, + {TAC_REG_SDW(0, 1, 0x45), 0x1}, + {TAC_REG_SDW(0, 1, 0x46), 0x2}, + {TAC_REG_SDW(0, 1, 0x47), 0x3}, + {TAC_REG_SDW(0, 1, 0x48), 0x4}, + {TAC_REG_SDW(0, 1, 0x49), 0x5}, + {TAC_REG_SDW(0, 1, 0x4a), 0x6}, + {TAC_REG_SDW(0, 1, 0x4b), 0x7}, + {TAC_REG_SDW(0, 1, 0x4c), 0x98}, + {TAC_REG_SDW(0, 1, 0x4d), 0x0}, + {TAC_REG_SDW(0, 1, 0x4e), 0x0}, + {TAC_REG_SDW(0, 1, 0x4f), 0x0}, + {TAC_REG_SDW(0, 1, 0x50), 0x1}, + {TAC_REG_SDW(0, 1, 0x51), 0x2}, + {TAC_REG_SDW(0, 1, 0x52), 0x3}, + {TAC_REG_SDW(0, 1, 0x53), 0x4}, + {TAC_REG_SDW(0, 1, 0x54), 0x5}, + {TAC_REG_SDW(0, 1, 0x55), 0x6}, + {TAC_REG_SDW(0, 1, 0x56), 0x7}, + {TAC_REG_SDW(0, 1, 0x57), 0x0}, + {TAC_REG_SDW(0, 1, 0x58), 0x0}, + {TAC_REG_SDW(0, 1, 0x59), 0x1}, + {TAC_REG_SDW(0, 1, 0x5a), 0x2}, + {TAC_REG_SDW(0, 1, 0x5b), 0x3}, + {TAC_REG_SDW(0, 1, 0x5c), 0x4}, + {TAC_REG_SDW(0, 1, 0x5d), 0x5}, + {TAC_REG_SDW(0, 1, 0x5e), 0x6}, + {TAC_REG_SDW(0, 1, 0x5f), 0x7}, + {TAC_REG_SDW(0, 1, 0x60), 0x98}, + {TAC_REG_SDW(0, 1, 0x61), 0x0}, + {TAC_REG_SDW(0, 1, 0x62), 0x0}, + {TAC_REG_SDW(0, 1, 0x63), 0x0}, + {TAC_REG_SDW(0, 1, 0x64), 0x1}, + {TAC_REG_SDW(0, 1, 0x65), 0x2}, + {TAC_REG_SDW(0, 1, 0x66), 0x3}, + {TAC_REG_SDW(0, 1, 0x67), 0x4}, + {TAC_REG_SDW(0, 1, 0x68), 0x5}, + {TAC_REG_SDW(0, 1, 0x69), 0x6}, + {TAC_REG_SDW(0, 1, 0x6a), 0x7}, + {TAC_REG_SDW(0, 1, 0x6b), 0x0}, + {TAC_REG_SDW(0, 1, 0x6c), 0x0}, + {TAC_REG_SDW(0, 1, 0x6d), 0x1}, + {TAC_REG_SDW(0, 1, 0x6e), 0x2}, + {TAC_REG_SDW(0, 1, 0x6f), 0x3}, + {TAC_REG_SDW(0, 1, 0x70), 0x4}, + {TAC_REG_SDW(0, 1, 0x71), 0x5}, + {TAC_REG_SDW(0, 1, 0x72), 0x6}, + {TAC_REG_SDW(0, 1, 0x73), 0x7}, +}; + +static const struct reg_sequence tac_spk_seq[] = { + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0), +}; + +static const struct reg_sequence tac_sm_seq[] = { + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0), +}; + +static const struct reg_sequence tac_uaj_seq[] = { + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT), 0), + REG_SEQ0(SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU36, + TAC_SDCA_CHANNEL_GAIN, TAC_JACK_MONO_CS), 0), +}; + +static bool tac_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAC_REG_SDW(0, 0, 1) ... TAC_REG_SDW(0, 0, 5): + case TAC_REG_SDW(0, 2, 1) ... TAC_REG_SDW(0, 2, 6): + case TAC_REG_SDW(0, 2, 24) ... TAC_REG_SDW(0, 2, 55): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1, + TAC_SDCA_CTL_HIDTX_CURRENT_OWNER, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1, + TAC_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, + TAC_SDCA_CTL_DET_MODE, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_PDE23, + TAC_SDCA_REQUESTED_PS, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_PDE11, + TAC_SDCA_REQUESTED_PS, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE47, + TAC_SDCA_REQUESTED_PS, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE34, + TAC_SDCA_REQUESTED_PS, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_PDE23, + TAC_SDCA_ACTUAL_PS, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_PDE11, + TAC_SDCA_ACTUAL_PS, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE47, + TAC_SDCA_ACTUAL_PS, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_PDE34, + TAC_SDCA_ACTUAL_PS, 0): + case SDW_SCP_SDCA_INT1: + case SDW_SCP_SDCA_INT2: + case SDW_SCP_SDCA_INT3: + case SDW_SCP_SDCA_INT4: + case SDW_SDCA_CTL(1, 0, 0x10, 0): + case SDW_SDCA_CTL(2, 0, 0x10, 0): + case SDW_SDCA_CTL(3, 0, 0x10, 0): + case SDW_SDCA_CTL(4, 0, 0x1, 0): + case 0x44007F80 ... 0x44007F87: + case TAC_DSP_ALGO_STATUS: /* DSP algo status - always read from HW */ + return true; + default: + break; + } + + return false; +} + +static int tac_sdca_mbq_size(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21, + TAC_SDCA_CHANNEL_VOLUME, TAC_CHANNEL_LEFT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU21, + TAC_SDCA_CHANNEL_VOLUME, TAC_CHANNEL_RIGHT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SA, TAC_SDCA_ENT_FU23, + TAC_SDCA_MASTER_GAIN, 0): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU113, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_SM, TAC_SDCA_ENT_FU11, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_LEFT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU41, + TAC_SDCA_CHANNEL_GAIN, TAC_CHANNEL_RIGHT): + case SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU36, + TAC_SDCA_CHANNEL_GAIN, TAC_JACK_MONO_CS): + return 2; + + default: + return 1; + } +} + +static const struct regmap_sdw_mbq_cfg tac_mbq_cfg = { + .mbq_size = tac_sdca_mbq_size, +}; + +static const struct regmap_config tac_regmap = { + .reg_bits = 32, + .val_bits = 16, /* mbq support */ + .reg_defaults = tac_reg_default, + .num_reg_defaults = ARRAY_SIZE(tac_reg_default), + .max_register = 0x47FFFFFF, + .cache_type = REGCACHE_MAPLE, + .volatile_reg = tac_volatile_reg, + .use_single_read = true, + .use_single_write = true, +}; + +/* Check if device has DSP algo that needs status monitoring */ +static bool tac_has_dsp_algo(struct tac5xx2_prv *tac_dev) +{ + switch (tac_dev->part_id) { + case 0x5682: + case 0x2883: + return true; + default: + return false; + } +} + +/* Check if device has UAJ (Universal Audio Jack) support */ +static bool tac_has_uaj_support(struct tac5xx2_prv *tac_dev) +{ + return tac_dev->uaj_func_data; +} + +/* Forward declaration for headset detection */ +static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev); + +/* Volume controls for mic, hp and mic cap */ +static const struct snd_kcontrol_new tac5xx2_snd_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("Amp Volume", TAC_AMP_LVL_CFG0, TAC_AMP_LVL_CFG1, + 2, 0, 44, 1, tac5xx2_amp_tlv), + TAC_DOUBLE_Q78_TLV("DMIC Capture Volume", SM, FU113), + TAC_DOUBLE_Q78_TLV("Speaker Volume", SA, FU21), +}; + +static const struct snd_kcontrol_new tac_uaj_controls[] = { + TAC_DOUBLE_Q78_TLV("UAJ Playback Volume", UAJ, FU41), + SDCA_SINGLE_Q78_TLV("UAJ Capture Volume", + SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_FU36, + TAC_SDCA_CHANNEL_GAIN, TAC_JACK_MONO_CS), + TAC_DVC_MIN, TAC_DVC_MAX, TAC_DVC_STEP, tac5xx2_dvc_tlv), +}; + +static const struct snd_soc_dapm_widget tac5xx2_common_widgets[] = { + /* Port 1: Speaker Playback Path */ + SND_SOC_DAPM_AIF_IN("AIF1 Playback", "DP1 Speaker Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("FU21_L", FU21_L_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("FU21_R", FU21_R_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("FU23_L", FU23_L_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("FU23_R", FU23_R_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_OUTPUT("SPK_L"), + SND_SOC_DAPM_OUTPUT("SPK_R"), + + /* Port 3: Smart Mic (DMIC) Capture Path */ + SND_SOC_DAPM_AIF_OUT("AIF3 Capture", "DP3 Mic Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("DMIC_L"), + SND_SOC_DAPM_INPUT("DMIC_R"), + SND_SOC_DAPM_PGA("IT11", IT11_USAGE_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CS113", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("FU11_L", FU11_L_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("FU11_R", FU11_R_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("PPU11", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("XU12", XU12_BYPASS_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("FU113_L", FU113_L_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("FU113_R", FU113_R_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("OT113", OT113_USAGE_REG, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_widget tac_uaj_widgets[] = { + /* Port 4: UAJ (Headphone) Playback Path */ + SND_SOC_DAPM_AIF_IN("AIF4 Playback", "DP4 UAJ Speaker Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_PGA("IT41", IT41_USAGE_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("FU41_L", FU41_L_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("FU41_R", FU41_R_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_PGA("XU42", XU42_BYPASS_REG, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("CS41", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_DAC("OT45", "DP4 UAJ Speaker Playback", OT45_USAGE_REG, 0, 0), + SND_SOC_DAPM_OUTPUT("HP_L"), + SND_SOC_DAPM_OUTPUT("HP_R"), + + /* Port 7: UAJ (Headset Mic) Capture Path */ + SND_SOC_DAPM_AIF_OUT("AIF7 Capture", "DP7 UAJ Mic Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_INPUT("UAJ_MIC"), + SND_SOC_DAPM_ADC("IT33", "DP7 UAJ Mic Capture", IT33_USAGE_REG, 0, 0), + SND_SOC_DAPM_PGA("FU36", FU36_MUTE_REG, 0, 1, NULL, 0), + SND_SOC_DAPM_SUPPLY("CS36", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_PGA("OT36", OT36_USAGE_REG, 0, 0, NULL, 0), +}; + +static const struct snd_soc_dapm_route tac5xx2_common_routes[] = { + /* Speaker Playback Path */ + {"FU21_L", NULL, "AIF1 Playback"}, + {"FU21_R", NULL, "AIF1 Playback"}, + + {"FU23_L", NULL, "FU21_L"}, + {"FU23_R", NULL, "FU21_R"}, + + {"SPK_L", NULL, "FU23_L"}, + {"SPK_R", NULL, "FU23_R"}, + + /* Smart Mic DAPM Routes */ + {"IT11", NULL, "DMIC_L"}, + {"IT11", NULL, "DMIC_R"}, + {"FU11_L", NULL, "IT11"}, + {"FU11_R", NULL, "IT11"}, + {"PPU11", NULL, "FU11_L"}, + {"PPU11", NULL, "FU11_R"}, + {"XU12", NULL, "PPU11"}, + {"FU113_L", NULL, "XU12"}, + {"FU113_R", NULL, "XU12"}, + {"FU113_L", NULL, "CS113"}, + {"FU113_R", NULL, "CS113"}, + {"OT113", NULL, "FU113_L"}, + {"OT113", NULL, "FU113_R"}, + {"OT113", NULL, "CS113"}, + {"AIF3 Capture", NULL, "OT113"}, +}; + +static const struct snd_soc_dapm_route tac_uaj_routes[] = { + /* UAJ Playback routes */ + {"IT41", NULL, "AIF4 Playback"}, + {"IT41", NULL, "CS41"}, + {"FU41_L", NULL, "IT41"}, + {"FU41_R", NULL, "IT41"}, + {"XU42", NULL, "FU41_L"}, + {"XU42", NULL, "FU41_R"}, + {"OT45", NULL, "XU42"}, + {"OT45", NULL, "CS41"}, + {"HP_L", NULL, "OT45"}, + {"HP_R", NULL, "OT45"}, + + /* UAJ Capture routes */ + {"IT33", NULL, "UAJ_MIC"}, + {"IT33", NULL, "CS36"}, + {"FU36", NULL, "IT33"}, + {"OT36", NULL, "FU36"}, + {"OT36", NULL, "CS36"}, + {"AIF7 Capture", NULL, "OT36"}, +}; + +static s32 tac_set_sdw_stream(struct snd_soc_dai *dai, + void *sdw_stream, s32 direction) +{ + if (sdw_stream) + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void tac_sdw_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int tac_clear_latch(struct tac5xx2_prv *priv) +{ + /* CLR_REG is a self-clearing bit */ + return regmap_update_bits(priv->regmap, TAC_INT_CFG, + TAC_INT_CFG_CLR_REG, TAC_INT_CFG_CLR_REG); +} + +static int tac_sdw_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component); + struct sdw_slave *sdw_peripheral = tac_dev->sdw_peripheral; + struct sdw_stream_runtime *sdw_stream; + struct sdw_stream_config stream_config = {0}; + struct sdw_port_config port_config = {0}; + u8 sample_rate_idx = 0; + int function_id; + int pde_entity; + int port_num; + int ret; + + if (!tac_dev->hw_init) { + dev_err(tac_dev->dev, + "error: operation without hw initialization"); + return -EINVAL; + } + + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + if (!sdw_stream) { + dev_err(tac_dev->dev, "failed to get dma data"); + return -EINVAL; + } + + ret = tac_clear_latch(tac_dev); + if (ret) + dev_warn(tac_dev->dev, "clear latch failed, err=%d", ret); + + switch (dai->id) { + case TAC5XX2_DMIC: + function_id = TAC_FUNCTION_ID_SM; + pde_entity = TAC_SDCA_ENT_PDE11; + port_num = TAC_SDW_PORT_NUM_DMIC; + break; + case TAC5XX2_UAJ: + function_id = TAC_FUNCTION_ID_UAJ; + pde_entity = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + TAC_SDCA_ENT_PDE47 : TAC_SDCA_ENT_PDE34; + port_num = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + TAC_SDW_PORT_NUM_UAJ_PLAYBACK : + TAC_SDW_PORT_NUM_UAJ_CAPTURE; + break; + case TAC5XX2_SPK: + function_id = TAC_FUNCTION_ID_SA; + pde_entity = TAC_SDCA_ENT_PDE23; + port_num = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + TAC_SDW_PORT_NUM_SPK_PLAYBACK : + TAC_SDW_PORT_NUM_SPK_CAPTURE; + break; + default: + dev_err(tac_dev->dev, "Invalid dai id: %d for power up\n", dai->id); + return -EINVAL; + } + + snd_sdw_params_to_config(substream, params, &stream_config, &port_config); + port_config.num = port_num; + ret = sdw_stream_add_slave(sdw_peripheral, &stream_config, + &port_config, 1, sdw_stream); + if (ret) { + dev_err(dai->dev, + "Unable to configure port %d: %d\n", port_num, ret); + return ret; + } + + switch (params_rate(params)) { + case 48000: + sample_rate_idx = 0x01; + break; + case 44100: + sample_rate_idx = 0x02; + break; + case 96000: + sample_rate_idx = 0x03; + break; + case 88200: + sample_rate_idx = 0x04; + break; + default: + dev_dbg(tac_dev->dev, "Unsupported sample rate: %d Hz", + params_rate(params)); + return -EINVAL; + } + + switch (function_id) { + case TAC_FUNCTION_ID_SM: + ret = regmap_write(tac_dev->regmap, + SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS113, + TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0), + sample_rate_idx); + if (ret) { + dev_err(tac_dev->dev, "Failed to set CS113 sample rate: %d", ret); + return ret; + } + + break; + case TAC_FUNCTION_ID_UAJ: + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + ret = regmap_write(tac_dev->regmap, + SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS41, + TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0), + sample_rate_idx); + if (ret) { + dev_err(tac_dev->dev, "Failed to set CS41 sample rate: %d", ret); + return ret; + } + } else { + ret = regmap_write(tac_dev->regmap, + SDW_SDCA_CTL(function_id, TAC_SDCA_ENT_CS36, + TAC_SDCA_CTL_CS_SAMP_RATE_IDX, 0), + sample_rate_idx); + if (ret) { + dev_err(tac_dev->dev, "Failed to set CS36 sample rate: %d", ret); + return ret; + } + } + break; + case TAC_FUNCTION_ID_SA: + /* SmartAmp: no additional sample rate configuration needed */ + break; + } + + ret = regmap_write(tac_dev->regmap, SDW_SDCA_CTL(function_id, pde_entity, + TAC_SDCA_REQUESTED_PS, 0), 0); + if (ret) { + dev_err(tac_dev->dev, + "failed to set func %d, entity %d's requested PS to 0: %d\n", + function_id, pde_entity, ret); + return ret; + } + + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, pde_entity, + SDCA_PDE_PS3, SDCA_PDE_PS0, NULL, 0); + if (ret) + dev_err(tac_dev->dev, "failed to transition func %d, pde %d from PS3 -> PS0, err=%d\n", + function_id, pde_entity, ret); + return ret; +} + +static int tac_sdw_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(dai->component); + int pde_entity, function_id; + int ret; + + sdw_stream_remove_slave(tac_dev->sdw_peripheral, sdw_stream); + + switch (dai->id) { + case TAC5XX2_DMIC: + pde_entity = TAC_SDCA_ENT_PDE11; + function_id = TAC_FUNCTION_ID_SM; + break; + case TAC5XX2_UAJ: + function_id = TAC_FUNCTION_ID_UAJ; + pde_entity = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ? + TAC_SDCA_ENT_PDE47 : TAC_SDCA_ENT_PDE34; + break; + case TAC5XX2_SPK: + function_id = TAC_FUNCTION_ID_SA; + pde_entity = TAC_SDCA_ENT_PDE23; + break; + default: + dev_err(tac_dev->dev, "unhandled dai %d for power down\n", dai->id); + return -EINVAL; + } + + ret = regmap_write(tac_dev->regmap, SDW_SDCA_CTL(function_id, pde_entity, + TAC_SDCA_REQUESTED_PS, 0), + SDCA_PDE_PS3); + if (ret) { + dev_err(tac_dev->dev, + "failed to set func %d, entity %d's requested PS to 3: %d\n", + function_id, pde_entity, ret); + return ret; + } + + ret = sdca_asoc_pde_poll_actual_ps(tac_dev->dev, tac_dev->regmap, function_id, + pde_entity, SDCA_PDE_PS0, SDCA_PDE_PS3, + NULL, 0); + if (ret) + dev_err(tac_dev->dev, + "failed to transition func %d, pde %d from PS0 -> PS3, err=%d\n", + function_id, pde_entity, ret); + + return ret; +} + +static const struct snd_soc_dai_ops tac_dai_ops = { + .hw_params = tac_sdw_hw_params, + .hw_free = tac_sdw_pcm_hw_free, + .set_stream = tac_set_sdw_stream, + .shutdown = tac_sdw_shutdown, +}; + +static int tac5xx2_sdca_btn_type(unsigned char *buffer, struct tac5xx2_prv *tac_dev) +{ + switch (*buffer) { + case 1: /* play pause */ + return SND_JACK_BTN_0; + case 10: /* vol down */ + return SND_JACK_BTN_3; + case 8: /* vol up */ + return SND_JACK_BTN_2; + case 4: /* long press */ + return SND_JACK_BTN_1; + case 2: /* next song */ + case 32: /* next song */ + return SND_JACK_BTN_4; + default: + return 0; + } +} + +static int tac5xx2_sdca_button_detect(struct tac5xx2_prv *tac_dev) +{ + unsigned int btn_type, offset, idx; + int ret, value, owner; + u8 buf[2]; + + ret = regmap_read(tac_dev->regmap, + SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1, + TAC_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret) { + dev_err(tac_dev->dev, + "Failed to read current UMP message owner 0x%x", ret); + return ret; + } + + if (owner == SDCA_UMP_OWNER_DEVICE) { + dev_dbg(tac_dev->dev, "skip button detect as current owner is not host\n"); + return 0; + } + + ret = regmap_read(tac_dev->regmap, + SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1, + TAC_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret) { + dev_err(tac_dev->dev, + "Failed to read current UMP message offset: %d", ret); + goto end_btn_det; + } + + dev_dbg(tac_dev->dev, "button detect: message offset = %x", offset); + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(tac_dev->regmap, + TAC_BUF_ADDR_HID1 + offset + idx, &value); + if (ret) { + dev_err(tac_dev->dev, + "Failed to read HID buffer: %d", ret); + goto end_btn_det; + } + buf[idx] = value & 0xff; + } + + if (buf[0] == 0x1) { + btn_type = tac5xx2_sdca_btn_type(&buf[1], tac_dev); + ret = btn_type; + } + +end_btn_det: + regmap_write(tac_dev->regmap, + SDW_SDCA_CTL(TAC_FUNCTION_ID_HID, TAC_SDCA_ENT_HID1, + TAC_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return ret; +} + +static int tac5xx2_sdca_headset_detect(struct tac5xx2_prv *tac_dev) +{ + int val, ret; + + ret = regmap_read(tac_dev->regmap, + SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, + TAC_SDCA_CTL_DET_MODE, 0), &val); + if (ret) { + dev_err(tac_dev->dev, "Failed to read the detect mode"); + return ret; + } + + switch (val) { + case 4: + tac_dev->jack_type = SND_JACK_MICROPHONE; + break; + case 5: + tac_dev->jack_type = SND_JACK_HEADPHONE; + break; + case 6: + tac_dev->jack_type = SND_JACK_HEADSET; + break; + case 0: + default: + tac_dev->jack_type = 0; + break; + } + + ret = regmap_write(tac_dev->regmap, + SDW_SDCA_CTL(TAC_FUNCTION_ID_UAJ, TAC_SDCA_ENT_GE35, + TAC_SDCA_CTL_SEL_MODE, 0), val); + if (ret) + dev_err(tac_dev->dev, "Failed to update the jack type to device"); + + return 0; +} + +static int tac5xx2_jack_init(struct tac5xx2_prv *tac_dev) +{ + int ret = 0; + + if (!tac_dev->hs_jack) + goto disable_interrupts; + + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_11); + if (ret) { + dev_err(tac_dev->dev, + "Failed to register jack detection interrupt: %d\n", ret); + goto disable_interrupts; + } + + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, + SDW_SCP_SDCA_INTMASK_SDCA_16); + if (ret) { + dev_err(tac_dev->dev, + "Failed to register for button detect interrupt: %d\n", ret); + goto disable_interrupts; + } + + return 0; + +disable_interrupts: + /* ignore errors while disabling interrupts */ + regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK2, 0); + regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INTMASK3, 0); + + return ret; +} + +static int tac5xx2_set_jack(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component); + int ret; + + tac_dev->hs_jack = hs_jack; + + /* resume can happen only after first hw_init */ + if (!tac_dev->first_hw_init_done) + return 0; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, + "%s: failed to resume %d\n", __func__, ret); + return ret; + } + + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, + "%s: skipping jack init for now\n", __func__); + return 0; + } + + ret = tac5xx2_jack_init(tac_dev); + if (ret) + dev_err(tac_dev->dev, "jack init failed, err=%d\n", ret); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return ret; +} + +static int tac_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + unsigned int sdca_int2, sdca_int3, jack_report_mask = 0; + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); + struct device *dev = &slave->dev; + int btn_type = 0; + int ret = 0; + + if (status->control_port) { + if (status->control_port & SDW_SCP_INT1_PARITY) + dev_warn(dev, "SCP: Parity error interrupt"); + if (status->control_port & SDW_SCP_INT1_BUS_CLASH) + dev_warn(dev, "SCP: Bus clash interrupt"); + } + + if (!tac_has_uaj_support(tac_dev)) + return 0; + + ret = regmap_read(tac_dev->regmap, SDW_SCP_SDCA_INT2, &sdca_int2); + if (ret) { + dev_err(dev, "Failed to read UAJ Interrupt, reg:%#x err=%d\n", + SDW_SCP_SDCA_INT2, ret); + return ret; + } + + ret = regmap_read(tac_dev->regmap, SDW_SCP_SDCA_INT3, &sdca_int3); + if (ret) { + dev_err(dev, "Failed to read HID interrupt reg=%#x: err=%d", + SDW_SCP_SDCA_INT3, ret); + return ret; + } + + dev_dbg(dev, "SDCA_INT2: 0x%02x, SDCA_INT3: 0x%02x\n", + sdca_int2, sdca_int3); + + if (sdca_int2 & SDW_SCP_SDCA_INT_SDCA_11) { + ret = tac5xx2_sdca_headset_detect(tac_dev); + if (ret < 0) + goto clear; + jack_report_mask |= SND_JACK_HEADSET; + } + + if (sdca_int3 & SDW_SCP_SDCA_INT_SDCA_16) { + btn_type = tac5xx2_sdca_button_detect(tac_dev); + if (btn_type < 0) + btn_type = 0; + jack_report_mask |= SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_BTN_4; + } + + if (tac_dev->jack_type == 0) + btn_type = 0; + + dev_dbg(tac_dev->dev, "in %s, jack_type=%d\n", __func__, tac_dev->jack_type); + dev_dbg(tac_dev->dev, "in %s, btn_type=0x%x\n", __func__, btn_type); + + if (!tac_dev->hs_jack) + goto clear; + + snd_soc_jack_report(tac_dev->hs_jack, tac_dev->jack_type | btn_type, + jack_report_mask); + +clear: + if (sdca_int2) { + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INT2, sdca_int2); + if (ret) + dev_dbg(tac_dev->dev, "Failed to clear jack interrupt\n"); + } + + if (sdca_int3) { + ret = regmap_write(tac_dev->regmap, SDW_SCP_SDCA_INT3, sdca_int3); + if (ret) + dev_dbg(tac_dev->dev, "failed to clear hid interrupt\n"); + } + + return 0; +} + +static struct snd_soc_dai_driver tac5572_dai_driver[] = { + { + .name = "tac5xx2-aif1", + .id = TAC5XX2_SPK, + .playback = { + .stream_name = "DP1 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, + { + .name = "tac5xx2-aif2", + .id = TAC5XX2_DMIC, + .capture = { + .stream_name = "DP3 Mic Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, + { + .name = "tac5xx2-aif3", + .id = TAC5XX2_UAJ, + .playback = { + .stream_name = "DP4 UAJ Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .capture = { + .stream_name = "DP7 UAJ Mic Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, +}; + +static struct snd_soc_dai_driver tac5672_dai_driver[] = { + { + .name = "tac5xx2-aif1", + .id = TAC5XX2_SPK, + .playback = { + .stream_name = "DP1 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .capture = { + .stream_name = "DP8 IV Sense Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "tac5xx2-aif2", + .id = TAC5XX2_DMIC, + .capture = { + .stream_name = "DP3 Mic Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, + { + .name = "tac5xx2-aif3", + .id = TAC5XX2_UAJ, + .playback = { + .stream_name = "DP4 UAJ Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .capture = { + .stream_name = "DP7 UAJ Mic Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, +}; + +static struct snd_soc_dai_driver tac5682_dai_driver[] = { + { + .name = "tac5xx2-aif1", + .id = TAC5XX2_SPK, + .playback = { + .stream_name = "DP1 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .capture = { + .stream_name = "DP2 Echo Reference Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "tac5xx2-aif2", + .id = TAC5XX2_DMIC, + .capture = { + .stream_name = "DP3 Mic Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, + { + .name = "tac5xx2-aif3", + .id = TAC5XX2_UAJ, + .playback = { + .stream_name = "DP4 UAJ Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .capture = { + .stream_name = "DP7 UAJ Mic Capture", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, +}; + +static struct snd_soc_dai_driver tas2883_dai_driver[] = { + { + .name = "tac5xx2-aif1", + .id = TAC5XX2_SPK, + .playback = { + .stream_name = "DP1 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + .symmetric_rate = 1, + }, + { + .name = "tac5xx2-aif2", + .id = TAC5XX2_DMIC, + .capture = { + .stream_name = "DP3 Mic Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TAC5XX2_DEVICE_RATES, + .formats = TAC5XX2_DEVICE_FORMATS, + }, + .ops = &tac_dai_ops, + }, +}; + +static s32 tac_component_probe(struct snd_soc_component *component) +{ + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(component); + int ret; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + if (!tac_has_uaj_support(tac_dev)) + goto done_comp_probe; + + ret = snd_soc_dapm_new_controls(snd_soc_component_to_dapm(component), + tac_uaj_widgets, + ARRAY_SIZE(tac_uaj_widgets)); + if (ret) { + dev_err(component->dev, "Failed to add UAJ widgets: %d\n", ret); + return ret; + } + + ret = snd_soc_dapm_add_routes(snd_soc_component_to_dapm(component), + tac_uaj_routes, ARRAY_SIZE(tac_uaj_routes)); + if (ret) { + dev_err(component->dev, "Failed to add UAJ routes: %d\n", ret); + return ret; + } + + ret = snd_soc_add_component_controls(component, tac_uaj_controls, + ARRAY_SIZE(tac_uaj_controls)); + if (ret) { + dev_err(component->dev, "Failed to add UAJ controls: %d\n", ret); + return ret; + } + +done_comp_probe: + tac_dev->component = component; + return 0; +} + +static void tac_component_remove(struct snd_soc_component *codec) +{ + struct tac5xx2_prv *tac_dev = snd_soc_component_get_drvdata(codec); + + tac_dev->component = NULL; +} + +static const struct snd_soc_component_driver soc_codec_driver_tacdevice = { + .probe = tac_component_probe, + .remove = tac_component_remove, + .controls = tac5xx2_snd_controls, + .num_controls = ARRAY_SIZE(tac5xx2_snd_controls), + .dapm_widgets = tac5xx2_common_widgets, + .num_dapm_widgets = ARRAY_SIZE(tac5xx2_common_widgets), + .dapm_routes = tac5xx2_common_routes, + .num_dapm_routes = ARRAY_SIZE(tac5xx2_common_routes), + .idle_bias_on = 0, + .endianness = 1, +}; + +static s32 tac_init(struct tac5xx2_prv *tac_dev) +{ + struct snd_soc_component_driver *component_driver; + struct snd_soc_dai_driver *dai_drv; + int num_dais; + s32 ret; + + dev_set_drvdata(tac_dev->dev, tac_dev); + + switch (tac_dev->part_id) { + case 0x5572: + dai_drv = tac5572_dai_driver; + num_dais = ARRAY_SIZE(tac5572_dai_driver); + break; + case 0x5672: + dai_drv = tac5672_dai_driver; + num_dais = ARRAY_SIZE(tac5672_dai_driver); + break; + case 0x5682: + dai_drv = tac5682_dai_driver; + num_dais = ARRAY_SIZE(tac5682_dai_driver); + break; + case 0x2883: + dai_drv = tas2883_dai_driver; + num_dais = ARRAY_SIZE(tas2883_dai_driver); + break; + default: + dev_err(tac_dev->dev, "Unsupported device: 0x%x\n", + tac_dev->part_id); + return -EINVAL; + } + + component_driver = devm_kzalloc(tac_dev->dev, sizeof(*component_driver), + GFP_KERNEL); + if (!component_driver) + return -ENOMEM; + + memcpy(component_driver, &soc_codec_driver_tacdevice, sizeof(*component_driver)); + if (tac_has_uaj_support(tac_dev)) + component_driver->set_jack = tac5xx2_set_jack; + + ret = devm_snd_soc_register_component(tac_dev->dev, component_driver, + dai_drv, num_dais); + if (ret) { + dev_err(tac_dev->dev, "%s: codec register error:%d.\n", + __func__, ret); + return ret; + } + + return 0; +} + +static s32 tac5xx2_sdca_dev_suspend(struct device *dev) +{ + struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev); + + if (!tac_dev->hw_init) + return 0; + + regcache_cache_only(tac_dev->regmap, true); + return 0; +} + +static s32 tac5xx2_sdca_dev_system_suspend(struct device *dev) +{ + return tac5xx2_sdca_dev_suspend(dev); +} + +static s32 tac5xx2_sdca_dev_resume(struct device *dev) +{ + struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret; + + if (!tac_dev->first_hw_init_done) { + dev_dbg(dev, "Device not initialized yet, skipping resume sync\n"); + return 0; + } + + ret = sdw_slave_wait_for_init(slave, TAC5XX2_PROBE_TIMEOUT_MS); + if (ret) { + sdw_show_ping_status(slave->bus, true); + return ret; + } + + regcache_cache_only(tac_dev->regmap, false); + regcache_mark_dirty(tac_dev->regmap); + ret = regcache_sync(tac_dev->regmap); + if (ret < 0) + dev_warn(dev, "Failed to sync regcache: %d\n", ret); + + /* Detect and set jack type for UAJ path before playback. + * This is required as jack detection does not trigger interrupt + * when device is in runtime_pm suspend with bus in clock stop mode. + */ + if (tac_has_uaj_support(tac_dev)) + tac5xx2_sdca_headset_detect(tac_dev); + + return 0; +} + +static const struct dev_pm_ops tac5xx2_sdca_pm = { + SYSTEM_SLEEP_PM_OPS(tac5xx2_sdca_dev_system_suspend, tac5xx2_sdca_dev_resume) + RUNTIME_PM_OPS(tac5xx2_sdca_dev_suspend, tac5xx2_sdca_dev_resume, NULL) +}; + +static s32 tac_fw_read_hdr(const u8 *data, struct tac_fw_hdr *hdr) +{ + hdr->size = get_unaligned_le32(data); + hdr->version_offset = get_unaligned_le32(data + 4); + hdr->plt_id = get_unaligned_le32(data + 8); + hdr->ppc3_ver = get_unaligned_le32(data + 12); + memcpy(hdr->ddc_name, data + 16, 64); + hdr->ddc_name[63] = 0; + hdr->timestamp = get_unaligned_le64(data + 80); + + return TAC_FW_HDR_SIZE; +} + +static s32 tac_fw_get_next_file(const u8 *data, size_t data_size, struct tac_fw_file *file) +{ + u32 file_length; + + /* Validate file header size */ + if (data_size < TAC_FW_FILE_HDR) + return -EINVAL; + + file->vendor_id = get_unaligned_le32(&data[0]); + file->file_id = get_unaligned_le32(&data[4]); + file->version = get_unaligned_le32(&data[8]); + file->length = get_unaligned_le32(&data[12]); + file->dest_addr = get_unaligned_le32(&data[16]); + file_length = file->length; + + /* Validate file payload exists */ + if (data_size < TAC_FW_FILE_HDR + file_length) + return -EINVAL; + + file->fw_data = (u8 *)&data[20]; + + return file_length + sizeof(u32) * 5; +} + +static void tac5xx2_fw_ready(const struct firmware *fmw, void *context) +{ + struct tac5xx2_prv *tac_dev = context; + struct tac_fw_file *files; + u32 fw_hdr_size; + u32 num_files = 0; + struct tac_fw_hdr hdr; + struct tm tm_time; + size_t img_sz; + u32 offset; + s32 ret = 0; + u8 *buf; + + if (!fmw || !fmw->data || fmw->size == 0 || fmw->size < TAC_FW_HDR_SIZE + TAC_FW_FILE_HDR) { + dev_err(tac_dev->dev, "fw file: %s is empty or invalid\n", + tac_dev->fw_binaryname); + goto out; + } + + /* Verify firmware size from header */ + fw_hdr_size = get_unaligned_le32(fmw->data); + if (fw_hdr_size != fmw->size) { + dev_err(tac_dev->dev, "firmware size mismatch: hdr=%u, actual=%zu\n", + fw_hdr_size, fmw->size); + goto out; + } + + files = devm_kzalloc(tac_dev->dev, sizeof(*files) * TAC_MAX_FW_CHUNKS, GFP_KERNEL); + buf = devm_kmemdup(tac_dev->dev, fmw->data, fmw->size, GFP_KERNEL); + if (!files || !buf) + goto out; + + /* validate the cache the firmware */ + img_sz = fmw->size; + offset = tac_fw_read_hdr(buf, &hdr); + while (offset < img_sz && num_files < TAC_MAX_FW_CHUNKS) { + u32 file_length; + + if (offset + TAC_FW_FILE_HDR > img_sz) { + dev_warn(tac_dev->dev, "Incomplete block header at offset %d\n", + offset); + goto out; + } + /* Validate that the file payload doesn't exceed buffer */ + file_length = get_unaligned_le32(&buf[offset + 12]); + /* Check for integer overflow and buffer bounds */ + if (file_length > img_sz || offset > img_sz - TAC_FW_FILE_HDR || + file_length > img_sz - offset - TAC_FW_FILE_HDR) { + dev_warn(tac_dev->dev, "File at offset %d exceeds buffer: length=%u, available=%zu\n", + offset, file_length, img_sz - offset - TAC_FW_FILE_HDR); + goto out; + } + ret = tac_fw_get_next_file(&buf[offset], img_sz - offset, &files[num_files]); + if (ret < 0) { + dev_err(tac_dev->dev, "Failed to parse file at offset %d\n", offset); + goto out; + } + offset += ret; + num_files++; + } + + if (num_files == 0) { + dev_err(tac_dev->dev, "firmware with no files\n"); + goto out; + } + + /* cache ready to use validated firmware */ + tac_dev->fw_file_cnt = num_files; + tac_dev->fw_files = files; + + time64_to_tm(hdr.timestamp, 0, &tm_time); + dev_dbg(tac_dev->dev, "fw file: %s, num_files=%u, ts:%04ld-%02d-%02d %02d:%02d\n", + tac_dev->fw_binaryname, tac_dev->fw_file_cnt, + tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, + tm_time.tm_hour, tm_time.tm_min); + dev_dbg(tac_dev->dev, "fw file: DDC Name: %s\n", hdr.ddc_name); + dev_dbg(tac_dev->dev, "fw file: PPC3 Version: 3.%ld.%ld.%ld\n", + FIELD_GET(GENMASK(31, 24), hdr.ppc3_ver), + FIELD_GET(GENMASK(23, 16), hdr.ppc3_ver), + FIELD_GET(GENMASK(15, 8), hdr.ppc3_ver) & 0x3f); + +out: + complete_all(&tac_dev->fw_caching_complete); + if (fmw) + release_firmware(fmw); +} + +static int tac_load_and_cache_firmware_async(struct tac5xx2_prv *tac_dev) +{ + tac_dev->fw_file_cnt = 0; + tac_dev->fw_files = NULL; /* ready to download files */ + + return request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + tac_dev->fw_binaryname, tac_dev->dev, + GFP_KERNEL, tac_dev, tac5xx2_fw_ready); +} + +static int tac_download(struct tac5xx2_prv *tac_dev) +{ + struct tac_fw_file *files = tac_dev->fw_files; + u32 num_files = tac_dev->fw_file_cnt; + u32 i; + int ret = 0; + + for (i = 0; i < num_files; i++) { + ret = sdw_nwrite_no_pm(tac_dev->sdw_peripheral, files[i].dest_addr, + files[i].length, files[i].fw_data); + if (ret < 0) { + dev_dbg(tac_dev->dev, + "FW write failed at addr 0x%x: %d\n", + files[i].dest_addr, ret); + return ret; + } + } + + return 0; +} + +/* + * tac5xx2 uses custom firmware binary fw. + * This is not using UMP File Download. + */ +static s32 tac_download_fw_to_hw(struct tac5xx2_prv *tac_dev) +{ + int ret; + + ret = tac_download(tac_dev); + if (ret < 0) { + dev_err(tac_dev->dev, "Firmware download failed: %d\n", ret); + return ret; + } + + dev_dbg(tac_dev->dev, "Firmware download complete: %d chunks\n", + tac_dev->fw_file_cnt); + tac_dev->fw_dl_success = true; + + return 0; +} + +static struct pci_dev *tac_get_pci_dev(struct sdw_slave *peripheral) +{ + struct device *dev = &peripheral->dev; + + for (; dev; dev = dev->parent) { + if (dev_is_pci(dev)) + return to_pci_dev(dev); + } + + return NULL; +} + +static void tac_generate_fw_name(struct sdw_slave *slave, char *name, size_t size) +{ + struct sdw_bus *bus = slave->bus; + u16 part_id = slave->id.part_id; + u8 unique_id = slave->id.unique_id; + struct pci_dev *pci = tac_get_pci_dev(slave); + + if (pci) + scnprintf(name, size, "%04X-%04X-%1X-%1X.bin", part_id, + pci->subsystem_device, bus->link_id, unique_id); + else + /* Default firmware name based on part ID */ + scnprintf(name, size, "%s%04x-%1X-%1X.bin", + part_id == 0x2883 ? "tas" : "tac", + part_id, bus->link_id, unique_id); +} + +static int tac_io_init(struct device *dev, struct sdw_slave *slave, bool first) +{ + struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev); + u64 time; + int ret; + + if (tac_dev->hw_init) { + dev_dbg(dev, "early return hw_init already done.."); + return 0; + } + + time = wait_for_completion_timeout(&tac_dev->fw_caching_complete, + msecs_to_jiffies(TAC5XX2_FW_CACHE_TIMEOUT_MS)); + if (!time) { + ret = -ETIMEDOUT; + dev_warn(tac_dev->dev, "%s: fw caching timeout\n", __func__); + goto io_init_err; + } + + if (tac_dev->fw_files && tac_dev->fw_file_cnt > 0) { + ret = tac_download_fw_to_hw(tac_dev); + if (ret) { + dev_err(tac_dev->dev, "FW download failed, fw: %d\n", ret); + goto io_init_err; + } + } + + if (tac_dev->sa_func_data) { + ret = sdca_regmap_write_init(dev, tac_dev->regmap, + tac_dev->sa_func_data); + if (ret) { + dev_err(dev, "smartamp init table update failed\n"); + goto io_init_err; + } + dev_dbg(dev, "smartamp init done\n"); + + if (first) { + ret = regmap_multi_reg_write(tac_dev->regmap, tac_spk_seq, + ARRAY_SIZE(tac_spk_seq)); + if (ret) { + dev_err(dev, "init writes failed, err=%d", ret); + goto io_init_err; + } + } + } + + if (tac_dev->sm_func_data) { + ret = sdca_regmap_write_init(dev, tac_dev->regmap, + tac_dev->sm_func_data); + if (ret) { + dev_err(dev, "smartmic init table update failed\n"); + goto io_init_err; + } + dev_dbg(dev, "smartmic init done\n"); + + if (first) { + ret = regmap_multi_reg_write(tac_dev->regmap, tac_sm_seq, + ARRAY_SIZE(tac_sm_seq)); + if (ret) { + dev_err(tac_dev->dev, + "init writes failed, err=%d", ret); + goto io_init_err; + } + } + } + + if (tac_dev->uaj_func_data) { + ret = sdca_regmap_write_init(dev, tac_dev->regmap, + tac_dev->uaj_func_data); + if (ret) { + dev_err(dev, "uaj init table update failed\n"); + goto io_init_err; + } + dev_dbg(dev, "uaj init done\n"); + + if (first) { + ret = regmap_multi_reg_write(tac_dev->regmap, tac_uaj_seq, + ARRAY_SIZE(tac_uaj_seq)); + if (ret) { + dev_err(tac_dev->dev, + "init writes failed, err=%d", ret); + goto io_init_err; + } + + if (tac_dev->hs_jack) { + ret = tac5xx2_jack_init(tac_dev); + if (ret) { + dev_err(tac_dev->dev, "jack init failed"); + goto io_init_err; + } + } + } + } + + if (tac_dev->hid_func_data) { + ret = sdca_regmap_write_init(dev, tac_dev->regmap, + tac_dev->hid_func_data); + if (ret) { + dev_err(dev, "hid init table update failed\n"); + goto io_init_err; + } + dev_dbg(dev, "hid init done\n"); + } + + tac_dev->hw_init = true; + + return 0; + +io_init_err: + dev_err(dev, "init writes failed, err=%d", ret); + return ret; +} + +static int tac_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&slave->dev); + struct device *dev = &slave->dev; + bool first = false; + int ret; + + tac_dev->status = status; + if (status == SDW_SLAVE_UNATTACHED) { + tac_dev->hw_init = false; + tac_dev->fw_dl_success = false; + } + + if (tac_dev->hw_init || tac_dev->status != SDW_SLAVE_ATTACHED) { + dev_dbg(dev, "%s: early return, hw_init=%d, status=%d", + __func__, tac_dev->hw_init, tac_dev->status); + return 0; + } + + if (!tac_dev->first_hw_init_done) { + pm_runtime_set_active(tac_dev->dev); + tac_dev->first_hw_init_done = true; + first = true; + } + + pm_runtime_get_noresume(tac_dev->dev); + + regcache_mark_dirty(tac_dev->regmap); + regcache_cache_only(tac_dev->regmap, false); + ret = tac_io_init(&slave->dev, slave, first); + if (ret) { + dev_err(dev, "Device initialization failed: %d\n", ret); + goto err_out; + } + + ret = regcache_sync(tac_dev->regmap); + if (ret) + dev_warn(dev, "Failed to sync regcache after init: %d\n", ret); + +err_out: + pm_runtime_mark_last_busy(tac_dev->dev); + pm_runtime_put_autosuspend(tac_dev->dev); + + return ret; +} + +static int tac5xx2_sdw_read_prop(struct sdw_slave *peripheral) +{ + struct device *dev = &peripheral->dev; + int ret; + + ret = sdw_slave_read_prop(peripheral); + if (ret) { + dev_err(dev, "sdw_slave_read_prop failed: %d", ret); + return ret; + } + + return 0; +} + +static int tac_port_prep(struct sdw_slave *slave, struct sdw_prepare_ch *prep_ch, + enum sdw_port_prep_ops pre_ops) +{ + struct device *dev = &slave->dev; + struct tac5xx2_prv *tac_dev = dev_get_drvdata(dev); + unsigned int val; + int ret; + + if (pre_ops != SDW_OPS_PORT_POST_PREP) + return 0; + + if (!tac_dev->fw_dl_success) + return 0; + + ret = regmap_read(tac_dev->regmap, TAC_DSP_ALGO_STATUS, &val); + if (ret) { + dev_err(dev, "Failed to read algo status: %d\n", ret); + return ret; + } + + if (val != TAC_DSP_ALGO_STATUS_RUNNING) { + dev_dbg(dev, "Algo not running (0x%02x), re-enabling\n", val); + ret = regmap_write(tac_dev->regmap, TAC_DSP_ALGO_STATUS, + TAC_DSP_ALGO_STATUS_RUNNING); + if (ret) { + dev_err(dev, "Failed to re-enable algo: %d\n", ret); + return ret; + } + } + + return 0; +} + +static const struct sdw_slave_ops tac_sdw_ops = { + .read_prop = tac5xx2_sdw_read_prop, + .update_status = tac_update_status, + .interrupt_callback = tac_interrupt_callback, + .port_prep = tac_port_prep, +}; + +static s32 tac_sdw_probe(struct sdw_slave *peripheral, + const struct sdw_device_id *id) +{ + struct sdca_function_data *function_data = NULL; + struct device *dev = &peripheral->dev; + struct tac5xx2_prv *tac_dev; + struct regmap *regmap; + int ret, i; + + tac_dev = devm_kzalloc(dev, sizeof(*tac_dev), GFP_KERNEL); + if (!tac_dev) + return dev_err_probe(dev, -ENOMEM, + "Failed devm_kzalloc"); + + if (peripheral->sdca_data.num_functions > 0) { + dev_dbg(dev, "SDCA functions found: %d", + peripheral->sdca_data.num_functions); + + for (i = 0; i < peripheral->sdca_data.num_functions; i++) { + struct sdca_function_data **func_ptr; + const char *func_name; + + switch (peripheral->sdca_data.function[i].type) { + case SDCA_FUNCTION_TYPE_SMART_AMP: + func_ptr = &tac_dev->sa_func_data; + func_name = "smartamp"; + break; + case SDCA_FUNCTION_TYPE_SMART_MIC: + func_ptr = &tac_dev->sm_func_data; + func_name = "smartmic"; + break; + case SDCA_FUNCTION_TYPE_UAJ: + func_ptr = &tac_dev->uaj_func_data; + func_name = "uaj"; + break; + case SDCA_FUNCTION_TYPE_HID: + func_ptr = &tac_dev->hid_func_data; + func_name = "hid"; + break; + default: + continue; + } + + function_data = devm_kzalloc(dev, sizeof(*function_data), + GFP_KERNEL); + if (!function_data) + return dev_err_probe(dev, -ENOMEM, + "failed to allocate %s function data", + func_name); + function_data->desc = &peripheral->sdca_data.function[i]; + ret = sdca_parse_function(dev, peripheral, function_data); + if (!ret) + *func_ptr = function_data; + else + devm_kfree(dev, function_data); + } + } + + dev_dbg(dev, "SDCA functions enabled: SA=%s SM=%s UAJ=%s HID=%s", + tac_dev->sa_func_data ? "yes" : "no", + tac_dev->sm_func_data ? "yes" : "no", + tac_dev->uaj_func_data ? "yes" : "no", + tac_dev->hid_func_data ? "yes" : "no"); + + tac_dev->dev = dev; + tac_dev->sdw_peripheral = peripheral; + tac_dev->hw_init = false; + tac_dev->first_hw_init_done = false; + tac_dev->part_id = id->part_id; + dev_set_drvdata(dev, tac_dev); + + regmap = devm_regmap_init_sdw_mbq_cfg(&peripheral->dev, peripheral, + &tac_regmap, &tac_mbq_cfg); + if (IS_ERR(regmap)) + return dev_err_probe(dev, PTR_ERR(regmap), + "Failed devm_regmap_init_sdw\n"); + + regcache_cache_only(regmap, true); + tac_dev->regmap = regmap; + tac_dev->jack_type = 0; + init_completion(&tac_dev->fw_caching_complete); + + if (tac_has_dsp_algo(tac_dev)) { + tac_generate_fw_name(peripheral, tac_dev->fw_binaryname, + sizeof(tac_dev->fw_binaryname)); + + ret = tac_load_and_cache_firmware_async(tac_dev); + if (ret) { + complete_all(&tac_dev->fw_caching_complete); + dev_dbg(dev, "failed to load fw: %d, use rom mode\n", ret); + } + } else { + complete_all(&tac_dev->fw_caching_complete); + } + + ret = tac_init(tac_dev); + if (ret) + return dev_err_probe(dev, ret, + "failed to initialize tac device\n"); + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(dev); + + pm_runtime_enable(dev); + /* the device is still not in active */ + + return 0; +} + +static void tac_sdw_remove(struct sdw_slave *peripheral) +{ + struct tac5xx2_prv *tac_dev = dev_get_drvdata(&peripheral->dev); + + pm_runtime_disable(tac_dev->dev); + + dev_set_drvdata(&peripheral->dev, NULL); +} + +static const struct sdw_device_id tac_sdw_id[] = { + SDW_SLAVE_ENTRY(0x0102, 0x5572, 0), + SDW_SLAVE_ENTRY(0x0102, 0x5672, 0), + SDW_SLAVE_ENTRY(0x0102, 0x5682, 0), + SDW_SLAVE_ENTRY(0x0102, 0x2883, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, tac_sdw_id); + +static struct sdw_driver tac_sdw_driver = { + .driver = { + .name = "slave-tac5xx2", + .pm = pm_ptr(&tac5xx2_sdca_pm), + }, + .probe = tac_sdw_probe, + .remove = tac_sdw_remove, + .ops = &tac_sdw_ops, + .id_table = tac_sdw_id, +}; +module_sdw_driver(tac_sdw_driver); + +MODULE_IMPORT_NS("SND_SOC_SDCA"); +MODULE_AUTHOR("Texas Instruments Inc."); +MODULE_DESCRIPTION("ASoC TAC5XX2 SoundWire Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tac5xx2.h b/sound/soc/codecs/tac5xx2.h new file mode 100644 index 000000000000..eed8e6cf3498 --- /dev/null +++ b/sound/soc/codecs/tac5xx2.h @@ -0,0 +1,259 @@ +/* SPDX-License-Identifier: GPL-2.0 + * + * ALSA SoC Texas Instruments TAC5XX2 Audio Smart Amplifier + * + * Copyright (C) 2025 Texas Instruments Incorporated + * https://www.ti.com + * + * This the header file for TAC5XX2 family of devices + * which includes TAC5572, TAC5672, TAC5682 and TAS2883 + * + * Author: Niranjan H Y <niranjanhy@ti.com> + */ +#ifndef __RGL_TAC5XX2_H__ +#define __RGL_TAC5XX2_H__ + +/* for soundwire */ +#define TAC_REG_SDW(book, page, reg) (((book) * 256 * 128) + \ + 0x3000000 + ((page) * 128) + (reg)) + +/* page 0 registers */ +#define TAC_SW_RESET TAC_REG_SDW(0, 0, 1) +#define TAC_SLEEP_MODEZ TAC_REG_SDW(0, 0, 2) +#define TAC_FEATURE_PDZ TAC_REG_SDW(0, 0, 3) +#define TAC_TX_CH_EN TAC_REG_SDW(0, 0, 4) +#define TAC_RX_CH_PD TAC_REG_SDW(0, 0, 5) +#define TAC_SHDNZ_CFG TAC_REG_SDW(0, 0, 6) +#define TAC_MISC_CFG0 TAC_REG_SDW(0, 0, 7) +#define TAC_MISC_CFG1 TAC_REG_SDW(0, 0, 8) +#define TAC_GPIO1_CFG0 TAC_REG_SDW(0, 0, 9) +#define TAC_GPIO2_CFG0 TAC_REG_SDW(0, 0, 10) +#define TAC_GPIO3_CFG0 TAC_REG_SDW(0, 0, 11) +#define TAC_GPIO4_CFG0 TAC_REG_SDW(0, 0, 12) +#define TAC_GPIO5_CFG0 TAC_REG_SDW(0, 0, 13) +#define TAC_GPIO6_CFG0 TAC_REG_SDW(0, 0, 14) +#define TAC_INTF_CFG1 TAC_REG_SDW(0, 0, 15) +#define TAC_INTF_CFG5 TAC_REG_SDW(0, 0, 16) +#define TAC_PASI_BCLK_CFG0 TAC_REG_SDW(0, 0, 17) +#define TAC_PASI_FSYNC_CFG0 TAC_REG_SDW(0, 0, 18) +#define TAC_PASI_DIN1_CFG0 TAC_REG_SDW(0, 0, 19) +#define TAC_PASI_DIN2_CFG0 TAC_REG_SDW(0, 0, 20) +#define TAC_PDM_DIN1_CFG0 TAC_REG_SDW(0, 0, 21) +#define TAC_PDM_DIN2_CFG0 TAC_REG_SDW(0, 0, 22) +#define TAC_MCLK_SEL TAC_REG_SDW(0, 0, 23) +#define TAC_I2C2_CFG0 TAC_REG_SDW(0, 0, 24) +#define TAC_SDW_IO_CFG0 TAC_REG_SDW(0, 0, 25) +#define TAC_SDW_CLK_CFG0 TAC_REG_SDW(0, 0, 26) +#define TAC_PASI_CFG0 TAC_REG_SDW(0, 0, 27) +#define TAC_PASI_CFG1 TAC_REG_SDW(0, 0, 28) +#define TAC_PASI_TX_CFG0 TAC_REG_SDW(0, 0, 29) +#define TAC_PASI_TX_CFG1 TAC_REG_SDW(0, 0, 30) +#define TAC_PASI_TX_CFG2 TAC_REG_SDW(0, 0, 31) +#define TAC_PASI_TX_CFG3 TAC_REG_SDW(0, 0, 32) +#define TAC_PASI_TX_CH1_CFG0 TAC_REG_SDW(0, 0, 33) +#define TAC_PASI_TX_CH2_CFG0 TAC_REG_SDW(0, 0, 34) +#define TAC_PASI_TX_CH3_CFG0 TAC_REG_SDW(0, 0, 35) +#define TAC_PASI_TX_CH4_CFG0 TAC_REG_SDW(0, 0, 36) +#define TAC_PASI_TX_CH5_CFG0 TAC_REG_SDW(0, 0, 37) +#define TAC_PASI_TX_CH6_CFG0 TAC_REG_SDW(0, 0, 38) +#define TAC_PASI_TX_CH7_CFG0 TAC_REG_SDW(0, 0, 39) +#define TAC_PASI_TX_CH8_CFG0 TAC_REG_SDW(0, 0, 40) +#define TAC_PASI_RX_CFG0 TAC_REG_SDW(0, 0, 41) +#define TAC_PASI_RX_CFG1 TAC_REG_SDW(0, 0, 42) +#define TAC_PASI_RX_CFG2 TAC_REG_SDW(0, 0, 43) +#define TAC_PASI_RX_CH1_CFG0 TAC_REG_SDW(0, 0, 44) +#define TAC_PASI_RX_CH2_CFG0 TAC_REG_SDW(0, 0, 45) +#define TAC_PASI_RX_CH3_CFG0 TAC_REG_SDW(0, 0, 46) +#define TAC_PASI_RX_CH4_CFG0 TAC_REG_SDW(0, 0, 47) +#define TAC_PASI_RX_CH5_CFG0 TAC_REG_SDW(0, 0, 48) +#define TAC_PASI_RX_CH6_CFG0 TAC_REG_SDW(0, 0, 49) +#define TAC_PASI_RX_CH7_CFG0 TAC_REG_SDW(0, 0, 50) +#define TAC_PASI_RX_CH8_CFG0 TAC_REG_SDW(0, 0, 51) +#define TAC_ADC_CH1_CFG0 TAC_REG_SDW(0, 0, 52) +#define TAC_ADC_DVOL_CFG0 TAC_REG_SDW(0, 0, 53) +#define TAC_ADC_CH1_FGAIN TAC_REG_SDW(0, 0, 54) +#define TAC_ADC_CH1_CFG1 TAC_REG_SDW(0, 0, 55) +#define TAC_ADC_CH2_CFG0 TAC_REG_SDW(0, 0, 57) +#define TAC_ADC_DVOL_CFG1 TAC_REG_SDW(0, 0, 58) +#define TAC_ADC_CH2_FGAIN TAC_REG_SDW(0, 0, 59) +#define TAC_ADC_CH2_CFG1 TAC_REG_SDW(0, 0, 60) +#define TAC_ADC_CFG1 TAC_REG_SDW(0, 0, 62) +#define TAC_PDM_CH1_DVOL TAC_REG_SDW(0, 0, 63) +#define TAC_PDM_CH1_FGAIN TAC_REG_SDW(0, 0, 64) +#define TAC_PDM_CH1_CFG0 TAC_REG_SDW(0, 0, 65) +#define TAC_PDM_CH2_DVOL TAC_REG_SDW(0, 0, 67) +#define TAC_PDM_CH2_FGAIN TAC_REG_SDW(0, 0, 68) +#define TAC_PDM_CH2_CFG2 TAC_REG_SDW(0, 0, 69) +#define TAC_PDM_CH3_DVOL TAC_REG_SDW(0, 0, 71) +#define TAC_PDM_CH3_FGAIN TAC_REG_SDW(0, 0, 72) +#define TAC_PDM_CH3_CFG0 TAC_REG_SDW(0, 0, 73) +#define TAC_PDM_CH4_DVOL TAC_REG_SDW(0, 0, 75) +#define TAC_PDM_CH4_FGAIN TAC_REG_SDW(0, 0, 76) +#define TAC_PDM_CH4_CFG0 TAC_REG_SDW(0, 0, 77) +#define TAC_MICBIAS_CFG0 TAC_REG_SDW(0, 0, 79) +#define TAC_MICPREAMP_CFG TAC_REG_SDW(0, 0, 80) +#define TAC_MICBIAS_CFG1 TAC_REG_SDW(0, 0, 81) +#define TAC_CLASSD_CH1_DVOL TAC_REG_SDW(0, 0, 82) +#define TAC_CLASSD_CH1_FGAIN TAC_REG_SDW(0, 0, 83) +#define TAC_CLASSD_CH2_DVOL TAC_REG_SDW(0, 0, 85) +#define TAC_CLASSD_CH2_FGAIN TAC_REG_SDW(0, 0, 86) +#define TAC_GCHP_CH1_DVOL TAC_REG_SDW(0, 0, 88) +#define TAC_GCHP_CH1_FGAIN TAC_REG_SDW(0, 0, 89) +#define TAC_GCHP_CH2_DVOL TAC_REG_SDW(0, 0, 91) +#define TAC_GCHP_CH2_FGAIN TAC_REG_SDW(0, 0, 92) +#define TAC_AMP_LVL_CFG0 TAC_REG_SDW(0, 0, 94) +#define TAC_AMP_LVL_CFG1 TAC_REG_SDW(0, 0, 95) +#define TAC_AMP_LVL_CFG2 TAC_REG_SDW(0, 0, 96) +#define TAC_AMP_LVL_CFG3 TAC_REG_SDW(0, 0, 97) +#define TAC_EFF_MODE_CFG0 TAC_REG_SDW(0, 0, 98) +#define TAC_EFF_MODE_CFG1 TAC_REG_SDW(0, 0, 99) +#define TAC_CLASSD_CFG0 TAC_REG_SDW(0, 0, 100) +#define TAC_CLASSD_CFG1 TAC_REG_SDW(0, 0, 101) +#define TAC_CLASSD_CFG3 TAC_REG_SDW(0, 0, 102) +#define TAC_CLASSD_CFG4 TAC_REG_SDW(0, 0, 103) +#define TAC_CLASSD_CFG5 TAC_REG_SDW(0, 0, 104) +#define TAC_CLASSD_CFG6 TAC_REG_SDW(0, 0, 105) +#define TAC_CLASSD_CFG8 TAC_REG_SDW(0, 0, 106) +#define TAC_ISNS_CFG TAC_REG_SDW(0, 0, 107) +#define TAC_DSP_CFG0 TAC_REG_SDW(0, 0, 108) +#define TAC_DSP_CFG1 TAC_REG_SDW(0, 0, 109) +#define TAC_DSP_CFG2 TAC_REG_SDW(0, 0, 110) +#define TAC_DSP_CFG3 TAC_REG_SDW(0, 0, 111) +#define TAC_JACK_DET_CFG1 TAC_REG_SDW(0, 0, 112) +#define TAC_JACK_DET_CFG2 TAC_REG_SDW(0, 0, 113) +#define TAC_JACK_DET_CFG3 TAC_REG_SDW(0, 0, 114) +#define TAC_JACK_DET_CFG4 TAC_REG_SDW(0, 0, 115) +#define TAC_JACK_DET_CFG7 TAC_REG_SDW(0, 0, 116) +#define TAC_UJ_IMPEDANCE_L TAC_REG_SDW(0, 0, 117) +#define TAC_UJ_IMPEDANCE_R TAC_REG_SDW(0, 0, 118) +#define UJ_IMPEDANCE_L TAC_REG_SDW(0, 0, 119) +#define UJ_IMPEDANCE_R TAC_REG_SDW(0, 0, 120) +#define TAC_GP_ANA_STS TAC_REG_SDW(0, 0, 123) +#define TAC_DEV_ID TAC_REG_SDW(0, 0, 124) +#define TAC_REV_ID TAC_REG_SDW(0, 0, 125) +#define TAC_I2C_CKSUM TAC_REG_SDW(0, 0, 126) +#define TAC_BOOK TAC_REG_SDW(0, 0, 127) + +#define TAC_INT_CFG TAC_REG_SDW(0, 2, 1) +#define TAC_INT_CFG_CLR_REG BIT(3) + +/* smartamp function */ +#define TAC_FUNCTION_ID_SA 0x1 + +#define TAC_SDCA_ENT_ENT0 0x0 +#define TAC_SDCA_ENT_PPU21 0x1 +#define TAC_SDCA_ENT_FU21 0x2 +#define TAC_SDCA_ENT_FU26 0x3 +#define TAC_SDCA_ENT_XU22 0x4 +#define TAC_SDCA_ENT_CS24 0x5 +#define TAC_SDCA_ENT_CS21 0x6 +#define TAC_SDCA_ENT_CS25 0x7 +#define TAC_SDCA_ENT_CS26 0x8 +#define TAC_SDCA_ENT_CS28 0x9 +#define TAC_SDCA_ENT_PPU26 0xa +#define TAC_SDCA_ENT_FU23 0xb +#define TAC_SDCA_ENT_PDE23 0xc +#define TAC_SDCA_ENT_TG23 0x12 +#define TAC_SDCA_ENT_IT21 0x13 +#define TAC_SDCA_ENT_IT29 0x14 +#define TAC_SDCA_ENT_IT26 0x15 +#define TAC_SDCA_ENT_IT28 0x16 +#define TAC_SDCA_ENT_OT24 0x17 +#define TAC_SDCA_ENT_OT23 0x18 +#define TAC_SDCA_ENT_OT25 0x19 +#define TAC_SDCA_ENT_OT28 0x1a +#define TAC_SDCA_ENT_OT27 0x1c +#define TAC_SDCA_ENT_SPE199 0x21 +#define TAC_SDCA_ENT_OT20 0x24 +#define TAC_SDCA_ENT_FU27 0x26 +#define TAC_SDCA_ENT_FU20 0x27 +#define TAC_SDCA_ENT_PDE24 0x2e +#define TAC_SDCA_ENT_PDE27 0x2f +#define TAC_SDCA_ENT_PDE28 0x30 +#define TAC_SDCA_ENT_PDE20 0x31 +#define TAC_SDCA_ENT_SAPU29 0x35 + +/* Control selector definitions */ +#define TAC_SDCA_MASTER_GAIN 0x0B +#define TAC_SDCA_MASTER_MUTE 0x01 +#define TAC_SDCA_CHANNEL_MUTE 0x01 +#define TAC_SDCA_CHANNEL_GAIN 0x02 +#define TAC_SDCA_POSTURENUMBER 0x10 +#define TAC_SDCA_REQUESTED_PS 0x01 +#define TAC_SDCA_ACTUAL_PS 0x10 +#define TAC_SDCA_CHANNEL_VOLUME 0x02 + +/* 2. smart mic function */ +#define TAC_FUNCTION_ID_SM 0x2 + +#define TAC_SDCA_ENT_IT11 0x1 +#define TAC_SDCA_ENT_OT113 0x2 +#define TAC_SDCA_ENT_CS11 0x3 +#define TAC_SDCA_ENT_CS18 0x4 +#define TAC_SDCA_ENT_FU113 0x5 +#define TAC_SDCA_ENT_FU13 0x6 +#define TAC_SDCA_ENT_FU11 0x8 +#define TAC_SDCA_ENT_XU12 0xa +#define TAC_SDCA_ENT_CS113 0xc +#define TAC_SDCA_ENT_CX11 0xf +#define TAC_SDCA_ENT_PDE11 0x12 +#define TAC_SDCA_ENT_PPU11 0x9 + +/* controls */ +#define TAC_SDCA_CTL_USAGE 0x04 +#define TAC_SDCA_CTL_IT_CLUSTER 0x10 +#define TAC_SDCA_CTL_OT_DP_SEL 0x11 +#define TAC_SDCA_CTL_XU_BYPASS 0x01 +/* cx */ +#define TAC_SDCA_CTL_CX_CLK_SEL 0x01 +/* cs */ +#define TAC_SDCA_CTL_CS_CLKVLD 0x02 +#define TAC_SDCA_CTL_CS_SAMP_RATE_IDX 0x10 +/* cs113 end */ +/* ppu */ +#define TAC_SDCA_CTL_PPU_POSTURE_NUM 0x10 + +/* 3. UAJ function */ +#define TAC_FUNCTION_ID_UAJ 0x3 +#define TAC_SDCA_ENT_PDE47 0x35 +#define TAC_SDCA_ENT_PDE34 0x32 +#define TAC_SDCA_ENT_FU41 0x26 /* user */ +#define TAC_SDCA_ENT_IT41 0x07 +#define TAC_SDCA_ENT_XU42 0x2C +#define TAC_SDCA_ENT_CS41 0x30 +#define TAC_SDCA_ENT_OT45 0x0E +#define TAC_SDCA_ENT_IT33 0x03 +#define TAC_SDCA_ENT_OT36 0x0A +#define TAC_SDCA_ENT_FU36 0x28 +#define TAC_SDCA_ENT_CS36 0x2E +#define TAC_SDCA_ENT_GE35 0x3B /* 59 */ + +#define TAC_SDCA_CTL_SEL_MODE 0x1 +#define TAC_SDCA_CTL_DET_MODE 0x2 + +/* 4. HID function */ +#define TAC_FUNCTION_ID_HID 0x4 +#define TAC_SDCA_ENT_HID1 0x1 +/* HID Control Selectors */ +#define TAC_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define TAC_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define TAC_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define TAC_SDCA_CTL_DETECTED_MODE 0x10 +#define TAC_SDCA_CTL_SELECTED_MODE 0x11 + +#define TAC_BUF_ADDR_HID1 0x44007F80 + +/* DAI interfaces */ +#define TAC5XX2_SPK 0 +#define TAC5XX2_DMIC 2 +#define TAC5XX2_UAJ 3 + +/* Port numbers for DAIs */ +#define TAC_SDW_PORT_NUM_SPK_PLAYBACK 1 +#define TAC_SDW_PORT_NUM_SPK_CAPTURE 2 +#define TAC_SDW_PORT_NUM_DMIC 3 +#define TAC_SDW_PORT_NUM_UAJ_PLAYBACK 4 +#define TAC_SDW_PORT_NUM_UAJ_CAPTURE 7 +#define TAC_SDW_PORT_NUM_IV_SENSE 8 + +#endif diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 80206c2e0946..1a7650b9b2a7 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -745,7 +745,7 @@ static void tas2552_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id tas2552_id[] = { - { "tas2552" }, + { .name = "tas2552" }, { } }; MODULE_DEVICE_TABLE(i2c, tas2552_id); diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index ceb367ae05ba..e1d62f30418a 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -675,11 +675,12 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562) if (tas2562->sdz_gpio == NULL) { tas2562->sdz_gpio = devm_gpiod_get_optional(dev, "shut-down", GPIOD_OUT_HIGH); - if (IS_ERR(tas2562->sdz_gpio)) + if (IS_ERR(tas2562->sdz_gpio)) { if (PTR_ERR(tas2562->sdz_gpio) == -EPROBE_DEFER) return -EPROBE_DEFER; - tas2562->sdz_gpio = NULL; + tas2562->sdz_gpio = NULL; + } } if (tas2562->model_id == TAS2110) @@ -711,9 +712,9 @@ static int tas2562_parse_dt(struct tas2562_data *tas2562) } static const struct i2c_device_id tas2562_id[] = { - { "tas2562", TAS2562 }, - { "tas2564", TAS2564 }, - { "tas2110", TAS2110 }, + { .name = "tas2562", .driver_data = TAS2562 }, + { .name = "tas2564", .driver_data = TAS2564 }, + { .name = "tas2110", .driver_data = TAS2110 }, { } }; MODULE_DEVICE_TABLE(i2c, tas2562_id); diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 55211266927d..b11b998aa32a 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -1036,7 +1036,7 @@ static int tas2764_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id tas2764_i2c_id[] = { - { "tas2764"}, + { .name = "tas2764" }, { } }; MODULE_DEVICE_TABLE(i2c, tas2764_i2c_id); diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index dbda9f327535..e79754edac5c 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -958,7 +958,7 @@ static int tas2770_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id tas2770_i2c_id[] = { - { "tas2770"}, + { .name = "tas2770" }, { } }; MODULE_DEVICE_TABLE(i2c, tas2770_i2c_id); diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index cf3f6abd7e7b..1ec1c076204f 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -621,7 +621,7 @@ static int tas2780_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id tas2780_i2c_id[] = { - { "tas2780"}, + { .name = "tas2780" }, { } }; MODULE_DEVICE_TABLE(i2c, tas2780_i2c_id); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c index 885e0b6fed00..bd16d5326a23 100644 --- a/sound/soc/codecs/tas2781-fmwlib.c +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -921,7 +921,8 @@ static int tasdevice_process_block(void *context, unsigned char *data, data[subblk_offset + 1], data[subblk_offset + 2]), data[subblk_offset + 3]); - if (rc < 0) { + if (rc < 0 && + !(tas_priv->isspi && rc == -EXDEV)) { is_err = true; dev_err(tas_priv->dev, "process_block: single write error\n"); @@ -953,7 +954,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, data[subblk_offset + 1], data[subblk_offset + 2]), &(data[subblk_offset + 4]), len); - if (rc < 0) { + if (rc < 0 && !(tas_priv->isspi && rc == -EXDEV)) { is_err = true; dev_err(tas_priv->dev, "%s: bulk_write error = %d\n", @@ -991,7 +992,7 @@ static int tasdevice_process_block(void *context, unsigned char *data, data[subblk_offset + 4]), data[subblk_offset + 1], data[subblk_offset + 5]); - if (rc < 0) { + if (rc < 0 && !(tas_priv->isspi && rc == -EXDEV)) { is_err = true; dev_err(tas_priv->dev, "%s: update_bits error = %d\n", diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c index a78a8f9b9833..9e6f0ad5f05d 100644 --- a/sound/soc/codecs/tas2781-i2c.c +++ b/sound/soc/codecs/tas2781-i2c.c @@ -100,27 +100,28 @@ static const struct bulk_reg_val tas2781_cali_start_reg[] = { }; static const struct i2c_device_id tasdevice_id[] = { - { "tas2020", TAS2020 }, - { "tas2118", TAS2118 }, - { "tas2120", TAS2120 }, - { "tas2320", TAS2320 }, - { "tas2563", TAS2563 }, - { "tas2568", TAS2568 }, - { "tas2570", TAS2570 }, - { "tas2572", TAS2572 }, - { "tas2574", TAS2574 }, - { "tas2781", TAS2781 }, - { "tas5802", TAS5802 }, - { "tas5806m", TAS5806M }, - { "tas5806md", TAS5806MD }, - { "tas5815", TAS5815 }, - { "tas5822", TAS5822 }, - { "tas5825", TAS5825 }, - { "tas5827", TAS5827 }, - { "tas5828", TAS5828 }, - { "tas5830", TAS5830 }, - { "tas5832", TAS5832 }, - {} + { .name = "tas2020", .driver_data = TAS2020 }, + { .name = "tas2118", .driver_data = TAS2118 }, + { .name = "tas2120", .driver_data = TAS2120 }, + { .name = "tas2320", .driver_data = TAS2320 }, + { .name = "tas2563", .driver_data = TAS2563 }, + { .name = "tas2568", .driver_data = TAS2568 }, + { .name = "tas2570", .driver_data = TAS2570 }, + { .name = "tas2572", .driver_data = TAS2572 }, + { .name = "tas2573", .driver_data = TAS2573 }, + { .name = "tas2574", .driver_data = TAS2574 }, + { .name = "tas2781", .driver_data = TAS2781 }, + { .name = "tas5802", .driver_data = TAS5802 }, + { .name = "tas5806m", .driver_data = TAS5806M }, + { .name = "tas5806md", .driver_data = TAS5806MD }, + { .name = "tas5815", .driver_data = TAS5815 }, + { .name = "tas5822", .driver_data = TAS5822 }, + { .name = "tas5825", .driver_data = TAS5825 }, + { .name = "tas5827", .driver_data = TAS5827 }, + { .name = "tas5828", .driver_data = TAS5828 }, + { .name = "tas5830", .driver_data = TAS5830 }, + { .name = "tas5832", .driver_data = TAS5832 }, + { } }; static const struct of_device_id tasdevice_of_match[] = { @@ -132,6 +133,7 @@ static const struct of_device_id tasdevice_of_match[] = { { .compatible = "ti,tas2568", .data = &tasdevice_id[TAS2568] }, { .compatible = "ti,tas2570", .data = &tasdevice_id[TAS2570] }, { .compatible = "ti,tas2572", .data = &tasdevice_id[TAS2572] }, + { .compatible = "ti,tas2573", .data = &tasdevice_id[TAS2573] }, { .compatible = "ti,tas2574", .data = &tasdevice_id[TAS2574] }, { .compatible = "ti,tas2781", .data = &tasdevice_id[TAS2781] }, { .compatible = "ti,tas5802", .data = &tasdevice_id[TAS5802] }, @@ -1683,7 +1685,7 @@ static void tasdevice_fw_ready(const struct firmware *fmw, tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; /* There is no calibration required for TAS58XX. */ - if (tas_priv->chip_id < TAS5802) { + if (tas_priv->chip_id == TAS2563 || tas_priv->chip_id == TAS2781) { ret = tasdevice_create_cali_ctrls(tas_priv); if (ret) { dev_err(tas_priv->dev, "cali controls error\n"); @@ -1736,6 +1738,7 @@ out: if (tas_priv->fw_state == TASDEVICE_RCA_FW_OK) { switch (tas_priv->chip_id) { case TAS2563: + case TAS2573: case TAS2781: case TAS5802: case TAS5806M: @@ -1900,6 +1903,7 @@ static int tasdevice_codec_probe(struct snd_soc_component *codec) case TAS2568: case TAS2570: case TAS2572: + case TAS2573: case TAS2574: p = (struct snd_kcontrol_new *)tas2x20_snd_controls; size = ARRAY_SIZE(tas2x20_snd_controls); @@ -2094,6 +2098,7 @@ static const struct acpi_device_id tasdevice_acpi_match[] = { { "TXNW2568", (kernel_ulong_t)&tasdevice_id[TAS2568] }, { "TXNW2570", (kernel_ulong_t)&tasdevice_id[TAS2570] }, { "TXNW2572", (kernel_ulong_t)&tasdevice_id[TAS2572] }, + { "TXNW2573", (kernel_ulong_t)&tasdevice_id[TAS2573] }, { "TXNW2574", (kernel_ulong_t)&tasdevice_id[TAS2574] }, { "TXNW2781", (kernel_ulong_t)&tasdevice_id[TAS2781] }, { "TXNW5802", (kernel_ulong_t)&tasdevice_id[TAS5802] }, diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 90008d2d06e2..3d0b116544cc 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -100,6 +100,8 @@ struct tas2783_prv { wait_queue_head_t fw_wait; bool fw_dl_task_done; bool fw_dl_success; + /* use fallback fw name */ + bool fw_use_fallback; }; static const struct reg_default tas2783_reg_default[] = { @@ -740,11 +742,19 @@ static void tas2783_fw_ready(const struct firmware *fmw, void *context) goto out; } + /* firmware binary not found*/ if (!fmw || !fmw->data) { - /* firmware binary not found*/ - dev_err(tas_dev->dev, - "Failed to read fw binary %s\n", - tas_dev->rca_binaryname); + if (!tas_dev->fw_use_fallback) { + tas_dev->fw_use_fallback = true; + dev_info(tas_dev->dev, + "Failed to read preferred fw binary: %s, attempting fallback binary load\n", + tas_dev->rca_binaryname); + } else { + dev_err(tas_dev->dev, + "Failed to read fallback fw binary %s\n", + tas_dev->rca_binaryname); + } + ret = -EINVAL; goto out; } @@ -1011,7 +1021,6 @@ static s32 tas_component_probe(struct snd_soc_component *component) snd_soc_component_get_drvdata(component); tas_dev->component = component; - tas25xx_register_misc(tas_dev->sdw_peripheral); return 0; } @@ -1020,7 +1029,6 @@ static void tas_component_remove(struct snd_soc_component *codec) { struct tas2783_prv *tas_dev = snd_soc_component_get_drvdata(codec); - tas25xx_deregister_misc(); tas_dev->component = NULL; } @@ -1082,22 +1090,14 @@ static s32 tas2783_sdca_dev_resume(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct tas2783_prv *tas_dev = dev_get_drvdata(dev); - unsigned long t; - - if (!slave->unattach_request) - goto regmap_sync; + int ret; - t = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(TAS2783_PROBE_TIMEOUT)); - if (!t) { - dev_err(&slave->dev, "resume: initialization timed out\n"); + ret = sdw_slave_wait_for_init(slave, TAS2783_PROBE_TIMEOUT); + if (ret) { sdw_show_ping_status(slave->bus, true); - return -ETIMEDOUT; + return ret; } - slave->unattach_request = 0; - -regmap_sync: regcache_cache_only(tas_dev->regmap, false); regcache_sync(tas_dev->regmap); return 0; @@ -1115,13 +1115,16 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz bool pci_found = false; #if IS_ENABLED(CONFIG_PCI) struct device *dev = &slave->dev; + struct tas2783_prv *tas_dev = dev_get_drvdata(&slave->dev); struct pci_dev *pci = NULL; + const char *fw_uid_prefix = tas_dev->fw_use_fallback ? "" : "0x"; for (; dev; dev = dev->parent) { if (dev->bus == &pci_bus_type) { pci = to_pci_dev(dev); - scnprintf(name, size, "%04X-%1X-%1X.bin", - pci->subsystem_device, bus->link_id, unique_id); + scnprintf(name, size, "%04X-%1X-%s%1X.bin", + pci->subsystem_device, bus->link_id, + fw_uid_prefix, unique_id); pci_found = true; break; } @@ -1133,28 +1136,15 @@ static void tas_generate_fw_name(struct sdw_slave *slave, char *name, size_t siz bus->link_id, unique_id); } -static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) +static s32 tas_fw_load(struct tas2783_prv *tas_dev, struct sdw_slave *slave) { - struct tas2783_prv *tas_dev = dev_get_drvdata(dev); s32 ret; u8 unique_id = tas_dev->sdw_peripheral->id.unique_id; - if (tas_dev->hw_init) - return 0; - - tas_dev->fw_dl_task_done = false; - tas_dev->fw_dl_success = false; - - ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); - if (ret) { - dev_err(dev, "sw reset failed, err=%d", ret); - return ret; - } - usleep_range(2000, 2200); - tas_generate_fw_name(slave, tas_dev->rca_binaryname, sizeof(tas_dev->rca_binaryname)); + tas_dev->fw_dl_task_done = false; ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, tas_dev->rca_binaryname, tas_dev->dev, GFP_KERNEL, tas_dev, tas2783_fw_ready); @@ -1169,8 +1159,35 @@ static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) msecs_to_jiffies(TIMEOUT_FW_DL_MS)); if (!ret) { dev_err(tas_dev->dev, "fw request, wait_event timeout\n"); - ret = -EAGAIN; - } else { + return -EAGAIN; + } + + return 0; +} + +static s32 tas_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct tas2783_prv *tas_dev = dev_get_drvdata(dev); + s32 ret; + + if (tas_dev->hw_init) + return 0; + + tas_dev->fw_dl_success = false; + + ret = regmap_write(tas_dev->regmap, TAS2783_SW_RESET, 0x1); + if (ret) { + dev_err(dev, "sw reset failed, err=%d", ret); + return ret; + } + usleep_range(2000, 2200); + + tas_dev->fw_use_fallback = false; + ret = tas_fw_load(tas_dev, slave); + if (!ret && tas_dev->fw_use_fallback) + ret = tas_fw_load(tas_dev, slave); + + if (!ret) { if (tas_dev->sa_func_data) ret = sdca_regmap_write_init(dev, tas_dev->regmap, tas_dev->sa_func_data); @@ -1310,10 +1327,10 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, return dev_err_probe(dev, -ENOMEM, "failed to parse sdca functions"); + function_data->desc = &peripheral->sdca_data.function[i]; + /* Parse the function */ - ret = sdca_parse_function(dev, peripheral, - &peripheral->sdca_data.function[i], - function_data); + ret = sdca_parse_function(dev, peripheral, function_data); if (!ret) tas_dev->sa_func_data = function_data; else diff --git a/sound/soc/codecs/tas2783.h b/sound/soc/codecs/tas2783.h index bf34319c9a9f..d5996c73526c 100644 --- a/sound/soc/codecs/tas2783.h +++ b/sound/soc/codecs/tas2783.h @@ -100,12 +100,4 @@ #define TAS2783_CALIB_DATA_SZ ((TAS2783_CALIB_HDR_SZ) + TAS2783_CALIB_CRC_SZ + \ ((TAS2783_CALIB_PARAMS) * 4 * (TAS2783_CALIB_MAX_SPK_COUNT))) -#if IS_ENABLED(CONFIG_SND_SOC_TAS2783_UTIL) -int32_t tas25xx_register_misc(struct sdw_slave *peripheral); -int32_t tas25xx_deregister_misc(void); -#else -static void tas25xx_register_misc(struct sdw_slave *peripheral) {} -static void tas25xx_deregister_misc(void) {} -#endif - #endif /*__TAS2783_H__ */ diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index 12bf6a89dbd8..4d1c122c8f04 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -891,7 +891,7 @@ static const struct snd_soc_component_driver soc_component_dev_tas5086 = { }; static const struct i2c_device_id tas5086_i2c_id[] = { - { "tas5086" }, + { .name = "tas5086" }, { } }; MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id); @@ -975,17 +975,13 @@ static int tas5086_i2c_probe(struct i2c_client *i2c) return ret; } -static void tas5086_i2c_remove(struct i2c_client *i2c) -{} - static struct i2c_driver tas5086_i2c_driver = { .driver = { - .name = "tas5086", + .name = "tas5086", .of_match_table = of_match_ptr(tas5086_dt_ids), }, - .id_table = tas5086_i2c_id, - .probe = tas5086_i2c_probe, - .remove = tas5086_i2c_remove, + .id_table = tas5086_i2c_id, + .probe = tas5086_i2c_probe, }; module_i2c_driver(tas5086_i2c_driver); diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index 19ccf8641e16..8b5f9accf120 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -1064,13 +1064,13 @@ static const struct of_device_id tas571x_of_match[] __maybe_unused = { MODULE_DEVICE_TABLE(of, tas571x_of_match); static const struct i2c_device_id tas571x_i2c_id[] = { - { "tas5707", (kernel_ulong_t) &tas5707_chip }, - { "tas5711", (kernel_ulong_t) &tas5711_chip }, - { "tas5717", (kernel_ulong_t) &tas5717_chip }, - { "tas5719", (kernel_ulong_t) &tas5717_chip }, - { "tas5721", (kernel_ulong_t) &tas5721_chip }, - { "tas5733", (kernel_ulong_t) &tas5733_chip }, - { "tas5753", (kernel_ulong_t) &tas5753_chip }, + { .name = "tas5707", .driver_data = (kernel_ulong_t)&tas5707_chip }, + { .name = "tas5711", .driver_data = (kernel_ulong_t)&tas5711_chip }, + { .name = "tas5717", .driver_data = (kernel_ulong_t)&tas5717_chip }, + { .name = "tas5719", .driver_data = (kernel_ulong_t)&tas5717_chip }, + { .name = "tas5721", .driver_data = (kernel_ulong_t)&tas5721_chip }, + { .name = "tas5733", .driver_data = (kernel_ulong_t)&tas5733_chip }, + { .name = "tas5753", .driver_data = (kernel_ulong_t)&tas5753_chip }, { } }; MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id); diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index 2dcdd0a4bf80..0bbcfaadf3f2 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -716,9 +716,9 @@ static struct snd_soc_dai_driver tas5720_dai[] = { }; static const struct i2c_device_id tas5720_id[] = { - { "tas5720", TAS5720 }, - { "tas5720a-q1", TAS5720A_Q1 }, - { "tas5722", TAS5722 }, + { .name = "tas5720", .driver_data = TAS5720 }, + { .name = "tas5720a-q1", .driver_data = TAS5720A_Q1 }, + { .name = "tas5722", .driver_data = TAS5722 }, { } }; MODULE_DEVICE_TABLE(i2c, tas5720_id); diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index 867046b7aaa0..bcc8cab8d667 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -580,7 +580,7 @@ static void tas5805m_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id tas5805m_i2c_id[] = { - { "tas5805m", }, + { .name = "tas5805m" }, { } }; MODULE_DEVICE_TABLE(i2c, tas5805m_i2c_id); diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index 85ecc246896f..f5d50f8a1cfb 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -794,7 +794,7 @@ static void tas6424_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id tas6424_i2c_ids[] = { - { "tas6424" }, + { .name = "tas6424" }, { } }; MODULE_DEVICE_TABLE(i2c, tas6424_i2c_ids); diff --git a/sound/soc/codecs/tas675x.c b/sound/soc/codecs/tas675x.c new file mode 100644 index 000000000000..82526362de7b --- /dev/null +++ b/sound/soc/codecs/tas675x.c @@ -0,0 +1,2194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ALSA SoC Texas Instruments TAS67524 Quad-Channel Audio Amplifier + * + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Sen Wang <sen@ti.com> + */ + +#include <linux/bitfield.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/gpio/consumer.h> +#include <linux/regulator/consumer.h> +#include <linux/delay.h> +#include <linux/property.h> +#include <linux/interrupt.h> +#include <linux/workqueue.h> +#include <linux/pm_runtime.h> +#include <linux/iopoll.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/pcm_params.h> + +#include "tas675x.h" + +#define TAS675X_FAULT_CHECK_INTERVAL_MS 200 + +enum tas675x_type { + TAS67524, +}; + +struct tas675x_reg_param { + u8 page; + u8 reg; + u32 val; +}; + +struct tas675x_priv { + struct device *dev; + struct regmap *regmap; + enum tas675x_type dev_type; + /* Custom regmap lock; protects writes across books */ + struct mutex io_lock; + + struct gpio_desc *pd_gpio; + struct gpio_desc *stby_gpio; + struct regulator_bulk_data supplies[2]; + struct regulator *vbat; + bool fast_boot; + + int audio_slot; + int llp_slot; + int vpredict_slot; + int isense_slot; + int bclk_offset; + int slot_width; + unsigned int tx_mask; + + int gpio1_func; + int gpio2_func; + + unsigned long active_playback_dais; + unsigned long active_capture_dais; + unsigned int rate; + unsigned int saved_rtldg_en; +#define TAS675X_DSP_PARAM_NUM 2 + struct tas675x_reg_param dsp_params[TAS675X_DSP_PARAM_NUM]; + + /* Fault monitor, disabled when Fault IRQ is used */ + struct delayed_work fault_check_work; +#define TAS675X_FAULT_REGS_NUM 9 + unsigned int last_status[TAS675X_FAULT_REGS_NUM]; +}; + +static const char * const tas675x_supply_names[] = { + "dvdd", /* Digital power supply */ + "pvdd", /* Output powerstage supply */ +}; + +/* Page 1 setup initialization defaults */ +static const struct reg_sequence tas675x_page1_init[] = { + REG_SEQ0(TAS675X_PAGE_REG(1, 0xC8), 0x20), /* Charge pump clock */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0x2F), 0x90), /* VBAT idle */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0x29), 0x40), /* OC/CBC threshold */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0x2E), 0x0C), /* OC/CBC config */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0xC5), 0x02), /* OC/CBC config */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0xC6), 0x10), /* OC/CBC config */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0x1F), 0x20), /* OC/CBC config */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0x16), 0x01), /* OC/CBC config */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0x1E), 0x04), /* OC/CBC config */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0xC1), 0x00), /* CH1 DC fault */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0xC2), 0x04), /* CH2 DC fault */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0xC3), 0x00), /* CH3 DC fault */ + REG_SEQ0(TAS675X_PAGE_REG(1, 0xC4), 0x00), /* CH4 DC fault */ +}; + +static inline const char *tas675x_state_name(unsigned int state) +{ + switch (state & 0x0F) { + case TAS675X_STATE_DEEPSLEEP: return "DEEPSLEEP"; + case TAS675X_STATE_LOAD_DIAG: return "LOAD_DIAG"; + case TAS675X_STATE_SLEEP: return "SLEEP"; + case TAS675X_STATE_HIZ: return "HIZ"; + case TAS675X_STATE_PLAY: return "PLAY"; + case TAS675X_STATE_FAULT: return "FAULT"; + case TAS675X_STATE_AUTOREC: return "AUTOREC"; + default: return "UNKNOWN"; + } +} + +static inline int tas675x_set_state_all(struct tas675x_priv *tas, u8 state) +{ + const struct reg_sequence seq[] = { + REG_SEQ0(TAS675X_STATE_CTRL_CH1_CH2_REG, state), + REG_SEQ0(TAS675X_STATE_CTRL_CH3_CH4_REG, state), + }; + + return regmap_multi_reg_write(tas->regmap, seq, ARRAY_SIZE(seq)); +} + +static inline int tas675x_select_book(struct regmap *regmap, u8 book) +{ + int ret; + + /* Reset page to 0 before switching books */ + ret = regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00); + if (!ret) + ret = regmap_write(regmap, TAS675X_BOOK_CTRL_REG, book); + + return ret; +} + +/* Raw I2C version of tas675x_select_book, must be called with io_lock held */ +static inline int __tas675x_select_book(struct tas675x_priv *tas, u8 book) +{ + struct i2c_client *client = to_i2c_client(tas->dev); + int ret; + + /* Reset page to 0 before switching books */ + ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, 0x00); + if (ret) + return ret; + + return i2c_smbus_write_byte_data(client, TAS675X_BOOK_CTRL_REG, book); +} + +static int tas675x_dsp_mem_write(struct tas675x_priv *tas, u8 page, u8 reg, u32 val) +{ + struct i2c_client *client = to_i2c_client(tas->dev); + u8 buf[4]; + int ret; + + /* DSP registers are 32 bit big-endian */ + buf[0] = (val >> 24) & 0xFF; + buf[1] = (val >> 16) & 0xFF; + buf[2] = (val >> 8) & 0xFF; + buf[3] = val & 0xFF; + + /* + * DSP regs in a different book, therefore block + * regmap access before completion. + */ + mutex_lock(&tas->io_lock); + + ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP); + if (ret) + goto out; + + ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page); + if (ret) + goto out; + + ret = i2c_smbus_write_i2c_block_data(client, reg, sizeof(buf), buf); + +out: + __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT); + mutex_unlock(&tas->io_lock); + + return ret; +} + +static int tas675x_dsp_mem_read(struct tas675x_priv *tas, u8 page, u8 reg, u32 *val) +{ + struct i2c_client *client = to_i2c_client(tas->dev); + u8 buf[4]; + int ret; + + /* + * DSP regs in a different book, therefore block + * regmap access before completion. + */ + mutex_lock(&tas->io_lock); + + ret = __tas675x_select_book(tas, TAS675X_BOOK_DSP); + if (ret) + goto out; + + ret = i2c_smbus_write_byte_data(client, TAS675X_PAGE_CTRL_REG, page); + if (ret) + goto out; + + ret = i2c_smbus_read_i2c_block_data(client, reg, sizeof(buf), buf); + if (ret == sizeof(buf)) { + *val = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; + ret = 0; + } else if (ret >= 0) { + ret = -EIO; + } + +out: + __tas675x_select_book(tas, TAS675X_BOOK_DEFAULT); + mutex_unlock(&tas->io_lock); + + return ret; +} + +static const struct { + const char *name; + int val; +} tas675x_gpio_func_map[] = { + /* Output functions */ + { "low", TAS675X_GPIO_SEL_LOW }, + { "auto-mute", TAS675X_GPIO_SEL_AUTO_MUTE_ALL }, + { "auto-mute-ch4", TAS675X_GPIO_SEL_AUTO_MUTE_CH4 }, + { "auto-mute-ch3", TAS675X_GPIO_SEL_AUTO_MUTE_CH3 }, + { "auto-mute-ch2", TAS675X_GPIO_SEL_AUTO_MUTE_CH2 }, + { "auto-mute-ch1", TAS675X_GPIO_SEL_AUTO_MUTE_CH1 }, + { "sdout2", TAS675X_GPIO_SEL_SDOUT2 }, + { "sdout1", TAS675X_GPIO_SEL_SDOUT1 }, + { "warn", TAS675X_GPIO_SEL_WARN }, + { "fault", TAS675X_GPIO_SEL_FAULT }, + { "clock-sync", TAS675X_GPIO_SEL_CLOCK_SYNC }, + { "invalid-clock", TAS675X_GPIO_SEL_INVALID_CLK }, + { "high", TAS675X_GPIO_SEL_HIGH }, + /* Input functions */ + { "mute", TAS675X_GPIO_IN_MUTE }, + { "phase-sync", TAS675X_GPIO_IN_PHASE_SYNC }, + { "sdin2", TAS675X_GPIO_IN_SDIN2 }, + { "deep-sleep", TAS675X_GPIO_IN_DEEP_SLEEP }, + { "hiz", TAS675X_GPIO_IN_HIZ }, + { "play", TAS675X_GPIO_IN_PLAY }, + { "sleep", TAS675X_GPIO_IN_SLEEP }, +}; + +static int tas675x_gpio_func_parse(struct device *dev, const char *propname) +{ + const char *str; + int i, ret; + + ret = device_property_read_string(dev, propname, &str); + if (ret) + return -1; + + for (i = 0; i < ARRAY_SIZE(tas675x_gpio_func_map); i++) { + if (!strcmp(str, tas675x_gpio_func_map[i].name)) + return tas675x_gpio_func_map[i].val; + } + + dev_warn(dev, "Invalid %s value '%s'\n", propname, str); + return -1; +} + +static const struct { + unsigned int reg; + unsigned int mask; +} tas675x_gpio_input_table[TAS675X_GPIO_IN_NUM] = { + [TAS675X_GPIO_IN_ID_MUTE] = { + TAS675X_GPIO_INPUT_MUTE_REG, TAS675X_GPIO_IN_MUTE_MASK }, + [TAS675X_GPIO_IN_ID_PHASE_SYNC] = { + TAS675X_GPIO_INPUT_SYNC_REG, TAS675X_GPIO_IN_SYNC_MASK }, + [TAS675X_GPIO_IN_ID_SDIN2] = { + TAS675X_GPIO_INPUT_SDIN2_REG, TAS675X_GPIO_IN_SDIN2_MASK }, + [TAS675X_GPIO_IN_ID_DEEP_SLEEP] = { + TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_DEEP_SLEEP_MASK }, + [TAS675X_GPIO_IN_ID_HIZ] = { + TAS675X_GPIO_INPUT_SLEEP_HIZ_REG, TAS675X_GPIO_IN_HIZ_MASK }, + [TAS675X_GPIO_IN_ID_PLAY] = { + TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_PLAY_MASK }, + [TAS675X_GPIO_IN_ID_SLEEP] = { + TAS675X_GPIO_INPUT_PLAY_SLEEP_REG, TAS675X_GPIO_IN_SLEEP_MASK }, +}; + +static void tas675x_config_gpio_pin(struct regmap *regmap, int func_id, + unsigned int out_sel_reg, + unsigned int pin_idx, + unsigned int *gpio_ctrl) +{ + int id; + + if (func_id < 0) + return; + + if (func_id & TAS675X_GPIO_FUNC_INPUT) { + /* 3-bit mux: 0 = disabled, 0b1 = GPIO1, 0b10 = GPIO2 */ + id = func_id & ~TAS675X_GPIO_FUNC_INPUT; + regmap_update_bits(regmap, + tas675x_gpio_input_table[id].reg, + tas675x_gpio_input_table[id].mask, + (pin_idx + 1) << __ffs(tas675x_gpio_input_table[id].mask)); + } else { + /* Output GPIO, update selection register and enable bit */ + regmap_write(regmap, out_sel_reg, func_id); + *gpio_ctrl |= pin_idx ? TAS675X_GPIO2_OUTPUT_EN : TAS675X_GPIO1_OUTPUT_EN; + } +} + +static int tas675x_rtldg_thresh_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + /* threshold reg ranges up to 24bit */ + uinfo->value.integer.max = 0x00FFFFFF; + return 0; +} + +static int tas675x_set_rtldg_thresh(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp); + const struct tas675x_reg_param *t = + (const struct tas675x_reg_param *)kcontrol->private_value; + u32 val = ucontrol->value.integer.value[0]; + int ret; + + ret = tas675x_dsp_mem_write(tas, t->page, t->reg, val); + + /* Cache the value */ + if (!ret) { + int i; + + for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) { + if (tas->dsp_params[i].page == t->page && + tas->dsp_params[i].reg == t->reg) { + tas->dsp_params[i].val = val; + break; + } + } + } + + /* Return 1 to notify change, or propagate error */ + return ret ? ret : 1; +} + +static int tas675x_get_rtldg_thresh(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp); + const struct tas675x_reg_param *t = + (const struct tas675x_reg_param *)kcontrol->private_value; + u32 val = 0; + int ret; + + ret = tas675x_dsp_mem_read(tas, t->page, t->reg, &val); + if (!ret) + ucontrol->value.integer.value[0] = val; + + return ret; +} + +static const struct tas675x_reg_param tas675x_dsp_defaults[] = { + [TAS675X_DSP_PARAM_ID_OL_THRESH] = { + TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_OL_THRESH_REG }, + [TAS675X_DSP_PARAM_ID_SL_THRESH] = { + TAS675X_DSP_PAGE_RTLDG, TAS675X_DSP_RTLDG_SL_THRESH_REG }, +}; + +static_assert(ARRAY_SIZE(tas675x_dsp_defaults) == TAS675X_DSP_PARAM_NUM); + +static int tas675x_set_dcldg_trigger(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp); + unsigned int state, state34; + int ret; + + if (!ucontrol->value.integer.value[0]) + return 0; + + if (snd_soc_component_active(comp)) + return -EBUSY; + + ret = pm_runtime_resume_and_get(tas->dev); + if (ret < 0) + return ret; + + /* + * Abort automatic DC LDG retry loops (startup or init-after-fault) + * and clear faults before manual diagnostics. + */ + regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG, + TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT, + TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT); + regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR); + + /* Wait for LOAD_DIAG to exit */ + ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG, + state, (state & 0x0F) != TAS675X_STATE_LOAD_DIAG && + (state >> 4) != TAS675X_STATE_LOAD_DIAG, + TAS675X_POLL_INTERVAL_US, + TAS675X_STATE_TRANSITION_TIMEOUT_US); + ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG, + state34, (state34 & 0x0F) != TAS675X_STATE_LOAD_DIAG && + (state34 >> 4) != TAS675X_STATE_LOAD_DIAG, + TAS675X_POLL_INTERVAL_US, + TAS675X_STATE_TRANSITION_TIMEOUT_US); + if (ret) { + dev_err(tas->dev, + "DC LDG: abort timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n", + state, tas675x_state_name(state), tas675x_state_name(state >> 4), + state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4)); + goto out_restore_ldg_ctrl; + } + + /* Transition to HIZ state */ + ret = tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH); + if (ret) + goto out_restore_ldg_ctrl; + + /* Set LOAD_DIAG state for manual DC LDG */ + ret = tas675x_set_state_all(tas, TAS675X_STATE_LOAD_DIAG_BOTH); + if (ret) + goto out_restore_ldg_ctrl; + + /* Wait for device to transition to LOAD_DIAG state */ + ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG, + state, state == TAS675X_STATE_LOAD_DIAG_BOTH, + TAS675X_POLL_INTERVAL_US, + TAS675X_STATE_TRANSITION_TIMEOUT_US); + ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG, + state34, state34 == TAS675X_STATE_LOAD_DIAG_BOTH, + TAS675X_POLL_INTERVAL_US, + TAS675X_STATE_TRANSITION_TIMEOUT_US); + if (ret) { + dev_err(tas->dev, + "DC LDG: LOAD_DIAG timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n", + state, tas675x_state_name(state), tas675x_state_name(state >> 4), + state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4)); + goto out_restore_hiz; + } + + /* Clear ABORT and BYPASS bits to enable manual DC LDG */ + ret = regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG, + TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT, + 0); + if (ret) + goto out_restore_hiz; + + dev_dbg(tas->dev, "DC LDG: Started\n"); + + /* Poll all channels for SLEEP state */ + ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG, + state, state == TAS675X_STATE_SLEEP_BOTH, + TAS675X_POLL_INTERVAL_US, + TAS675X_DC_LDG_TIMEOUT_US); + ret |= regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG, + state34, state34 == TAS675X_STATE_SLEEP_BOTH, + TAS675X_POLL_INTERVAL_US, + TAS675X_DC_LDG_TIMEOUT_US); + if (ret) { + dev_err(tas->dev, + "DC LDG: SLEEP timeout (CH1/2=0x%02x [%s/%s], CH3/4=0x%02x [%s/%s])\n", + state, tas675x_state_name(state), tas675x_state_name(state >> 4), + state34, tas675x_state_name(state34), tas675x_state_name(state34 >> 4)); + goto out_restore_hiz; + } + + dev_dbg(tas->dev, "DC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n", + state, state34); + +out_restore_hiz: + tas675x_set_state_all(tas, TAS675X_STATE_HIZ_BOTH); + +out_restore_ldg_ctrl: + regmap_update_bits(tas->regmap, TAS675X_DC_LDG_CTRL_REG, + TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT, + 0); + + pm_runtime_mark_last_busy(tas->dev); + pm_runtime_put_autosuspend(tas->dev); + + return ret; +} + +static int tas675x_set_acldg_trigger(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp); + unsigned int state, state34; + int ret; + + if (!ucontrol->value.integer.value[0]) + return 0; + + if (snd_soc_component_active(comp)) + return -EBUSY; + + ret = pm_runtime_resume_and_get(tas->dev); + if (ret < 0) + return ret; + + /* AC Load Diagnostics requires SLEEP state */ + ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH); + if (ret) { + dev_err(tas->dev, "AC LDG: Failed to set SLEEP state: %d\n", ret); + goto out; + } + + /* Start AC LDG on all 4 channels (0x0F) */ + ret = regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x0F); + if (ret) { + dev_err(tas->dev, "AC LDG: Failed to start: %d\n", ret); + goto out; + } + + dev_dbg(tas->dev, "AC LDG: Started\n"); + + /* Poll all channels for SLEEP state */ + ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH1_CH2_REG, + state, (state == TAS675X_STATE_SLEEP_BOTH), + TAS675X_POLL_INTERVAL_US, + TAS675X_AC_LDG_TIMEOUT_US); + if (ret) { + dev_err(tas->dev, + "AC LDG: CH1/CH2 timeout: %d (state=0x%02x [%s/%s])\n", + ret, state, tas675x_state_name(state), + tas675x_state_name(state >> 4)); + regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00); + goto out; + } + + ret = regmap_read_poll_timeout(tas->regmap, TAS675X_STATE_REPORT_CH3_CH4_REG, + state34, (state34 == TAS675X_STATE_SLEEP_BOTH), + TAS675X_POLL_INTERVAL_US, + TAS675X_AC_LDG_TIMEOUT_US); + if (ret) { + dev_err(tas->dev, + "AC LDG: CH3/CH4 timeout: %d (state=0x%02x [%s/%s])\n", + ret, state34, tas675x_state_name(state34), + tas675x_state_name(state34 >> 4)); + regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00); + goto out; + } + + dev_dbg(tas->dev, "AC LDG: Completed successfully (CH1/2=0x%02x, CH3/4=0x%02x)\n", + state, state34); + regmap_write(tas->regmap, TAS675X_AC_LDG_CTRL_REG, 0x00); + +out: + pm_runtime_mark_last_busy(tas->dev); + pm_runtime_put_autosuspend(tas->dev); + + return ret; +} + +static int tas675x_rtldg_impedance_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 0xFFFF; + return 0; +} + +static int tas675x_get_rtldg_impedance(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp); + unsigned int msb_reg = (unsigned int)kcontrol->private_value; + u8 buf[2]; + int ret; + + ret = regmap_bulk_read(tas->regmap, msb_reg, buf, 2); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = (buf[0] << 8) | buf[1]; + return 0; +} + +static int tas675x_dc_resistance_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + /* 10-bit: 2-bit MSB + 8-bit LSB, 0.1 ohm/code, 0-102.3 ohm */ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1023; + return 0; +} + +static int tas675x_get_dc_resistance(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_kcontrol_chip(kcontrol); + struct tas675x_priv *tas = snd_soc_component_get_drvdata(comp); + unsigned int lsb_reg = (unsigned int)kcontrol->private_value; + unsigned int msb, lsb, shift; + int ret; + + ret = regmap_read(tas->regmap, TAS675X_DC_LDG_DCR_MSB_REG, &msb); + if (ret) + return ret; + + ret = regmap_read(tas->regmap, lsb_reg, &lsb); + if (ret) + return ret; + + /* 2-bit MSB: CH1=[7:6], CH2=[5:4], CH3=[3:2], CH4=[1:0] */ + shift = 6 - (lsb_reg - TAS675X_CH1_DC_LDG_DCR_LSB_REG) * 2; + msb = (msb >> shift) & 0x3; + + ucontrol->value.integer.value[0] = (msb << 8) | lsb; + return 0; +} + +/* Counterparts with read-only access */ +#define SOC_SINGLE_RO(xname, xreg, xshift, xmax) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .name = xname, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = snd_soc_info_volsw, \ + .get = snd_soc_get_volsw, \ + .private_value = SOC_SINGLE_VALUE(xreg, xshift, 0, xmax, 0, 0) } +#define SOC_DC_RESIST_RO(xname, xlsb_reg) \ +{ .name = xname, \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = tas675x_dc_resistance_info, \ + .get = tas675x_get_dc_resistance, \ + .private_value = (xlsb_reg) } +#define SOC_RTLDG_IMP_RO(xname, xreg) \ +{ .name = xname, \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE, \ + .info = tas675x_rtldg_impedance_info, \ + .get = tas675x_get_rtldg_impedance, \ + .private_value = (xreg) } + +#define SOC_DSP_THRESH_EXT(xname, xthresh) \ +{ .name = xname, \ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \ + .info = tas675x_rtldg_thresh_info, \ + .get = tas675x_get_rtldg_thresh, \ + .put = tas675x_set_rtldg_thresh, \ + .private_value = (unsigned long)&(xthresh) } + +/* + * DAC digital volumes. From -103 to 0 dB in 0.5 dB steps, -103.5 dB means mute. + * DAC analog gain. From -15.5 to 0 dB in 0.5 dB steps, no mute. + */ +static const DECLARE_TLV_DB_SCALE(tas675x_dig_vol_tlv, -10350, 50, 1); +static const DECLARE_TLV_DB_SCALE(tas675x_ana_gain_tlv, -1550, 50, 0); + +static const char * const tas675x_ss_texts[] = { + "Disabled", "Triangle", "Random", "Triangle and Random" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ss_enum, TAS675X_SS_CTRL_REG, 0, tas675x_ss_texts); + +static const char * const tas675x_ss_tri_range_texts[] = { + "6.5%", "13.5%", "5%", "10%" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ss_tri_range_enum, + TAS675X_SS_RANGE_CTRL_REG, 0, + tas675x_ss_tri_range_texts); + +static const char * const tas675x_ss_rdm_range_texts[] = { + "0.83%", "2.50%", "5.83%", "12.50%", "25.83%" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_range_enum, + TAS675X_SS_RANGE_CTRL_REG, 4, + tas675x_ss_rdm_range_texts); + +static const char * const tas675x_ss_rdm_dwell_texts[] = { + "1/FSS to 2/FSS", "1/FSS to 4/FSS", "1/FSS to 8/FSS", "1/FSS to 15/FSS" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ss_rdm_dwell_enum, + TAS675X_SS_RANGE_CTRL_REG, 2, + tas675x_ss_rdm_dwell_texts); + +static const char * const tas675x_oc_limit_texts[] = { + "Level 4", "Level 3", "Level 2", "Level 1" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_oc_limit_enum, TAS675X_CURRENT_LIMIT_CTRL_REG, + 0, tas675x_oc_limit_texts); + +static const char * const tas675x_otw_texts[] = { + "Disabled", ">95C", ">110C", ">125C", ">135C", ">145C", ">155C", ">165C" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ch1_otw_enum, + TAS675X_OTW_CTRL_CH1_CH2_REG, 4, + tas675x_otw_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch2_otw_enum, + TAS675X_OTW_CTRL_CH1_CH2_REG, 0, + tas675x_otw_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch3_otw_enum, + TAS675X_OTW_CTRL_CH3_CH4_REG, 4, + tas675x_otw_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch4_otw_enum, + TAS675X_OTW_CTRL_CH3_CH4_REG, 0, + tas675x_otw_texts); + +static const char * const tas675x_dc_ldg_sl_texts[] = { + "0.5 Ohm", "1 Ohm", "1.5 Ohm", "2 Ohm", "2.5 Ohm", + "3 Ohm", "3.5 Ohm", "4 Ohm", "4.5 Ohm", "5 Ohm" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ch1_dc_ldg_sl_enum, + TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 4, + tas675x_dc_ldg_sl_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch2_dc_ldg_sl_enum, + TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0, + tas675x_dc_ldg_sl_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch3_dc_ldg_sl_enum, + TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 4, + tas675x_dc_ldg_sl_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch4_dc_ldg_sl_enum, + TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0, + tas675x_dc_ldg_sl_texts); + +static const char * const tas675x_dc_slol_ramp_texts[] = { + "15 ms", "30 ms", "10 ms", "20 ms" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_ramp_enum, + TAS675X_DC_LDG_TIME_CTRL_REG, 6, + tas675x_dc_slol_ramp_texts); + +static const char * const tas675x_dc_slol_settling_texts[] = { + "10 ms", "5 ms", "20 ms", "15 ms" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_dc_slol_settling_enum, + TAS675X_DC_LDG_TIME_CTRL_REG, 4, + tas675x_dc_slol_settling_texts); + +static const char * const tas675x_dc_s2pg_ramp_texts[] = { + "5 ms", "2.5 ms", "10 ms", "15 ms" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_ramp_enum, + TAS675X_DC_LDG_TIME_CTRL_REG, 2, + tas675x_dc_s2pg_ramp_texts); + +static const char * const tas675x_dc_s2pg_settling_texts[] = { + "10 ms", "5 ms", "20 ms", "30 ms" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_dc_s2pg_settling_enum, + TAS675X_DC_LDG_TIME_CTRL_REG, 0, + tas675x_dc_s2pg_settling_texts); + +static const char * const tas675x_dsp_mode_texts[] = { + "Normal", "LLP", "FFLP" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_dsp_mode_enum, + TAS675X_LL_EN_REG, 0, + tas675x_dsp_mode_texts); + +static const char * const tas675x_ana_ramp_texts[] = { + "15us", "60us", "200us", "400us" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ana_ramp_enum, + TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 2, + tas675x_ana_ramp_texts); + +static const char * const tas675x_ramp_rate_texts[] = { + "4 FS", "16 FS", "32 FS", "Instant" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_rate_enum, + TAS675X_DIG_VOL_RAMP_CTRL_REG, 6, + tas675x_ramp_rate_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_rate_enum, + TAS675X_DIG_VOL_RAMP_CTRL_REG, 2, + tas675x_ramp_rate_texts); + +static const char * const tas675x_ramp_step_texts[] = { + "4dB", "2dB", "1dB", "0.5dB" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ramp_down_step_enum, + TAS675X_DIG_VOL_RAMP_CTRL_REG, 4, + tas675x_ramp_step_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ramp_up_step_enum, + TAS675X_DIG_VOL_RAMP_CTRL_REG, 0, + tas675x_ramp_step_texts); + +static const char * const tas675x_vol_combine_ch12_texts[] = { + "Independent", "CH2 follows CH1", "CH1 follows CH2" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch12_enum, + TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0, + tas675x_vol_combine_ch12_texts); + +static const char * const tas675x_vol_combine_ch34_texts[] = { + "Independent", "CH4 follows CH3", "CH3 follows CH4" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_vol_combine_ch34_enum, + TAS675X_DIG_VOL_COMBINE_CTRL_REG, 2, + tas675x_vol_combine_ch34_texts); + +static const char * const tas675x_auto_mute_time_texts[] = { + "11.5ms", "53ms", "106.5ms", "266.5ms", + "535ms", "1065ms", "2665ms", "5330ms" +}; + +static SOC_ENUM_SINGLE_DECL(tas675x_ch1_mute_time_enum, + TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 4, + tas675x_auto_mute_time_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch2_mute_time_enum, + TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0, + tas675x_auto_mute_time_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch3_mute_time_enum, + TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 4, + tas675x_auto_mute_time_texts); +static SOC_ENUM_SINGLE_DECL(tas675x_ch4_mute_time_enum, + TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0, + tas675x_auto_mute_time_texts); + +/* + * ALSA Mixer Controls + * + * For detailed documentation of each control see: + * Documentation/sound/codecs/tas675x.rst + */ +static const struct snd_kcontrol_new tas675x_snd_controls[] = { + /* Volume & Gain Control */ + SOC_DOUBLE_R_TLV("Analog Playback Volume", TAS675X_ANALOG_GAIN_CH1_CH2_REG, + TAS675X_ANALOG_GAIN_CH3_CH4_REG, 1, 0x1F, 1, tas675x_ana_gain_tlv), + SOC_ENUM("Analog Gain Ramp Step", tas675x_ana_ramp_enum), + SOC_SINGLE_RANGE_TLV("CH1 Digital Playback Volume", + TAS675X_DIG_VOL_CH1_REG, 0, 0x30, 0xFF, 1, + tas675x_dig_vol_tlv), + SOC_SINGLE_RANGE_TLV("CH2 Digital Playback Volume", + TAS675X_DIG_VOL_CH2_REG, 0, 0x30, 0xFF, 1, + tas675x_dig_vol_tlv), + SOC_SINGLE_RANGE_TLV("CH3 Digital Playback Volume", + TAS675X_DIG_VOL_CH3_REG, 0, 0x30, 0xFF, 1, + tas675x_dig_vol_tlv), + SOC_SINGLE_RANGE_TLV("CH4 Digital Playback Volume", + TAS675X_DIG_VOL_CH4_REG, 0, 0x30, 0xFF, 1, + tas675x_dig_vol_tlv), + SOC_ENUM("Volume Ramp Down Rate", tas675x_ramp_down_rate_enum), + SOC_ENUM("Volume Ramp Down Step", tas675x_ramp_down_step_enum), + SOC_ENUM("Volume Ramp Up Rate", tas675x_ramp_up_rate_enum), + SOC_ENUM("Volume Ramp Up Step", tas675x_ramp_up_step_enum), + SOC_ENUM("CH1/2 Volume Combine", tas675x_vol_combine_ch12_enum), + SOC_ENUM("CH3/4 Volume Combine", tas675x_vol_combine_ch34_enum), + + /* Auto Mute & Silence Detection */ + SOC_SINGLE("CH1 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 0, 1, 0), + SOC_SINGLE("CH2 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 1, 1, 0), + SOC_SINGLE("CH3 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 2, 1, 0), + SOC_SINGLE("CH4 Auto Mute Switch", TAS675X_AUTO_MUTE_EN_REG, 3, 1, 0), + SOC_SINGLE("Auto Mute Combine Switch", TAS675X_AUTO_MUTE_EN_REG, 4, 1, 0), + SOC_ENUM("CH1 Auto Mute Time", tas675x_ch1_mute_time_enum), + SOC_ENUM("CH2 Auto Mute Time", tas675x_ch2_mute_time_enum), + SOC_ENUM("CH3 Auto Mute Time", tas675x_ch3_mute_time_enum), + SOC_ENUM("CH4 Auto Mute Time", tas675x_ch4_mute_time_enum), + + /* Clock & EMI Management */ + SOC_ENUM("Spread Spectrum Mode", tas675x_ss_enum), + SOC_ENUM("SS Triangle Range", tas675x_ss_tri_range_enum), + SOC_ENUM("SS Random Range", tas675x_ss_rdm_range_enum), + SOC_ENUM("SS Random Dwell Range", tas675x_ss_rdm_dwell_enum), + SOC_SINGLE("SS Triangle Dwell Min", TAS675X_SS_DWELL_CTRL_REG, 4, 15, 0), + SOC_SINGLE("SS Triangle Dwell Max", TAS675X_SS_DWELL_CTRL_REG, 0, 15, 0), + + /* Hardware Protection */ + SOC_SINGLE("OTSD Auto Recovery Switch", TAS675X_OTSD_RECOVERY_EN_REG, 1, 1, 0), + SOC_ENUM("Overcurrent Limit Level", tas675x_oc_limit_enum), + SOC_ENUM("CH1 OTW Threshold", tas675x_ch1_otw_enum), + SOC_ENUM("CH2 OTW Threshold", tas675x_ch2_otw_enum), + SOC_ENUM("CH3 OTW Threshold", tas675x_ch3_otw_enum), + SOC_ENUM("CH4 OTW Threshold", tas675x_ch4_otw_enum), + + /* DSP Signal Path & Mode */ + SOC_ENUM("DSP Signal Path Mode", tas675x_dsp_mode_enum), + + /* DC Load Diagnostics */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "DC LDG Trigger", + .access = SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = snd_ctl_boolean_mono_info, + .put = tas675x_set_dcldg_trigger, + }, + SOC_SINGLE("DC LDG Auto Diagnostics Switch", TAS675X_DC_LDG_CTRL_REG, 0, 1, 1), + SOC_SINGLE("CH1 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 3, 1, 0), + SOC_SINGLE("CH2 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 2, 1, 0), + SOC_SINGLE("CH3 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 1, 1, 0), + SOC_SINGLE("CH4 LO LDG Switch", TAS675X_DC_LDG_LO_CTRL_REG, 0, 1, 0), + SOC_ENUM("DC LDG SLOL Ramp Time", tas675x_dc_slol_ramp_enum), + SOC_ENUM("DC LDG SLOL Settling Time", tas675x_dc_slol_settling_enum), + SOC_ENUM("DC LDG S2PG Ramp Time", tas675x_dc_s2pg_ramp_enum), + SOC_ENUM("DC LDG S2PG Settling Time", tas675x_dc_s2pg_settling_enum), + SOC_ENUM("CH1 DC LDG SL Threshold", tas675x_ch1_dc_ldg_sl_enum), + SOC_ENUM("CH2 DC LDG SL Threshold", tas675x_ch2_dc_ldg_sl_enum), + SOC_ENUM("CH3 DC LDG SL Threshold", tas675x_ch3_dc_ldg_sl_enum), + SOC_ENUM("CH4 DC LDG SL Threshold", tas675x_ch4_dc_ldg_sl_enum), + SOC_SINGLE_RO("DC LDG Result", TAS675X_DC_LDG_RESULT_REG, 0, 0xFF), + SOC_SINGLE_RO("CH1 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 4, 0x0F), + SOC_SINGLE_RO("CH2 DC LDG Report", TAS675X_DC_LDG_REPORT_CH1_CH2_REG, 0, 0x0F), + SOC_SINGLE_RO("CH3 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 4, 0x0F), + SOC_SINGLE_RO("CH4 DC LDG Report", TAS675X_DC_LDG_REPORT_CH3_CH4_REG, 0, 0x0F), + SOC_SINGLE_RO("CH1 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 7, 1), + SOC_SINGLE_RO("CH2 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 6, 1), + SOC_SINGLE_RO("CH3 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 5, 1), + SOC_SINGLE_RO("CH4 LO LDG Report", TAS675X_DC_LDG_RESULT_REG, 4, 1), + SOC_DC_RESIST_RO("CH1 DC Resistance", TAS675X_CH1_DC_LDG_DCR_LSB_REG), + SOC_DC_RESIST_RO("CH2 DC Resistance", TAS675X_CH2_DC_LDG_DCR_LSB_REG), + SOC_DC_RESIST_RO("CH3 DC Resistance", TAS675X_CH3_DC_LDG_DCR_LSB_REG), + SOC_DC_RESIST_RO("CH4 DC Resistance", TAS675X_CH4_DC_LDG_DCR_LSB_REG), + + /* AC Load Diagnostics */ + { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "AC LDG Trigger", + .access = SNDRV_CTL_ELEM_ACCESS_WRITE, + .info = snd_ctl_boolean_mono_info, + .put = tas675x_set_acldg_trigger, + }, + SOC_SINGLE("AC LDG Gain", TAS675X_AC_LDG_CTRL_REG, 4, 1, 0), + SOC_SINGLE("AC LDG Test Frequency", TAS675X_AC_LDG_FREQ_CTRL_REG, 0, 0xFF, 0), + SOC_SINGLE_RO("CH1 AC LDG Real", TAS675X_AC_LDG_REPORT_CH1_R_REG, 0, 0xFF), + SOC_SINGLE_RO("CH1 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH1_I_REG, 0, 0xFF), + SOC_SINGLE_RO("CH2 AC LDG Real", TAS675X_AC_LDG_REPORT_CH2_R_REG, 0, 0xFF), + SOC_SINGLE_RO("CH2 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH2_I_REG, 0, 0xFF), + SOC_SINGLE_RO("CH3 AC LDG Real", TAS675X_AC_LDG_REPORT_CH3_R_REG, 0, 0xFF), + SOC_SINGLE_RO("CH3 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH3_I_REG, 0, 0xFF), + SOC_SINGLE_RO("CH4 AC LDG Real", TAS675X_AC_LDG_REPORT_CH4_R_REG, 0, 0xFF), + SOC_SINGLE_RO("CH4 AC LDG Imag", TAS675X_AC_LDG_REPORT_CH4_I_REG, 0, 0xFF), + + /* Temperature and Voltage Monitoring */ + SOC_SINGLE_RO("PVDD Sense", TAS675X_PVDD_SENSE_REG, 0, 0xFF), + SOC_SINGLE_RO("Global Temperature", TAS675X_TEMP_GLOBAL_REG, 0, 0xFF), + SOC_SINGLE_RO("CH1 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 0, 7), + SOC_SINGLE_RO("CH2 Temperature Range", TAS675X_TEMP_CH1_CH2_REG, 3, 7), + SOC_SINGLE_RO("CH3 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 0, 7), + SOC_SINGLE_RO("CH4 Temperature Range", TAS675X_TEMP_CH3_CH4_REG, 3, 7), + + /* Speaker Protection & Detection */ + SOC_SINGLE("Tweeter Detection Switch", TAS675X_TWEETER_DETECT_CTRL_REG, 0, 1, 1), + SOC_SINGLE("Tweeter Detect Threshold", TAS675X_TWEETER_DETECT_THRESH_REG, 0, 0xFF, 0), + SOC_SINGLE_RO("CH1 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 3, 1), + SOC_SINGLE_RO("CH2 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 2, 1), + SOC_SINGLE_RO("CH3 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 1, 1), + SOC_SINGLE_RO("CH4 Tweeter Detect Report", TAS675X_TWEETER_REPORT_REG, 0, 1), + + /* + * Unavailable in LLP, available in Normal & FFLP + */ + SOC_SINGLE("Thermal Foldback Switch", TAS675X_DSP_CTRL_REG, 0, 1, 0), + SOC_SINGLE("PVDD Foldback Switch", TAS675X_DSP_CTRL_REG, 4, 1, 0), + SOC_SINGLE("DC Blocker Bypass Switch", TAS675X_DC_BLOCK_BYP_REG, 0, 1, 0), + SOC_SINGLE("Clip Detect Switch", TAS675X_CLIP_DETECT_CTRL_REG, 6, 1, 0), + SOC_SINGLE("Audio SDOUT Switch", TAS675X_DSP_CTRL_REG, 5, 1, 0), + + /* + * Unavailable in both FFLP and LLP, Normal mode only + */ + /* Real-Time Load Diagnostics */ + SOC_SINGLE("CH1 RTLDG Switch", TAS675X_RTLDG_EN_REG, 3, 1, 0), + SOC_SINGLE("CH2 RTLDG Switch", TAS675X_RTLDG_EN_REG, 2, 1, 0), + SOC_SINGLE("CH3 RTLDG Switch", TAS675X_RTLDG_EN_REG, 1, 1, 0), + SOC_SINGLE("CH4 RTLDG Switch", TAS675X_RTLDG_EN_REG, 0, 1, 0), + SOC_SINGLE("RTLDG Clip Mask Switch", TAS675X_RTLDG_EN_REG, 4, 1, 0), + SOC_SINGLE("ISENSE Calibration Switch", TAS675X_ISENSE_CAL_REG, 3, 1, 0), + SOC_DSP_THRESH_EXT("RTLDG Open Load Threshold", + tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_OL_THRESH]), + SOC_DSP_THRESH_EXT("RTLDG Short Load Threshold", + tas675x_dsp_defaults[TAS675X_DSP_PARAM_ID_SL_THRESH]), + SOC_RTLDG_IMP_RO("CH1 RTLDG Impedance", TAS675X_CH1_RTLDG_IMP_MSB_REG), + SOC_RTLDG_IMP_RO("CH2 RTLDG Impedance", TAS675X_CH2_RTLDG_IMP_MSB_REG), + SOC_RTLDG_IMP_RO("CH3 RTLDG Impedance", TAS675X_CH3_RTLDG_IMP_MSB_REG), + SOC_RTLDG_IMP_RO("CH4 RTLDG Impedance", TAS675X_CH4_RTLDG_IMP_MSB_REG), +}; + +static const struct snd_kcontrol_new tas675x_audio_path_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1); + +static const struct snd_kcontrol_new tas675x_anc_path_switch = + SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 1); + +static const struct snd_soc_dapm_widget tas675x_dapm_widgets[] = { + SND_SOC_DAPM_SUPPLY("Analog Core", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDOUT Vpredict", SND_SOC_NOPM, 0, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("SDOUT Isense", SND_SOC_NOPM, 0, 0, NULL, 0), + + SND_SOC_DAPM_DAC("Audio DAC", "Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_DAC("ANC DAC", "ANC Playback", SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_ADC("Feedback ADC", "Feedback Capture", SND_SOC_NOPM, 0, 0), + + SND_SOC_DAPM_SWITCH("Audio Path", SND_SOC_NOPM, 0, 0, + &tas675x_audio_path_switch), + SND_SOC_DAPM_SWITCH("ANC Path", SND_SOC_NOPM, 0, 0, + &tas675x_anc_path_switch), + + /* + * Even though all channels are coupled in terms of power control, + * use logical outputs for each channel to allow independent routing + * and DAPM controls if needed. + */ + SND_SOC_DAPM_OUTPUT("OUT_CH1"), + SND_SOC_DAPM_OUTPUT("OUT_CH2"), + SND_SOC_DAPM_OUTPUT("OUT_CH3"), + SND_SOC_DAPM_OUTPUT("OUT_CH4"), + SND_SOC_DAPM_INPUT("SPEAKER_LOAD"), +}; + +static const struct snd_soc_dapm_route tas675x_dapm_routes[] = { + { "Audio DAC", NULL, "Analog Core" }, + { "Audio Path", "Switch", "Audio DAC" }, + { "OUT_CH1", NULL, "Audio Path" }, + { "OUT_CH2", NULL, "Audio Path" }, + { "OUT_CH3", NULL, "Audio Path" }, + { "OUT_CH4", NULL, "Audio Path" }, + + { "ANC DAC", NULL, "Analog Core" }, + { "ANC Path", "Switch", "ANC DAC" }, + { "OUT_CH1", NULL, "ANC Path" }, + { "OUT_CH2", NULL, "ANC Path" }, + { "OUT_CH3", NULL, "ANC Path" }, + { "OUT_CH4", NULL, "ANC Path" }, + + { "Feedback ADC", NULL, "Analog Core" }, + { "Feedback ADC", NULL, "SDOUT Vpredict" }, + { "Feedback ADC", NULL, "SDOUT Isense" }, + { "Feedback ADC", NULL, "SPEAKER_LOAD" }, +}; + +static void tas675x_program_slot_offsets(struct tas675x_priv *tas, + int dai_id, int slot_width) +{ + int offset = 0; + + switch (dai_id) { + case 0: + /* Standard Audio on SDIN */ + if (tas->audio_slot >= 0) + offset = tas->audio_slot * slot_width; + else if (tas->tx_mask) + offset = __ffs(tas->tx_mask) * slot_width; + else + return; + offset += tas->bclk_offset; + regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG, + TAS675X_SDIN_AUDIO_OFF_MSB_MASK, + FIELD_PREP(TAS675X_SDIN_AUDIO_OFF_MSB_MASK, offset >> 8)); + regmap_write(tas->regmap, TAS675X_SDIN_AUDIO_OFFSET_REG, + offset & 0xFF); + break; + case 1: + /* + * Low-Latency Playback on SDIN, **only** enabled in LLP mode + * and to be mixed with main audio before output amplification + * to achieve ANC/RNC. + */ + if (tas->llp_slot >= 0) + offset = tas->llp_slot * slot_width; + else if (tas->tx_mask) + offset = __ffs(tas->tx_mask) * slot_width; + else + return; + offset += tas->bclk_offset; + regmap_update_bits(tas->regmap, TAS675X_SDIN_OFFSET_MSB_REG, + TAS675X_SDIN_LL_OFF_MSB_MASK, + FIELD_PREP(TAS675X_SDIN_LL_OFF_MSB_MASK, offset >> 8)); + regmap_write(tas->regmap, TAS675X_SDIN_LL_OFFSET_REG, + offset & 0xFF); + break; + case 2: + /* SDOUT Data Output (Vpredict + Isense feedback) */ + if (!tas->slot_width) + break; + if (tas->vpredict_slot >= 0) { + offset = tas->vpredict_slot * slot_width; + offset += tas->bclk_offset; + regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG, + TAS675X_SDOUT_VP_OFF_MSB_MASK, + FIELD_PREP(TAS675X_SDOUT_VP_OFF_MSB_MASK, offset >> 8)); + regmap_write(tas->regmap, TAS675X_VPREDICT_OFFSET_REG, + offset & 0xFF); + } + if (tas->isense_slot >= 0) { + offset = tas->isense_slot * slot_width; + offset += tas->bclk_offset; + regmap_update_bits(tas->regmap, TAS675X_SDOUT_OFFSET_MSB_REG, + TAS675X_SDOUT_IS_OFF_MSB_MASK, + FIELD_PREP(TAS675X_SDOUT_IS_OFF_MSB_MASK, offset >> 8)); + regmap_write(tas->regmap, TAS675X_ISENSE_OFFSET_REG, + offset & 0xFF); + } + break; + } + + if (offset > 511) + dev_warn(tas->dev, + "DAI %d slot offset %d exceeds 511 SCLK limit\n", + dai_id, offset); +} + +static int tas675x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct tas675x_priv *tas = snd_soc_component_get_drvdata(component); + unsigned int rate = params_rate(params); + u8 word_length; + + /* + * Single clock domain: SDIN and SDOUT share one SCLK/FSYNC pair, + * so all active DAIs must use the same sample rate. + */ + if ((READ_ONCE(tas->active_playback_dais) || READ_ONCE(tas->active_capture_dais)) && + tas->rate && tas->rate != rate) { + dev_err(component->dev, + "Rate %u conflicts with active rate %u\n", + rate, tas->rate); + return -EINVAL; + } + + switch (params_width(params)) { + case 16: + word_length = TAS675X_WL_16BIT; + break; + case 20: + word_length = TAS675X_WL_20BIT; + break; + case 24: + word_length = TAS675X_WL_24BIT; + break; + case 32: + word_length = TAS675X_WL_32BIT; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + /* + * RTLDG is not supported above 96kHz. Auto-disable to + * prevent DSP overload and restore when rate drops back. + */ + if (rate > 96000) { + unsigned int val; + + regmap_read(component->regmap, TAS675X_RTLDG_EN_REG, + &val); + if (val & TAS675X_RTLDG_CH_EN_MASK) { + tas->saved_rtldg_en = val; + dev_dbg(component->dev, + "Sample rate %dHz > 96kHz: Auto-disabling RTLDG\n", + rate); + regmap_update_bits(component->regmap, + TAS675X_RTLDG_EN_REG, + TAS675X_RTLDG_CH_EN_MASK, + 0x00); + } + } else if (tas->saved_rtldg_en) { + unsigned int cur; + + /* + * Respect overrides and only restore if RTLDG is still auto-disabled + */ + regmap_read(component->regmap, TAS675X_RTLDG_EN_REG, + &cur); + if (!(cur & TAS675X_RTLDG_CH_EN_MASK)) { + dev_dbg(component->dev, + "Restoring RTLDG config after high-rate stream\n"); + regmap_update_bits(component->regmap, + TAS675X_RTLDG_EN_REG, + TAS675X_RTLDG_CH_EN_MASK, + TAS675X_RTLDG_CH_EN_MASK & + tas->saved_rtldg_en); + } + tas->saved_rtldg_en = 0; + } + + /* Set SDIN word length (audio path + low-latency path) */ + regmap_update_bits(component->regmap, TAS675X_SDIN_CTRL_REG, + TAS675X_SDIN_WL_MASK, + FIELD_PREP(TAS675X_SDIN_AUDIO_WL_MASK, word_length) | + FIELD_PREP(TAS675X_SDIN_LL_WL_MASK, word_length)); + } else { + /* Set SDOUT word length (VPREDICT + ISENSE) for capture */ + regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG, + TAS675X_SDOUT_WL_MASK, + FIELD_PREP(TAS675X_SDOUT_VP_WL_MASK, word_length) | + FIELD_PREP(TAS675X_SDOUT_IS_WL_MASK, word_length)); + } + + tas675x_program_slot_offsets(tas, dai->id, + tas->slot_width ?: params_width(params)); + + tas->rate = rate; + + return 0; +} + +static int tas675x_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + struct tas675x_priv *tas = snd_soc_component_get_drvdata(component); + bool tdm_mode = false, i2s_mode = false; + + /* Enforce Clocking Direction (Codec is strictly a consumer) */ + switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { + case SND_SOC_DAIFMT_BC_FC: + break; + default: + dev_err(component->dev, "Unsupported clock provider format\n"); + return -EINVAL; + } + + /* SCLK polarity: NB_NF or IB_NF only (no FSYNC inversion support) */ + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG, + TAS675X_SCLK_INV_MASK, 0x00); + break; + case SND_SOC_DAIFMT_IB_NF: + regmap_update_bits(component->regmap, TAS675X_SCLK_INV_CTRL_REG, + TAS675X_SCLK_INV_MASK, TAS675X_SCLK_INV_MASK); + break; + default: + dev_err(component->dev, "Unsupported clock inversion\n"); + return -EINVAL; + } + + /* Configure Audio Format and TDM Enable */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + i2s_mode = true; + tas->bclk_offset = 0; + regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG, + TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK | + TAS675X_FS_PULSE_MASK, + TAS675X_SAP_FMT_I2S); + regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG, + TAS675X_SDOUT_SELECT_MASK, + TAS675X_SDOUT_SELECT_NON_TDM); + break; + case SND_SOC_DAIFMT_RIGHT_J: + tas->bclk_offset = 0; + regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG, + TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK | + TAS675X_FS_PULSE_MASK, + TAS675X_SAP_FMT_RIGHT_J); + regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG, + TAS675X_SDOUT_SELECT_MASK, + TAS675X_SDOUT_SELECT_NON_TDM); + break; + case SND_SOC_DAIFMT_LEFT_J: + tas->bclk_offset = 0; + regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG, + TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK | + TAS675X_FS_PULSE_MASK, + TAS675X_SAP_FMT_LEFT_J); + regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG, + TAS675X_SDOUT_SELECT_MASK, + TAS675X_SDOUT_SELECT_NON_TDM); + break; + case SND_SOC_DAIFMT_DSP_A: + tdm_mode = true; + tas->bclk_offset = 1; + regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG, + TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK | + TAS675X_FS_PULSE_MASK, + TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM | + TAS675X_FS_PULSE_SHORT); + regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG, + TAS675X_SDOUT_SELECT_MASK, + TAS675X_SDOUT_SELECT_TDM_SDOUT1); + break; + case SND_SOC_DAIFMT_DSP_B: + tdm_mode = true; + tas->bclk_offset = 0; + regmap_update_bits(component->regmap, TAS675X_AUDIO_IF_CTRL_REG, + TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_MASK | + TAS675X_FS_PULSE_MASK, + TAS675X_TDM_EN_BIT | TAS675X_SAP_FMT_TDM | + TAS675X_FS_PULSE_SHORT); + regmap_update_bits(component->regmap, TAS675X_SDOUT_CTRL_REG, + TAS675X_SDOUT_SELECT_MASK, + TAS675X_SDOUT_SELECT_TDM_SDOUT1); + break; + default: + dev_err(component->dev, "Unsupported DAI format\n"); + return -EINVAL; + } + + /* Setup Vpredict and Isense outputs */ + if (dai->id == 2) { + unsigned int sdout_en = 0; + + if (tdm_mode) { + /* TDM: Vpredict and Isense may coexist on separate slots */ + if (tas->vpredict_slot >= 0) + sdout_en |= TAS675X_SDOUT_EN_VPREDICT; + if (tas->isense_slot >= 0) + sdout_en |= TAS675X_SDOUT_EN_ISENSE; + regmap_update_bits(component->regmap, + TAS675X_SDOUT_EN_REG, + TAS675X_SDOUT_EN_VPREDICT | + TAS675X_SDOUT_EN_ISENSE, + sdout_en); + if (tas->vpredict_slot >= 0 && tas->isense_slot >= 0 && + abs(tas->vpredict_slot - tas->isense_slot) < 4) + dev_warn(component->dev, + "ti,vpredict-slot-no and ti,isense-slot-no overlaps (each occupies 4 consecutive slots)\n"); + } else if (i2s_mode) { + /* I2S: only one source at a time; Vpredict takes priority */ + if (tas->vpredict_slot >= 0) + sdout_en = TAS675X_SDOUT_NON_TDM_SEL_VPREDICT | + TAS675X_SDOUT_EN_NON_TDM_ALL; + else if (tas->isense_slot >= 0) + sdout_en = TAS675X_SDOUT_NON_TDM_SEL_ISENSE | + TAS675X_SDOUT_EN_NON_TDM_ALL; + regmap_update_bits(component->regmap, + TAS675X_SDOUT_EN_REG, + TAS675X_SDOUT_NON_TDM_SEL_MASK | + TAS675X_SDOUT_EN_NON_TDM_ALL, + sdout_en); + if (sdout_en && + tas->gpio1_func != TAS675X_GPIO_SEL_SDOUT2 && + tas->gpio2_func != TAS675X_GPIO_SEL_SDOUT2) + dev_warn(component->dev, + "sdout enabled in I2S mode but no GPIO configured as SDOUT2; Ch3/Ch4 will be absent\n"); + } + } + + return 0; +} + +static int tas675x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask, + unsigned int rx_mask, int slots, int slot_width) +{ + struct tas675x_priv *tas = snd_soc_component_get_drvdata(dai->component); + + if (slots == 0) { + tas->slot_width = 0; + tas->tx_mask = 0; + return 0; + } + + /* No rx_mask as hardware does not support channel muxing for capture */ + tas->slot_width = slot_width; + tas->tx_mask = tx_mask; + return 0; +} + +static int tas675x_mute_stream(struct snd_soc_dai *dai, int mute, int direction) +{ + struct snd_soc_component *component = dai->component; + struct tas675x_priv *tas = snd_soc_component_get_drvdata(component); + unsigned int discard; + int ret; + + if (direction == SNDRV_PCM_STREAM_CAPTURE) { + if (mute) + clear_bit(dai->id, &tas->active_capture_dais); + else + set_bit(dai->id, &tas->active_capture_dais); + return 0; + } + + /* + * Track which playback DAIs are active. + * The TAS675x has two playback DAIs (main audio and LLP). + * Only transition to SLEEP when ALL are muted. + */ + if (mute) + clear_bit(dai->id, &tas->active_playback_dais); + else + set_bit(dai->id, &tas->active_playback_dais); + + /* Last playback stream */ + if (mute && !READ_ONCE(tas->active_playback_dais)) { + ret = tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH); + regmap_read(tas->regmap, TAS675X_CLK_FAULT_LATCHED_REG, &discard); + return ret; + } + + return tas675x_set_state_all(tas, + READ_ONCE(tas->active_playback_dais) ? + TAS675X_STATE_PLAY_BOTH : + TAS675X_STATE_SLEEP_BOTH); +} + +static const struct snd_soc_dai_ops tas675x_dai_ops = { + .hw_params = tas675x_hw_params, + .set_fmt = tas675x_set_fmt, + .set_tdm_slot = tas675x_set_tdm_slot, + .mute_stream = tas675x_mute_stream, +}; + +static struct snd_soc_dai_driver tas675x_dais[] = { + { + .name = "tas675x-audio", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tas675x_dai_ops, + }, + /* Only available when Low Latency Path (LLP) is enabled */ + { + .name = "tas675x-anc", + .id = 1, + .playback = { + .stream_name = "ANC Playback", + .channels_min = 2, + .channels_max = 4, + .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tas675x_dai_ops, + }, + { + .name = "tas675x-feedback", + .id = 2, + .capture = { + .stream_name = "Feedback Capture", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_LE | + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE, + }, + .ops = &tas675x_dai_ops, + } +}; + +/* + * Enable regulators and release hardware reset GPIOs. + * The device is not I2C-accessible until this returns. + */ +static int tas675x_hw_enable(struct tas675x_priv *tas) +{ + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(tas->supplies), tas->supplies); + if (ret) { + dev_err(tas->dev, "Failed to enable regulators: %d\n", ret); + return ret; + } + + if (!IS_ERR(tas->vbat)) { + ret = regulator_enable(tas->vbat); + if (ret) { + dev_err(tas->dev, "Failed to enable vbat: %d\n", ret); + regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies); + return ret; + } + } + + if (tas->pd_gpio && tas->stby_gpio) { + /* + * Independent Pin Control + * Deassert PD first to boot digital, then STBY for analog. + */ + /* Min 4ms digital boot wait */ + gpiod_set_value_cansleep(tas->pd_gpio, 0); + usleep_range(4000, 5000); + + /* ~2ms analog stabilization */ + gpiod_set_value_cansleep(tas->stby_gpio, 0); + usleep_range(2000, 3000); + } else if (tas->pd_gpio) { + /* + * Simultaneous Pin Release + * STBY tied to PD or hardwired HIGH. + */ + /* 6ms wait for simultaneous release transition */ + gpiod_set_value_cansleep(tas->pd_gpio, 0); + usleep_range(6000, 7000); + } else { + /* + * PD hardwired, device in DEEP_SLEEP. + * Digital core already booted, I2C active. Deassert STBY + * to bring up the analog output stage. + */ + /* ~2ms analog stabilization */ + gpiod_set_value_cansleep(tas->stby_gpio, 0); + usleep_range(2000, 3000); + } + + return 0; +} + +static void tas675x_hw_disable(struct tas675x_priv *tas) +{ + if (tas->stby_gpio) + gpiod_set_value_cansleep(tas->stby_gpio, 1); + + if (tas->pd_gpio) + gpiod_set_value_cansleep(tas->pd_gpio, 1); + + /* + * Hold PD/STBY asserted for at least 10ms + * before removing PVDD, VBAT or DVDD. + */ + usleep_range(10000, 11000); + + if (!IS_ERR(tas->vbat)) + regulator_disable(tas->vbat); + + regulator_bulk_disable(ARRAY_SIZE(tas->supplies), tas->supplies); +} + +/* + * Write device start-up defaults. + * Must be called after tas675x_hw_enable() and after regcache is enabled. + */ +static int tas675x_init_device(struct tas675x_priv *tas) +{ + struct regmap *regmap = tas->regmap; + unsigned int val; + int ret, i; + + /* Clear POR fault flag to prevent IRQ storm */ + regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val); + + /* Bypass DC Load Diagnostics for fast boot */ + if (tas->fast_boot) + regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG, + TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT, + TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT); + + tas675x_select_book(regmap, TAS675X_BOOK_DEFAULT); + + /* Enter setup mode */ + ret = regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_ENTER_VAL1); + if (ret) + goto err; + ret = regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_ENTER_VAL2); + if (ret) + goto err; + + /* Set all channels to Sleep (required before Page 1 config) */ + tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH); + + /* Set DAC clock per TRM startup script */ + regmap_write(regmap, TAS675X_DAC_CLK_REG, 0x00); + + /* + * Switch to Page 1 for safety-critical OC/CBC configuration, + * while bypassing regcache. (Page 1 not accessible post setup) + */ + regcache_cache_bypass(regmap, true); + ret = regmap_multi_reg_write(regmap, tas675x_page1_init, + ARRAY_SIZE(tas675x_page1_init)); + regcache_cache_bypass(regmap, false); + if (ret) + goto err_setup; + + /* Resync regmap's cached page selector */ + regmap_write(regmap, TAS675X_PAGE_CTRL_REG, 0x00); + + /* Exit setup mode */ + regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL); + regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL); + + /* Write DSP parameters if cached */ + for (i = 0; i < ARRAY_SIZE(tas->dsp_params); i++) { + if (tas->dsp_params[i].val) + tas675x_dsp_mem_write(tas, + tas->dsp_params[i].page, + tas->dsp_params[i].reg, + tas->dsp_params[i].val); + } + + /* + * Configure fault and warning event routing: + * + * ROUTING_1: CP fault/UVLO latch, OUTM soft short latch + * ROUTING_2: CBC latch, OTSD latch, OTSD, power fault + * ROUTING_3: CBC latch, OTSD latch, power latch, DC LDG, + * OTSD, power warnings + * ROUTING_4: OC latch, DC latch, protection shutdown + * OTW latch, OTW, clip latch + * ROUTING_5: clock latch+non-latch, RTLDG latch + * CBC warning, clip warning + */ + regmap_write(regmap, TAS675X_REPORT_ROUTING_1_REG, 0x70); + regmap_write(regmap, TAS675X_REPORT_ROUTING_2_REG, 0xA3); + regmap_write(regmap, TAS675X_REPORT_ROUTING_3_REG, 0xBB); + regmap_write(regmap, TAS675X_REPORT_ROUTING_4_REG, 0x7E); + regmap_write(regmap, TAS675X_REPORT_ROUTING_5_REG, 0xF3); + + /* Configure GPIO pins if specified in DT */ + if (tas->gpio1_func >= 0 || tas->gpio2_func >= 0) { + unsigned int gpio_ctrl = TAS675X_GPIO_CTRL_RSTVAL; + + tas675x_config_gpio_pin(regmap, tas->gpio1_func, + TAS675X_GPIO1_OUTPUT_SEL_REG, + 0, &gpio_ctrl); + tas675x_config_gpio_pin(regmap, tas->gpio2_func, + TAS675X_GPIO2_OUTPUT_SEL_REG, + 1, &gpio_ctrl); + regmap_write(regmap, TAS675X_GPIO_CTRL_REG, gpio_ctrl); + } + + /* Clear fast boot bits */ + if (tas->fast_boot) + regmap_update_bits(regmap, TAS675X_DC_LDG_CTRL_REG, + TAS675X_LDG_ABORT_BIT | TAS675X_LDG_BYPASS_BIT, + 0); + + /* Clear any stale faults from the boot sequence */ + regmap_read(regmap, TAS675X_POWER_FAULT_STATUS_1_REG, &val); + regmap_read(regmap, TAS675X_POWER_FAULT_LATCHED_REG, &val); + regmap_read(regmap, TAS675X_CLK_FAULT_LATCHED_REG, &val); + regmap_write(regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR); + + return 0; + +err_setup: + regmap_write(regmap, TAS675X_SETUP_REG1, TAS675X_SETUP_EXIT_VAL); + regmap_write(regmap, TAS675X_SETUP_REG2, TAS675X_SETUP_EXIT_VAL); +err: + dev_err(tas->dev, "Init device failed: %d\n", ret); + return ret; +} + +static void tas675x_power_off(struct tas675x_priv *tas) +{ + regcache_cache_only(tas->regmap, true); + regcache_mark_dirty(tas->regmap); + tas675x_hw_disable(tas); +} + +static int tas675x_power_on(struct tas675x_priv *tas) +{ + int ret; + + ret = tas675x_hw_enable(tas); + if (ret) + return ret; + + regcache_cache_only(tas->regmap, false); + regcache_mark_dirty(tas->regmap); + + ret = tas675x_init_device(tas); + if (ret) + goto err_disable; + + ret = regcache_sync(tas->regmap); + if (ret) { + dev_err(tas->dev, "Failed to sync regcache: %d\n", ret); + goto err_disable; + } + + /* Reset fault tracking */ + memset(tas->last_status, 0, sizeof(tas->last_status)); + + return 0; + +err_disable: + tas675x_power_off(tas); + return ret; +} + +static int tas675x_runtime_suspend(struct device *dev) +{ + struct tas675x_priv *tas = dev_get_drvdata(dev); + + disable_delayed_work_sync(&tas->fault_check_work); + tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH); + + return 0; +} + +static int tas675x_runtime_resume(struct device *dev) +{ + struct tas675x_priv *tas = dev_get_drvdata(dev); + + tas675x_set_state_all(tas, TAS675X_STATE_SLEEP_BOTH); + + if (!to_i2c_client(dev)->irq) { + enable_delayed_work(&tas->fault_check_work); + schedule_delayed_work(&tas->fault_check_work, + msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS)); + } + + return 0; +} + +static int tas675x_system_suspend(struct device *dev) +{ + struct tas675x_priv *tas = dev_get_drvdata(dev); + int ret; + + ret = tas675x_runtime_suspend(dev); + if (ret) + return ret; + + if (to_i2c_client(dev)->irq) + disable_irq(to_i2c_client(dev)->irq); + + tas675x_power_off(tas); + return 0; +} + +static int tas675x_system_resume(struct device *dev) +{ + struct tas675x_priv *tas = dev_get_drvdata(dev); + int ret; + + ret = tas675x_power_on(tas); + if (ret) + return ret; + + if (to_i2c_client(dev)->irq) + enable_irq(to_i2c_client(dev)->irq); + + return tas675x_runtime_resume(dev); +} + +static const struct snd_soc_component_driver soc_codec_dev_tas675x = { + .controls = tas675x_snd_controls, + .num_controls = ARRAY_SIZE(tas675x_snd_controls), + .dapm_widgets = tas675x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas675x_dapm_widgets), + .dapm_routes = tas675x_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(tas675x_dapm_routes), + .endianness = 1, +}; + +/* Fault register flags */ +#define TAS675X_FAULT_CRITICAL BIT(0) /* causes FAULT state, FAULT_CLEAR required */ +#define TAS675X_FAULT_TRACK BIT(1) /* track last value, only log on change */ +#define TAS675X_FAULT_ACTIVE BIT(2) /* skip when no stream is active */ + +struct tas675x_fault_reg { + unsigned int reg; + unsigned int flags; + const char *name; +}; + +static const struct tas675x_fault_reg tas675x_fault_table[] = { + /* Critical */ + { TAS675X_OTSD_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK, + "Overtemperature Shutdown" }, + { TAS675X_OC_DC_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK, + "Overcurrent / DC Fault" }, + { TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK, + "Real-Time Load Diagnostic Fault" }, + { TAS675X_CBC_FAULT_WARN_LATCHED_REG, TAS675X_FAULT_CRITICAL | TAS675X_FAULT_TRACK, + "CBC Fault/Warning" }, + /* Warning */ + { TAS675X_POWER_FAULT_STATUS_1_REG, TAS675X_FAULT_TRACK, + "CP / OUTM Fault" }, + { TAS675X_POWER_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK, + "Power Fault" }, + { TAS675X_CLK_FAULT_LATCHED_REG, TAS675X_FAULT_TRACK | TAS675X_FAULT_ACTIVE, + "Clock Fault" }, + { TAS675X_OTW_LATCHED_REG, TAS675X_FAULT_TRACK, + "Overtemperature Warning" }, + { TAS675X_CLIP_WARN_LATCHED_REG, TAS675X_FAULT_ACTIVE, + "Clip Warning" }, +}; + +static_assert(ARRAY_SIZE(tas675x_fault_table) == TAS675X_FAULT_REGS_NUM); + +/* + * Read and log all latched fault registers. + * Shared by both the polled fault_check_work and IRQ handler paths + * (which are mutually exclusive, only one is active per device). + * Returns true if any fault register needs to be cleared. + * + * For deciphering fault messages, see "Fault Monitoring" in + * Documentation/sound/codecs/tas675x.rst + */ +static bool tas675x_check_faults(struct tas675x_priv *tas) +{ + struct device *dev = tas->dev; + bool needs_clear = false; + unsigned int reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(tas675x_fault_table); i++) { + const struct tas675x_fault_reg *f = &tas675x_fault_table[i]; + + ret = regmap_read(tas->regmap, f->reg, ®); + if (ret) { + if (f->flags & TAS675X_FAULT_CRITICAL) { + dev_err(dev, "failed to read %s: %d\n", f->name, ret); + return needs_clear; + } + continue; + } + + if (reg) + needs_clear = true; + + /* Skip logging stream-dependent events when no stream is active */ + if ((f->flags & TAS675X_FAULT_ACTIVE) && + !READ_ONCE(tas->active_playback_dais) && + !READ_ONCE(tas->active_capture_dais)) + continue; + + /* Log on change or on every non-zero read */ + if (reg && (!(f->flags & TAS675X_FAULT_TRACK) || + reg != tas->last_status[i])) { + if (f->flags & TAS675X_FAULT_CRITICAL) + dev_crit(dev, "%s Latched: 0x%02x\n", f->name, reg); + else + dev_warn(dev, "%s Latched: 0x%02x\n", f->name, reg); + } + + if (f->flags & TAS675X_FAULT_TRACK) + tas->last_status[i] = reg; + } + + return needs_clear; +} + +static void tas675x_fault_check_work(struct work_struct *work) +{ + struct tas675x_priv *tas = container_of(work, struct tas675x_priv, + fault_check_work.work); + + if (tas675x_check_faults(tas)) + regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR); + + schedule_delayed_work(&tas->fault_check_work, + msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS)); +} + +static irqreturn_t tas675x_irq_handler(int irq, void *data) +{ + struct tas675x_priv *tas = data; + irqreturn_t ret = IRQ_NONE; + + if (pm_runtime_resume_and_get(tas->dev) < 0) + return IRQ_NONE; + + if (tas675x_check_faults(tas)) { + regmap_write(tas->regmap, TAS675X_RESET_REG, TAS675X_FAULT_CLEAR); + ret = IRQ_HANDLED; + } + + pm_runtime_mark_last_busy(tas->dev); + pm_runtime_put_autosuspend(tas->dev); + return ret; +} + +static const struct reg_default tas675x_reg_defaults[] = { + { TAS675X_PAGE_CTRL_REG, 0x00 }, + { TAS675X_OUTPUT_CTRL_REG, 0x00 }, + { TAS675X_STATE_CTRL_CH1_CH2_REG, TAS675X_STATE_SLEEP_BOTH }, + { TAS675X_STATE_CTRL_CH3_CH4_REG, TAS675X_STATE_SLEEP_BOTH }, + { TAS675X_ISENSE_CTRL_REG, 0x0F }, + { TAS675X_DC_DETECT_CTRL_REG, 0x00 }, + { TAS675X_SCLK_INV_CTRL_REG, 0x00 }, + { TAS675X_AUDIO_IF_CTRL_REG, 0x00 }, + { TAS675X_SDIN_CTRL_REG, 0x0A }, + { TAS675X_SDOUT_CTRL_REG, 0x1A }, + { TAS675X_SDIN_OFFSET_MSB_REG, 0x00 }, + { TAS675X_SDIN_AUDIO_OFFSET_REG, 0x00 }, + { TAS675X_SDIN_LL_OFFSET_REG, 0x60 }, + { TAS675X_SDIN_CH_SWAP_REG, 0x00 }, + { TAS675X_SDOUT_OFFSET_MSB_REG, 0xCF }, + { TAS675X_VPREDICT_OFFSET_REG, 0xFF }, + { TAS675X_ISENSE_OFFSET_REG, 0x00 }, + { TAS675X_SDOUT_EN_REG, 0x00 }, + { TAS675X_LL_EN_REG, 0x00 }, + { TAS675X_RTLDG_EN_REG, 0x10 }, + { TAS675X_DC_BLOCK_BYP_REG, 0x00 }, + { TAS675X_DSP_CTRL_REG, 0x00 }, + { TAS675X_PAGE_AUTO_INC_REG, 0x00 }, + { TAS675X_DIG_VOL_CH1_REG, 0x30 }, + { TAS675X_DIG_VOL_CH2_REG, 0x30 }, + { TAS675X_DIG_VOL_CH3_REG, 0x30 }, + { TAS675X_DIG_VOL_CH4_REG, 0x30 }, + { TAS675X_DIG_VOL_RAMP_CTRL_REG, 0x77 }, + { TAS675X_DIG_VOL_COMBINE_CTRL_REG, 0x00 }, + { TAS675X_AUTO_MUTE_EN_REG, 0x00 }, + { TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG, 0x00 }, + { TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG, 0x00 }, + { TAS675X_ANALOG_GAIN_CH1_CH2_REG, 0x00 }, + { TAS675X_ANALOG_GAIN_CH3_CH4_REG, 0x00 }, + { TAS675X_ANALOG_GAIN_RAMP_CTRL_REG, 0x00 }, + { TAS675X_PULSE_INJECTION_EN_REG, 0x03 }, + { TAS675X_CBC_CTRL_REG, 0x07 }, + { TAS675X_CURRENT_LIMIT_CTRL_REG, 0x00 }, + { TAS675X_ISENSE_CAL_REG, 0x00 }, + { TAS675X_PWM_PHASE_CTRL_REG, 0x00 }, + { TAS675X_SS_CTRL_REG, 0x00 }, + { TAS675X_SS_RANGE_CTRL_REG, 0x00 }, + { TAS675X_SS_DWELL_CTRL_REG, 0x00 }, + { TAS675X_RAMP_PHASE_CTRL_GPO_REG, 0x00 }, + { TAS675X_PWM_PHASE_M_CTRL_CH1_REG, 0x00 }, + { TAS675X_PWM_PHASE_M_CTRL_CH2_REG, 0x00 }, + { TAS675X_PWM_PHASE_M_CTRL_CH3_REG, 0x00 }, + { TAS675X_PWM_PHASE_M_CTRL_CH4_REG, 0x00 }, + { TAS675X_DC_LDG_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_LO_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_TIME_CTRL_REG, 0x00 }, + { TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG, 0x11 }, + { TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG, 0x11 }, + { TAS675X_AC_LDG_CTRL_REG, 0x10 }, + { TAS675X_TWEETER_DETECT_CTRL_REG, 0x08 }, + { TAS675X_TWEETER_DETECT_THRESH_REG, 0x00 }, + { TAS675X_AC_LDG_FREQ_CTRL_REG, 0xC8 }, + { TAS675X_REPORT_ROUTING_1_REG, 0x00 }, + { TAS675X_OTSD_RECOVERY_EN_REG, 0x00 }, + { TAS675X_REPORT_ROUTING_2_REG, 0xA2 }, + { TAS675X_REPORT_ROUTING_3_REG, 0x00 }, + { TAS675X_REPORT_ROUTING_4_REG, 0x06 }, + { TAS675X_CLIP_DETECT_CTRL_REG, 0x00 }, + { TAS675X_REPORT_ROUTING_5_REG, 0x00 }, + { TAS675X_GPIO1_OUTPUT_SEL_REG, 0x00 }, + { TAS675X_GPIO2_OUTPUT_SEL_REG, 0x00 }, + { TAS675X_GPIO_CTRL_REG, TAS675X_GPIO_CTRL_RSTVAL }, + { TAS675X_OTW_CTRL_CH1_CH2_REG, 0x11 }, + { TAS675X_OTW_CTRL_CH3_CH4_REG, 0x11 }, +}; + +static bool tas675x_is_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS675X_RESET_REG: + return false; + default: + return true; + } +} + +static bool tas675x_is_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case TAS675X_RESET_REG: + case TAS675X_BOOK_CTRL_REG: + case TAS675X_AUTO_MUTE_STATUS_REG: + case TAS675X_STATE_REPORT_CH1_CH2_REG: + case TAS675X_STATE_REPORT_CH3_CH4_REG: + case TAS675X_PVDD_SENSE_REG: + case TAS675X_TEMP_GLOBAL_REG: + case TAS675X_TEMP_CH1_CH2_REG: + case TAS675X_TEMP_CH3_CH4_REG: + case TAS675X_FS_MON_REG: + case TAS675X_SCLK_MON_REG: + case TAS675X_POWER_FAULT_STATUS_1_REG: + case TAS675X_POWER_FAULT_STATUS_2_REG: + case TAS675X_OT_FAULT_REG: + case TAS675X_OTW_STATUS_REG: + case TAS675X_CLIP_WARN_STATUS_REG: + case TAS675X_CBC_WARNING_STATUS_REG: + case TAS675X_POWER_FAULT_LATCHED_REG: + case TAS675X_OTSD_LATCHED_REG: + case TAS675X_OTW_LATCHED_REG: + case TAS675X_CLIP_WARN_LATCHED_REG: + case TAS675X_CLK_FAULT_LATCHED_REG: + case TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG: + case TAS675X_CBC_FAULT_WARN_LATCHED_REG: + case TAS675X_OC_DC_FAULT_LATCHED_REG: + case TAS675X_WARN_OT_MAX_FLAG_REG: + case TAS675X_DC_LDG_REPORT_CH1_CH2_REG ... TAS675X_TWEETER_REPORT_REG: + case TAS675X_CH1_RTLDG_IMP_MSB_REG ... TAS675X_CH4_DC_LDG_DCR_LSB_REG: + return true; + default: + return false; + } +} + +static const struct regmap_range_cfg tas675x_ranges[] = { + { + .name = "Pages", + .range_min = 0, + .range_max = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1, + .selector_reg = TAS675X_PAGE_CTRL_REG, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = TAS675X_PAGE_SIZE, + }, +}; + +static void tas675x_regmap_lock(void *lock_arg) +{ + struct tas675x_priv *tas = lock_arg; + + mutex_lock(&tas->io_lock); +} + +static void tas675x_regmap_unlock(void *lock_arg) +{ + struct tas675x_priv *tas = lock_arg; + + mutex_unlock(&tas->io_lock); +} + +static const struct regmap_config tas675x_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = TAS675X_PAGE_SIZE * TAS675X_PAGE_SIZE - 1, + .ranges = tas675x_ranges, + .num_ranges = ARRAY_SIZE(tas675x_ranges), + .cache_type = REGCACHE_MAPLE, + .reg_defaults = tas675x_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(tas675x_reg_defaults), + .readable_reg = tas675x_is_readable_register, + .volatile_reg = tas675x_is_volatile_register, +}; + +static int tas675x_i2c_probe(struct i2c_client *client) +{ + struct regmap_config cfg = tas675x_regmap_config; + struct tas675x_priv *tas; + u32 val; + int i, ret; + + tas = devm_kzalloc(&client->dev, sizeof(*tas), GFP_KERNEL); + if (!tas) + return -ENOMEM; + + tas->dev = &client->dev; + i2c_set_clientdata(client, tas); + + mutex_init(&tas->io_lock); + cfg.lock = tas675x_regmap_lock; + cfg.unlock = tas675x_regmap_unlock; + cfg.lock_arg = tas; + + memcpy(tas->dsp_params, tas675x_dsp_defaults, sizeof(tas->dsp_params)); + INIT_DELAYED_WORK(&tas->fault_check_work, tas675x_fault_check_work); + + tas->regmap = devm_regmap_init_i2c(client, &cfg); + if (IS_ERR(tas->regmap)) + return PTR_ERR(tas->regmap); + + /* Keep regmap cache-only until hardware is powered on */ + regcache_cache_only(tas->regmap, true); + + tas->dev_type = (enum tas675x_type)(unsigned long)device_get_match_data(tas->dev); + tas->fast_boot = device_property_read_bool(tas->dev, "ti,fast-boot"); + + tas->audio_slot = -1; + tas->llp_slot = -1; + tas->vpredict_slot = -1; + tas->isense_slot = -1; + if (!device_property_read_u32(tas->dev, "ti,audio-slot-no", &val)) + tas->audio_slot = val; + if (!device_property_read_u32(tas->dev, "ti,llp-slot-no", &val)) + tas->llp_slot = val; + if (!device_property_read_u32(tas->dev, "ti,vpredict-slot-no", &val)) + tas->vpredict_slot = val; + if (!device_property_read_u32(tas->dev, "ti,isense-slot-no", &val)) + tas->isense_slot = val; + + tas->gpio1_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio1-function"); + tas->gpio2_func = tas675x_gpio_func_parse(tas->dev, "ti,gpio2-function"); + + for (i = 0; i < ARRAY_SIZE(tas675x_supply_names); i++) + tas->supplies[i].supply = tas675x_supply_names[i]; + + ret = devm_regulator_bulk_get(tas->dev, ARRAY_SIZE(tas->supplies), tas->supplies); + if (ret) + return dev_err_probe(tas->dev, ret, "Failed to request supplies\n"); + + tas->vbat = devm_regulator_get_optional(tas->dev, "vbat"); + if (IS_ERR(tas->vbat) && PTR_ERR(tas->vbat) != -ENODEV) + return dev_err_probe(tas->dev, PTR_ERR(tas->vbat), + "Failed to get vbat supply\n"); + + tas->pd_gpio = devm_gpiod_get_optional(tas->dev, "powerdown", GPIOD_OUT_HIGH); + if (IS_ERR(tas->pd_gpio)) + return dev_err_probe(tas->dev, PTR_ERR(tas->pd_gpio), "Failed powerdown-gpios\n"); + + tas->stby_gpio = devm_gpiod_get_optional(tas->dev, "standby", GPIOD_OUT_HIGH); + if (IS_ERR(tas->stby_gpio)) + return dev_err_probe(tas->dev, PTR_ERR(tas->stby_gpio), "Failed standby-gpios\n"); + + if (!tas->pd_gpio && !tas->stby_gpio) + return dev_err_probe(tas->dev, -EINVAL, + "At least one of powerdown-gpios or standby-gpios is required\n"); + + ret = tas675x_power_on(tas); + if (ret) + return ret; + + if (client->irq) { + ret = devm_request_threaded_irq(tas->dev, client->irq, NULL, + tas675x_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_FALLING, + "tas675x-fault", tas); + if (ret) { + tas675x_power_off(tas); + return dev_err_probe(tas->dev, ret, "Failed to request IRQ\n"); + } + } else { + /* Schedule delayed work for fault checking at probe and runtime resume */ + schedule_delayed_work(&tas->fault_check_work, + msecs_to_jiffies(TAS675X_FAULT_CHECK_INTERVAL_MS)); + } + + /* Enable runtime PM with 2s autosuspend */ + pm_runtime_set_autosuspend_delay(tas->dev, 2000); + pm_runtime_use_autosuspend(tas->dev); + pm_runtime_set_active(tas->dev); + pm_runtime_mark_last_busy(tas->dev); + pm_runtime_enable(tas->dev); + + ret = devm_snd_soc_register_component(tas->dev, &soc_codec_dev_tas675x, + tas675x_dais, ARRAY_SIZE(tas675x_dais)); + if (ret) + goto err_pm_disable; + + return 0; + +err_pm_disable: + pm_runtime_force_suspend(tas->dev); + pm_runtime_disable(tas->dev); + tas675x_power_off(tas); + return ret; +} + +static void tas675x_i2c_remove(struct i2c_client *client) +{ + struct tas675x_priv *tas = dev_get_drvdata(&client->dev); + + disable_delayed_work_sync(&tas->fault_check_work); + if (client->irq) + disable_irq(client->irq); + + pm_runtime_force_suspend(&client->dev); + pm_runtime_disable(&client->dev); + tas675x_power_off(tas); +} + +static const struct dev_pm_ops tas675x_pm_ops = { + SYSTEM_SLEEP_PM_OPS(tas675x_system_suspend, tas675x_system_resume) + RUNTIME_PM_OPS(tas675x_runtime_suspend, tas675x_runtime_resume, NULL) +}; + +static const struct of_device_id tas675x_of_match[] = { + { .compatible = "ti,tas67524", .data = (void *)TAS67524 }, + { } +}; +MODULE_DEVICE_TABLE(of, tas675x_of_match); + +static const struct i2c_device_id tas675x_i2c_id[] = { + { "tas67524", TAS67524 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tas675x_i2c_id); + +static struct i2c_driver tas675x_i2c_driver = { + .driver = { + .name = "tas675x", + .of_match_table = tas675x_of_match, + .pm = pm_ptr(&tas675x_pm_ops), + }, + .probe = tas675x_i2c_probe, + .remove = tas675x_i2c_remove, + .id_table = tas675x_i2c_id, +}; + +module_i2c_driver(tas675x_i2c_driver); + +MODULE_AUTHOR("Sen Wang <sen@ti.com>"); +MODULE_DESCRIPTION("ASoC TAS675x Audio Amplifier Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas675x.h b/sound/soc/codecs/tas675x.h new file mode 100644 index 000000000000..db29bb377336 --- /dev/null +++ b/sound/soc/codecs/tas675x.h @@ -0,0 +1,367 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * ALSA SoC Texas Instruments TAS675x Quad-Channel Audio Amplifier + * + * Copyright (C) 2026 Texas Instruments Incorporated - https://www.ti.com/ + * Author: Sen Wang <sen@ti.com> + */ + +#ifndef __TAS675X_H__ +#define __TAS675X_H__ + +/* + * Book 0, Page 0 — Register Addresses + */ + +#define TAS675X_PAGE_SIZE 256 +#define TAS675X_PAGE_REG(page, reg) ((page) * TAS675X_PAGE_SIZE + (reg)) + +/* Page Control & Basic Config */ +#define TAS675X_PAGE_CTRL_REG 0x00 +#define TAS675X_RESET_REG 0x01 +#define TAS675X_OUTPUT_CTRL_REG 0x02 +#define TAS675X_STATE_CTRL_CH1_CH2_REG 0x03 +#define TAS675X_STATE_CTRL_CH3_CH4_REG 0x04 +#define TAS675X_ISENSE_CTRL_REG 0x05 +#define TAS675X_DC_DETECT_CTRL_REG 0x06 + +/* Serial Audio Port */ +#define TAS675X_SCLK_INV_CTRL_REG 0x20 +#define TAS675X_AUDIO_IF_CTRL_REG 0x21 +#define TAS675X_SDIN_CTRL_REG 0x23 +#define TAS675X_SDOUT_CTRL_REG 0x25 +#define TAS675X_SDIN_OFFSET_MSB_REG 0x27 +#define TAS675X_SDIN_AUDIO_OFFSET_REG 0x28 +#define TAS675X_SDIN_LL_OFFSET_REG 0x29 +#define TAS675X_SDIN_CH_SWAP_REG 0x2A +#define TAS675X_SDOUT_OFFSET_MSB_REG 0x2C +#define TAS675X_VPREDICT_OFFSET_REG 0x2D +#define TAS675X_ISENSE_OFFSET_REG 0x2E +#define TAS675X_SDOUT_EN_REG 0x31 +#define TAS675X_LL_EN_REG 0x32 + +/* DSP & Core Audio Control */ +#define TAS675X_RTLDG_EN_REG 0x37 +#define TAS675X_DC_BLOCK_BYP_REG 0x39 +#define TAS675X_DSP_CTRL_REG 0x3A +#define TAS675X_PAGE_AUTO_INC_REG 0x3B + +/* Volume & Mute */ +#define TAS675X_DIG_VOL_CH1_REG 0x40 +#define TAS675X_DIG_VOL_CH2_REG 0x41 +#define TAS675X_DIG_VOL_CH3_REG 0x42 +#define TAS675X_DIG_VOL_CH4_REG 0x43 +#define TAS675X_DIG_VOL_RAMP_CTRL_REG 0x44 +#define TAS675X_DIG_VOL_COMBINE_CTRL_REG 0x46 +#define TAS675X_AUTO_MUTE_EN_REG 0x47 +#define TAS675X_AUTO_MUTE_TIMING_CH1_CH2_REG 0x48 +#define TAS675X_AUTO_MUTE_TIMING_CH3_CH4_REG 0x49 + +/* Analog Gain & Power Stage */ +#define TAS675X_ANALOG_GAIN_CH1_CH2_REG 0x4A +#define TAS675X_ANALOG_GAIN_CH3_CH4_REG 0x4B +#define TAS675X_ANALOG_GAIN_RAMP_CTRL_REG 0x4E +#define TAS675X_PULSE_INJECTION_EN_REG 0x52 +#define TAS675X_CBC_CTRL_REG 0x54 +#define TAS675X_CURRENT_LIMIT_CTRL_REG 0x55 +#define TAS675X_DAC_CLK_REG 0x5A +#define TAS675X_ISENSE_CAL_REG 0x5B + +/* Spread Spectrum & PWM Phase */ +#define TAS675X_PWM_PHASE_CTRL_REG 0x60 +#define TAS675X_SS_CTRL_REG 0x61 +#define TAS675X_SS_RANGE_CTRL_REG 0x62 +#define TAS675X_SS_DWELL_CTRL_REG 0x66 +#define TAS675X_RAMP_PHASE_CTRL_GPO_REG 0x68 +#define TAS675X_PWM_PHASE_M_CTRL_CH1_REG 0x69 +#define TAS675X_PWM_PHASE_M_CTRL_CH2_REG 0x6A +#define TAS675X_PWM_PHASE_M_CTRL_CH3_REG 0x6B +#define TAS675X_PWM_PHASE_M_CTRL_CH4_REG 0x6C + +/* Status & Reporting */ +#define TAS675X_AUTO_MUTE_STATUS_REG 0x71 +#define TAS675X_STATE_REPORT_CH1_CH2_REG 0x72 +#define TAS675X_STATE_REPORT_CH3_CH4_REG 0x73 +#define TAS675X_PVDD_SENSE_REG 0x74 +#define TAS675X_TEMP_GLOBAL_REG 0x75 +#define TAS675X_FS_MON_REG 0x76 +#define TAS675X_SCLK_MON_REG 0x77 +#define TAS675X_REPORT_ROUTING_1_REG 0x7C + +/* Memory Paging & Book Control */ +#define TAS675X_SETUP_REG1 0x7D +#define TAS675X_SETUP_REG2 0x7E +#define TAS675X_BOOK_CTRL_REG 0x7F + +/* Fault Status */ +#define TAS675X_POWER_FAULT_STATUS_1_REG 0x7D +#define TAS675X_POWER_FAULT_STATUS_2_REG 0x80 +#define TAS675X_OT_FAULT_REG 0x81 +#define TAS675X_OTW_STATUS_REG 0x82 +#define TAS675X_CLIP_WARN_STATUS_REG 0x83 +#define TAS675X_CBC_WARNING_STATUS_REG 0x85 + +/* Latched Fault Registers */ +#define TAS675X_POWER_FAULT_LATCHED_REG 0x86 +#define TAS675X_OTSD_LATCHED_REG 0x87 +#define TAS675X_OTW_LATCHED_REG 0x88 +#define TAS675X_CLIP_WARN_LATCHED_REG 0x89 +#define TAS675X_CLK_FAULT_LATCHED_REG 0x8A +#define TAS675X_RTLDG_OL_SL_FAULT_LATCHED_REG 0x8B +#define TAS675X_CBC_FAULT_WARN_LATCHED_REG 0x8D +#define TAS675X_OC_DC_FAULT_LATCHED_REG 0x8E +#define TAS675X_OTSD_RECOVERY_EN_REG 0x8F + +/* Protection & Routing Controls */ +#define TAS675X_REPORT_ROUTING_2_REG 0x90 +#define TAS675X_REPORT_ROUTING_3_REG 0x91 +#define TAS675X_REPORT_ROUTING_4_REG 0x92 +#define TAS675X_CLIP_DETECT_CTRL_REG 0x93 +#define TAS675X_REPORT_ROUTING_5_REG 0x94 + +/* GPIO Pin Configuration */ +#define TAS675X_GPIO1_OUTPUT_SEL_REG 0x95 +#define TAS675X_GPIO2_OUTPUT_SEL_REG 0x96 +#define TAS675X_GPIO_INPUT_SLEEP_HIZ_REG 0x9B +#define TAS675X_GPIO_INPUT_PLAY_SLEEP_REG 0x9C +#define TAS675X_GPIO_INPUT_MUTE_REG 0x9D +#define TAS675X_GPIO_INPUT_SYNC_REG 0x9E +#define TAS675X_GPIO_INPUT_SDIN2_REG 0x9F +#define TAS675X_GPIO_CTRL_REG 0xA0 +#define TAS675X_GPIO_INVERT_REG 0xA1 + +/* Load Diagnostics Config */ +#define TAS675X_DC_LDG_CTRL_REG 0xB0 +#define TAS675X_DC_LDG_LO_CTRL_REG 0xB1 +#define TAS675X_DC_LDG_TIME_CTRL_REG 0xB2 +#define TAS675X_DC_LDG_SL_CH1_CH2_CTRL_REG 0xB3 +#define TAS675X_DC_LDG_SL_CH3_CH4_CTRL_REG 0xB4 +#define TAS675X_AC_LDG_CTRL_REG 0xB5 +#define TAS675X_TWEETER_DETECT_CTRL_REG 0xB6 +#define TAS675X_TWEETER_DETECT_THRESH_REG 0xB7 +#define TAS675X_AC_LDG_FREQ_CTRL_REG 0xB8 +#define TAS675X_TEMP_CH1_CH2_REG 0xBB +#define TAS675X_TEMP_CH3_CH4_REG 0xBC +#define TAS675X_WARN_OT_MAX_FLAG_REG 0xBD + +/* DC Load Diagnostic Reports */ +#define TAS675X_DC_LDG_REPORT_CH1_CH2_REG 0xC0 +#define TAS675X_DC_LDG_REPORT_CH3_CH4_REG 0xC1 +#define TAS675X_DC_LDG_RESULT_REG 0xC2 +#define TAS675X_AC_LDG_REPORT_CH1_R_REG 0xC3 +#define TAS675X_AC_LDG_REPORT_CH1_I_REG 0xC4 +#define TAS675X_AC_LDG_REPORT_CH2_R_REG 0xC5 +#define TAS675X_AC_LDG_REPORT_CH2_I_REG 0xC6 +#define TAS675X_AC_LDG_REPORT_CH3_R_REG 0xC7 +#define TAS675X_AC_LDG_REPORT_CH3_I_REG 0xC8 +#define TAS675X_AC_LDG_REPORT_CH4_R_REG 0xC9 +#define TAS675X_AC_LDG_REPORT_CH4_I_REG 0xCA +#define TAS675X_TWEETER_REPORT_REG 0xCB + +/* RTLDG Impedance */ +#define TAS675X_CH1_RTLDG_IMP_MSB_REG 0xD1 +#define TAS675X_CH1_RTLDG_IMP_LSB_REG 0xD2 +#define TAS675X_CH2_RTLDG_IMP_MSB_REG 0xD3 +#define TAS675X_CH2_RTLDG_IMP_LSB_REG 0xD4 +#define TAS675X_CH3_RTLDG_IMP_MSB_REG 0xD5 +#define TAS675X_CH3_RTLDG_IMP_LSB_REG 0xD6 +#define TAS675X_CH4_RTLDG_IMP_MSB_REG 0xD7 +#define TAS675X_CH4_RTLDG_IMP_LSB_REG 0xD8 + +/* DC Load Diagnostic Resistance */ +#define TAS675X_DC_LDG_DCR_MSB_REG 0xD9 +#define TAS675X_CH1_DC_LDG_DCR_LSB_REG 0xDA +#define TAS675X_CH2_DC_LDG_DCR_LSB_REG 0xDB +#define TAS675X_CH3_DC_LDG_DCR_LSB_REG 0xDC +#define TAS675X_CH4_DC_LDG_DCR_LSB_REG 0xDD + +/* Over-Temperature Warning */ +#define TAS675X_OTW_CTRL_CH1_CH2_REG 0xE2 +#define TAS675X_OTW_CTRL_CH3_CH4_REG 0xE3 + +/* RESET_REG (all bits auto-clear) */ +#define TAS675X_DEVICE_RESET BIT(4) +#define TAS675X_FAULT_CLEAR BIT(3) +#define TAS675X_REGISTER_RESET BIT(0) + +/* STATE_CTRL and STATE_REPORT — Channel state values */ +#define TAS675X_STATE_DEEPSLEEP 0x00 +#define TAS675X_STATE_LOAD_DIAG 0x01 +#define TAS675X_STATE_SLEEP 0x02 +#define TAS675X_STATE_HIZ 0x03 +#define TAS675X_STATE_PLAY 0x04 + +/* Additional STATE_REPORT values */ +#define TAS675X_STATE_FAULT 0x05 +#define TAS675X_STATE_AUTOREC 0x06 + +/* Combined values for both channel pairs in one register */ +#define TAS675X_STATE_DEEPSLEEP_BOTH \ + (TAS675X_STATE_DEEPSLEEP | (TAS675X_STATE_DEEPSLEEP << 4)) +#define TAS675X_STATE_LOAD_DIAG_BOTH \ + (TAS675X_STATE_LOAD_DIAG | (TAS675X_STATE_LOAD_DIAG << 4)) +#define TAS675X_STATE_SLEEP_BOTH \ + (TAS675X_STATE_SLEEP | (TAS675X_STATE_SLEEP << 4)) +#define TAS675X_STATE_HIZ_BOTH \ + (TAS675X_STATE_HIZ | (TAS675X_STATE_HIZ << 4)) +#define TAS675X_STATE_PLAY_BOTH \ + (TAS675X_STATE_PLAY | (TAS675X_STATE_PLAY << 4)) +#define TAS675X_STATE_FAULT_BOTH \ + (TAS675X_STATE_FAULT | (TAS675X_STATE_FAULT << 4)) + +/* STATE_CTRL_CH1_CH2 / STATE_CTRL_CH3_CH4 — mute bits */ +#define TAS675X_CH1_MUTE_BIT BIT(7) +#define TAS675X_CH2_MUTE_BIT BIT(3) +#define TAS675X_CH_MUTE_BOTH (TAS675X_CH1_MUTE_BIT | TAS675X_CH2_MUTE_BIT) + +/* SCLK_INV_CTRL_REG */ +#define TAS675X_SCLK_INV_TX_BIT BIT(5) +#define TAS675X_SCLK_INV_RX_BIT BIT(4) +#define TAS675X_SCLK_INV_MASK (TAS675X_SCLK_INV_TX_BIT | TAS675X_SCLK_INV_RX_BIT) + +/* AUDIO_IF_CTRL_REG */ +#define TAS675X_TDM_EN_BIT BIT(4) +#define TAS675X_SAP_FMT_MASK GENMASK(3, 2) +#define TAS675X_SAP_FMT_I2S (0x00 << 2) +#define TAS675X_SAP_FMT_TDM (0x01 << 2) +#define TAS675X_SAP_FMT_RIGHT_J (0x02 << 2) +#define TAS675X_SAP_FMT_LEFT_J (0x03 << 2) +#define TAS675X_FS_PULSE_MASK GENMASK(1, 0) +#define TAS675X_FS_PULSE_SHORT 0x01 + +/* SDIN_CTRL_REG */ +#define TAS675X_SDIN_AUDIO_WL_MASK GENMASK(3, 2) +#define TAS675X_SDIN_LL_WL_MASK GENMASK(1, 0) +#define TAS675X_SDIN_WL_MASK (TAS675X_SDIN_AUDIO_WL_MASK | TAS675X_SDIN_LL_WL_MASK) + +/* SDOUT_CTRL_REG */ +#define TAS675X_SDOUT_SELECT_MASK GENMASK(7, 4) +#define TAS675X_SDOUT_SELECT_TDM_SDOUT1 0x00 +#define TAS675X_SDOUT_SELECT_NON_TDM 0x10 +#define TAS675X_SDOUT_VP_WL_MASK GENMASK(3, 2) +#define TAS675X_SDOUT_IS_WL_MASK GENMASK(1, 0) +#define TAS675X_SDOUT_WL_MASK (TAS675X_SDOUT_VP_WL_MASK | TAS675X_SDOUT_IS_WL_MASK) + +/* SDOUT_EN_REG */ +#define TAS675X_SDOUT_NON_TDM_SEL_MASK GENMASK(5, 4) +#define TAS675X_SDOUT_NON_TDM_SEL_VPREDICT (0x0 << 4) +#define TAS675X_SDOUT_NON_TDM_SEL_ISENSE (0x1 << 4) +#define TAS675X_SDOUT_EN_VPREDICT BIT(0) +#define TAS675X_SDOUT_EN_ISENSE BIT(1) +#define TAS675X_SDOUT_EN_NON_TDM_ALL GENMASK(1, 0) + +/* Word length values (shared by SDIN_CTRL and SDOUT_CTRL) */ +#define TAS675X_WL_16BIT 0x00 +#define TAS675X_WL_20BIT 0x01 +#define TAS675X_WL_24BIT 0x02 +#define TAS675X_WL_32BIT 0x03 + +/* SDIN_OFFSET_MSB_REG */ +#define TAS675X_SDIN_AUDIO_OFF_MSB_MASK GENMASK(7, 6) +#define TAS675X_SDIN_LL_OFF_MSB_MASK GENMASK(5, 4) + +/* SDOUT_OFFSET_MSB_REG */ +#define TAS675X_SDOUT_VP_OFF_MSB_MASK GENMASK(7, 6) +#define TAS675X_SDOUT_IS_OFF_MSB_MASK GENMASK(5, 4) + +/* RTLDG_EN_REG */ +#define TAS675X_RTLDG_CLIP_MASK_BIT BIT(4) +#define TAS675X_RTLDG_CH_EN_MASK GENMASK(3, 0) + +/* DC_LDG_CTRL_REG */ +#define TAS675X_LDG_ABORT_BIT BIT(7) +#define TAS675X_LDG_BUFFER_WAIT_MASK GENMASK(6, 5) +#define TAS675X_LDG_WAIT_BYPASS_BIT BIT(2) +#define TAS675X_SLOL_DISABLE_BIT BIT(1) +#define TAS675X_LDG_BYPASS_BIT BIT(0) + +/* DC_LDG_TIME_CTRL_REG */ +#define TAS675X_LDG_RAMP_SLOL_MASK GENMASK(7, 6) +#define TAS675X_LDG_SETTLING_SLOL_MASK GENMASK(5, 4) +#define TAS675X_LDG_RAMP_S2PG_MASK GENMASK(3, 2) +#define TAS675X_LDG_SETTLING_S2PG_MASK GENMASK(1, 0) + +/* AC_LDG_CTRL_REG */ +#define TAS675X_AC_DIAG_GAIN_BIT BIT(4) +#define TAS675X_AC_DIAG_START_MASK GENMASK(3, 0) + +/* DC_LDG_RESULT_REG */ +#define TAS675X_DC_LDG_LO_RESULT_MASK GENMASK(7, 4) +#define TAS675X_DC_LDG_PASS_MASK GENMASK(3, 0) + +/* Load Diagnostics Timing Constants */ +#define TAS675X_POLL_INTERVAL_US 10000 +#define TAS675X_STATE_TRANSITION_TIMEOUT_US 50000 +#define TAS675X_DC_LDG_TIMEOUT_US 300000 +#define TAS675X_AC_LDG_TIMEOUT_US 400000 + +/* GPIO_CTRL_REG */ +#define TAS675X_GPIO1_OUTPUT_EN BIT(7) +#define TAS675X_GPIO2_OUTPUT_EN BIT(6) +#define TAS675X_GPIO_CTRL_RSTVAL 0x22 + +/* GPIO output select values */ +#define TAS675X_GPIO_SEL_LOW 0x00 +#define TAS675X_GPIO_SEL_AUTO_MUTE_ALL 0x02 +#define TAS675X_GPIO_SEL_AUTO_MUTE_CH4 0x03 +#define TAS675X_GPIO_SEL_AUTO_MUTE_CH3 0x04 +#define TAS675X_GPIO_SEL_AUTO_MUTE_CH2 0x05 +#define TAS675X_GPIO_SEL_AUTO_MUTE_CH1 0x06 +#define TAS675X_GPIO_SEL_SDOUT2 0x08 +#define TAS675X_GPIO_SEL_SDOUT1 0x09 +#define TAS675X_GPIO_SEL_WARN 0x0A +#define TAS675X_GPIO_SEL_FAULT 0x0B +#define TAS675X_GPIO_SEL_CLOCK_SYNC 0x0E +#define TAS675X_GPIO_SEL_INVALID_CLK 0x0F +#define TAS675X_GPIO_SEL_HIGH 0x13 + +/* GPIO input function encoding (flag bit | function ID) */ +#define TAS675X_GPIO_FUNC_INPUT 0x100 + +/* Input Function IDs */ +#define TAS675X_GPIO_IN_ID_MUTE 0 +#define TAS675X_GPIO_IN_ID_PHASE_SYNC 1 +#define TAS675X_GPIO_IN_ID_SDIN2 2 +#define TAS675X_GPIO_IN_ID_DEEP_SLEEP 3 +#define TAS675X_GPIO_IN_ID_HIZ 4 +#define TAS675X_GPIO_IN_ID_PLAY 5 +#define TAS675X_GPIO_IN_ID_SLEEP 6 +#define TAS675X_GPIO_IN_NUM 7 + +#define TAS675X_GPIO_IN_MUTE (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_MUTE) +#define TAS675X_GPIO_IN_PHASE_SYNC \ + (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_PHASE_SYNC) +#define TAS675X_GPIO_IN_SDIN2 (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_SDIN2) +#define TAS675X_GPIO_IN_DEEP_SLEEP \ + (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_DEEP_SLEEP) +#define TAS675X_GPIO_IN_HIZ (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_HIZ) +#define TAS675X_GPIO_IN_PLAY (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_PLAY) +#define TAS675X_GPIO_IN_SLEEP (TAS675X_GPIO_FUNC_INPUT | TAS675X_GPIO_IN_ID_SLEEP) + +/* GPIO input 3-bit mux field masks */ +#define TAS675X_GPIO_IN_MUTE_MASK GENMASK(2, 0) +#define TAS675X_GPIO_IN_SYNC_MASK GENMASK(2, 0) +#define TAS675X_GPIO_IN_SDIN2_MASK GENMASK(6, 4) +#define TAS675X_GPIO_IN_DEEP_SLEEP_MASK GENMASK(6, 4) +#define TAS675X_GPIO_IN_HIZ_MASK GENMASK(2, 0) +#define TAS675X_GPIO_IN_PLAY_MASK GENMASK(6, 4) +#define TAS675X_GPIO_IN_SLEEP_MASK GENMASK(2, 0) + +/* Book addresses for tas675x_select_book() */ +#define TAS675X_BOOK_DEFAULT 0x00 +#define TAS675X_BOOK_DSP 0x8C + +/* DSP memory addresses (DSP Book) */ +#define TAS675X_DSP_PAGE_RTLDG 0x22 +#define TAS675X_DSP_RTLDG_OL_THRESH_REG 0x98 +#define TAS675X_DSP_RTLDG_SL_THRESH_REG 0x9C + +#define TAS675X_DSP_PARAM_ID_OL_THRESH 0 +#define TAS675X_DSP_PARAM_ID_SL_THRESH 1 + +/* Setup Mode Entry/Exit*/ +#define TAS675X_SETUP_ENTER_VAL1 0x11 +#define TAS675X_SETUP_ENTER_VAL2 0xFF +#define TAS675X_SETUP_EXIT_VAL 0x00 + +#endif /* __TAS675X_H__ */ diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c index 7d6fcba9986e..7ddea2fbe2d2 100644 --- a/sound/soc/codecs/tda7419.c +++ b/sound/soc/codecs/tda7419.c @@ -614,7 +614,7 @@ static int tda7419_probe(struct i2c_client *i2c) } static const struct i2c_device_id tda7419_i2c_id[] = { - { "tda7419" }, + { .name = "tda7419" }, { } }; MODULE_DEVICE_TABLE(i2c, tda7419_i2c_id); diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index ac0c5c337677..f30479f1f53b 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -296,7 +296,7 @@ static int tfa9879_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id tfa9879_i2c_id[] = { - { "tfa9879" }, + { .name = "tfa9879" }, { } }; MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id); diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 270eee1ea534..d7d958ecd8db 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1391,9 +1391,9 @@ static const struct snd_soc_component_driver soc_component_dev_adc3xxx = { }; static const struct i2c_device_id adc3xxx_i2c_id[] = { - { "tlv320adc3001", ADC3001 }, - { "tlv320adc3101", ADC3101 }, - {} + { .name = "tlv320adc3001", .driver_data = ADC3001 }, + { .name = "tlv320adc3101", .driver_data = ADC3101 }, + { } }; MODULE_DEVICE_TABLE(i2c, adc3xxx_i2c_id); diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index e4f27a734501..4eb9cea27276 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -1326,10 +1326,10 @@ static int adcx140_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id adcx140_i2c_id[] = { - { "tlv320adc3140", 0 }, - { "tlv320adc5140", 1 }, - { "tlv320adc6140", 2 }, - {} + { .name = "tlv320adc3140", .driver_data = 0 }, + { .name = "tlv320adc5140", .driver_data = 1 }, + { .name = "tlv320adc6140", .driver_data = 2 }, + { } }; MODULE_DEVICE_TABLE(i2c, adcx140_i2c_id); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index a31fb95048b8..2f928ae23887 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -28,8 +28,8 @@ static int tlv320aic23_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id tlv320aic23_id[] = { - {"tlv320aic23"}, - {} + { .name = "tlv320aic23" }, + { } }; MODULE_DEVICE_TABLE(i2c, tlv320aic23_id); diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 4362c2c06ce8..1d2e0ea6d4fe 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1630,14 +1630,14 @@ static void aic31xx_configure_ocmv(struct aic31xx_priv *priv) } static const struct i2c_device_id aic31xx_i2c_id[] = { - { "tlv320aic310x", AIC3100 }, - { "tlv320aic311x", AIC3110 }, - { "tlv320aic3100", AIC3100 }, - { "tlv320aic3110", AIC3110 }, - { "tlv320aic3120", AIC3120 }, - { "tlv320aic3111", AIC3111 }, - { "tlv320dac3100", DAC3100 }, - { "tlv320dac3101", DAC3101 }, + { .name = "tlv320aic310x", .driver_data = AIC3100 }, + { .name = "tlv320aic311x", .driver_data = AIC3110 }, + { .name = "tlv320aic3100", .driver_data = AIC3100 }, + { .name = "tlv320aic3110", .driver_data = AIC3110 }, + { .name = "tlv320aic3120", .driver_data = AIC3120 }, + { .name = "tlv320aic3111", .driver_data = AIC3111 }, + { .name = "tlv320dac3100", .driver_data = DAC3100 }, + { .name = "tlv320dac3101", .driver_data = DAC3101 }, { } }; MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id); diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index b27b5ae1e4b2..449353d5f088 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -38,9 +38,9 @@ static void aic32x4_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id aic32x4_i2c_id[] = { - { "tlv320aic32x4", (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 }, - { "tlv320aic32x6", (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 }, - { "tas2505", (kernel_ulong_t)AIC32X4_TYPE_TAS2505 }, + { .name = "tlv320aic32x4", .driver_data = (kernel_ulong_t)AIC32X4_TYPE_AIC32X4 }, + { .name = "tlv320aic32x6", .driver_data = (kernel_ulong_t)AIC32X4_TYPE_AIC32X6 }, + { .name = "tas2505", .driver_data = (kernel_ulong_t)AIC32X4_TYPE_TAS2505 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, aic32x4_i2c_id); diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c index 0b585925c1ac..71528ad3a3b6 100644 --- a/sound/soc/codecs/tlv320aic3x-i2c.c +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -18,11 +18,11 @@ #include "tlv320aic3x.h" static const struct i2c_device_id aic3x_i2c_id[] = { - { "tlv320aic3x", AIC3X_MODEL_3X }, - { "tlv320aic33", AIC3X_MODEL_33 }, - { "tlv320aic3007", AIC3X_MODEL_3007 }, - { "tlv320aic3104", AIC3X_MODEL_3104 }, - { "tlv320aic3106", AIC3X_MODEL_3106 }, + { .name = "tlv320aic3x", .driver_data = AIC3X_MODEL_3X }, + { .name = "tlv320aic33", .driver_data = AIC3X_MODEL_33 }, + { .name = "tlv320aic3007", .driver_data = AIC3X_MODEL_3007 }, + { .name = "tlv320aic3104", .driver_data = AIC3X_MODEL_3104 }, + { .name = "tlv320aic3106", .driver_data = AIC3X_MODEL_3106 }, { } }; MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id); diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c index ce22298b43ef..b38393a8130f 100644 --- a/sound/soc/codecs/tlv320aic3x.c +++ b/sound/soc/codecs/tlv320aic3x.c @@ -1049,11 +1049,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) { + static const u8 dual_rate_q[] = {4, 8, 9, 12, 16}; struct snd_soc_component *component = dai->component; struct aic3x_priv *aic3x = snd_soc_component_get_drvdata(component); int codec_clk = 0, bypass_pll = 0, fsref, last_clk = 0; u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1; u16 d, pll_d = 1; + bool dual_rate; int clk; int width = aic3x->slot_width; @@ -1079,14 +1081,25 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, /* Fsref can be 44100 or 48000 */ fsref = (params_rate(params) % 11025 == 0) ? 44100 : 48000; + dual_rate = params_rate(params) >= 64000; /* Try to find a value for Q which allows us to bypass the PLL and * generate CODEC_CLK directly. */ - for (pll_q = 2; pll_q < 18; pll_q++) - if (aic3x->sysclk / (128 * pll_q) == fsref) { - bypass_pll = 1; - break; + if (dual_rate) { + for (int i = 0; i < ARRAY_SIZE(dual_rate_q); i++) { + pll_q = dual_rate_q[i]; + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } } + } else { + for (pll_q = 2; pll_q < 18; pll_q++) + if (aic3x->sysclk / (128 * pll_q) == fsref) { + bypass_pll = 1; + break; + } + } if (bypass_pll) { pll_q &= 0xf; @@ -1106,13 +1119,13 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream, * right DAC to right channel input */ data = (LDAC2LCH | RDAC2RCH); data |= (fsref == 44100) ? FSREF_44100 : FSREF_48000; - if (params_rate(params) >= 64000) + if (dual_rate) data |= DUAL_RATE_MODE; snd_soc_component_write(component, AIC3X_CODEC_DATAPATH_REG, data); /* codec sample rate select */ data = (fsref * 20) / params_rate(params); - if (params_rate(params) < 64000) + if (!dual_rate) data /= 2; data /= 5; data -= 2; @@ -1689,8 +1702,15 @@ static int aic3x_component_probe(struct snd_soc_component *component) return 0; } +static int aic3x_of_xlate_dai_id(struct snd_soc_component *component, + struct device_node *endpoint) +{ + return 0; +} + static const struct snd_soc_component_driver soc_component_dev_aic3x = { .set_bias_level = aic3x_set_bias_level, + .of_xlate_dai_id = aic3x_of_xlate_dai_id, .probe = aic3x_component_probe, .controls = aic3x_snd_controls, .num_controls = ARRAY_SIZE(aic3x_snd_controls), diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 5a7beeadb009..39284e4d420e 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -423,7 +423,7 @@ static const struct dev_pm_ops ts3a227e_pm = { }; static const struct i2c_device_id ts3a227e_i2c_ids[] = { - { "ts3a227e" }, + { .name = "ts3a227e" }, { } }; MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids); diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index 7390ab250ebb..dba581857920 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -1483,8 +1483,8 @@ static int tscs42xx_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id tscs42xx_i2c_id[] = { - { "tscs42A1" }, - { "tscs42A2" }, + { .name = "tscs42A1" }, + { .name = "tscs42A2" }, { } }; MODULE_DEVICE_TABLE(i2c, tscs42xx_i2c_id); diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index 64d0da40fbaf..aad394937ce6 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -3454,7 +3454,7 @@ static int tscs454_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id tscs454_i2c_id[] = { - { "tscs454" }, + { .name = "tscs454" }, { } }; MODULE_DEVICE_TABLE(i2c, tscs454_i2c_id); diff --git a/sound/soc/codecs/uda1334.c b/sound/soc/codecs/uda1334.c index f799772ff747..54c5cb5b3d4b 100644 --- a/sound/soc/codecs/uda1334.c +++ b/sound/soc/codecs/uda1334.c @@ -4,7 +4,6 @@ // // Based on WM8523 ALSA SoC Audio driver written by Mark Brown -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> diff --git a/sound/soc/codecs/uda1342.c b/sound/soc/codecs/uda1342.c index b0b29012842d..12f5757f4210 100644 --- a/sound/soc/codecs/uda1342.c +++ b/sound/soc/codecs/uda1342.c @@ -319,7 +319,7 @@ static DEFINE_RUNTIME_DEV_PM_OPS(uda1342_pm_ops, uda1342_suspend, uda1342_resume, NULL); static const struct i2c_device_id uda1342_i2c_id[] = { - { "uda1342" }, + { .name = "uda1342" }, { } }; MODULE_DEVICE_TABLE(i2c, uda1342_i2c_id); diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index 55c83d95bfba..1dd09d10fc4a 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -38,9 +38,9 @@ struct uda1380_priv { unsigned int dac_clk; struct work_struct work; struct i2c_client *i2c; - u16 *reg_cache; struct gpio_desc *power; struct gpio_desc *reset; + u16 reg_cache[]; }; /* @@ -767,11 +767,15 @@ static int uda1380_i2c_probe(struct i2c_client *i2c) struct uda1380_priv *uda1380; int ret; - uda1380 = devm_kzalloc(&i2c->dev, sizeof(struct uda1380_priv), + uda1380 = devm_kzalloc(&i2c->dev, + struct_size(uda1380, reg_cache, ARRAY_SIZE(uda1380_reg)), GFP_KERNEL); if (uda1380 == NULL) return -ENOMEM; + memcpy(uda1380->reg_cache, uda1380_reg, + ARRAY_SIZE(uda1380_reg) * sizeof(*uda1380->reg_cache)); + uda1380->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(uda1380->reset)) return dev_err_probe(dev, PTR_ERR(uda1380->reset), @@ -789,11 +793,6 @@ static int uda1380_i2c_probe(struct i2c_client *i2c) if (device_property_match_string(dev, "dac-clk", "wspll") >= 0) uda1380->dac_clk = UDA1380_DAC_CLK_WSPLL; - uda1380->reg_cache = devm_kmemdup_array(&i2c->dev, uda1380_reg, ARRAY_SIZE(uda1380_reg), - sizeof(uda1380_reg[0]), GFP_KERNEL); - if (!uda1380->reg_cache) - return -ENOMEM; - i2c_set_clientdata(i2c, uda1380); uda1380->i2c = i2c; @@ -803,7 +802,7 @@ static int uda1380_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id uda1380_i2c_id[] = { - { "uda1380" }, + { .name = "uda1380" }, { } }; MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id); diff --git a/sound/soc/codecs/wcd937x.c b/sound/soc/codecs/wcd937x.c index 72a53f95d688..e0169e783ee9 100644 --- a/sound/soc/codecs/wcd937x.c +++ b/sound/soc/codecs/wcd937x.c @@ -2499,18 +2499,13 @@ static int wcd937x_soc_codec_probe(struct snd_soc_component *component) { struct snd_soc_dapm_context *dapm = snd_soc_component_to_dapm(component); struct wcd937x_priv *wcd937x = snd_soc_component_get_drvdata(component); - struct sdw_slave *tx_sdw_dev = wcd937x->tx_sdw_dev; struct device *dev = component->dev; - unsigned long time_left; int i, ret; u32 chipid; - time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, - msecs_to_jiffies(5000)); - if (!time_left) { - dev_err(dev, "soundwire device init timeout\n"); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(wcd937x->tx_sdw_dev, 5000); + if (ret) + return ret; snd_soc_component_init_regmap(component, wcd937x->regmap); ret = pm_runtime_resume_and_get(dev); diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c index cb0a0bfdb6e3..c69e18667a85 100644 --- a/sound/soc/codecs/wcd938x.c +++ b/sound/soc/codecs/wcd938x.c @@ -3016,18 +3016,13 @@ static int wcd938x_irq_init(struct wcd938x_priv *wcd, struct device *dev) static int wcd938x_soc_codec_probe(struct snd_soc_component *component) { struct wcd938x_priv *wcd938x = snd_soc_component_get_drvdata(component); - struct sdw_slave *tx_sdw_dev = wcd938x->tx_sdw_dev; struct device *dev = component->dev; - unsigned long time_left; unsigned int variant; int ret, i; - time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, - msecs_to_jiffies(2000)); - if (!time_left) { - dev_err(dev, "soundwire device init timeout\n"); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(wcd938x->tx_sdw_dev, 2000); + if (ret) + return ret; snd_soc_component_init_regmap(component, wcd938x->regmap); diff --git a/sound/soc/codecs/wcd939x.c b/sound/soc/codecs/wcd939x.c index 01f1a08f48e6..010d12466722 100644 --- a/sound/soc/codecs/wcd939x.c +++ b/sound/soc/codecs/wcd939x.c @@ -2968,17 +2968,12 @@ static int wcd939x_irq_init(struct wcd939x_priv *wcd, struct device *dev) static int wcd939x_soc_codec_probe(struct snd_soc_component *component) { struct wcd939x_priv *wcd939x = snd_soc_component_get_drvdata(component); - struct sdw_slave *tx_sdw_dev = wcd939x->tx_sdw_dev; struct device *dev = component->dev; - unsigned long time_left; int ret, i; - time_left = wait_for_completion_timeout(&tx_sdw_dev->initialization_complete, - msecs_to_jiffies(2000)); - if (!time_left) { - dev_err(dev, "soundwire device init timeout\n"); - return -ETIMEDOUT; - } + ret = sdw_slave_wait_for_init(wcd939x->tx_sdw_dev, 2000); + if (ret) + return ret; snd_soc_component_init_regmap(component, wcd939x->regmap); diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index 1f59309d8c69..5c1b00acb5bf 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -204,7 +204,7 @@ static int wm1250_ev1_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm1250_ev1_i2c_id[] = { - { "wm1250-ev1" }, + { .name = "wm1250-ev1" }, { } }; MODULE_DEVICE_TABLE(i2c, wm1250_ev1_i2c_id); diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 126be2a2a8f3..9b68ee69324b 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -929,7 +929,7 @@ out: } static const struct i2c_device_id wm2000_i2c_id[] = { - { "wm2000" }, + { .name = "wm2000" }, { } }; MODULE_DEVICE_TABLE(i2c, wm2000_i2c_id); diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 87418c838ca0..ba8ce2e6e615 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2471,7 +2471,7 @@ static const struct dev_pm_ops wm2200_pm = { }; static const struct i2c_device_id wm2200_i2c_id[] = { - { "wm2200" }, + { .name = "wm2200" }, { } }; MODULE_DEVICE_TABLE(i2c, wm2200_i2c_id); diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 96fd098a9d36..bd94fa53c362 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2669,7 +2669,7 @@ static const struct dev_pm_ops wm5100_pm = { }; static const struct i2c_device_id wm5100_i2c_id[] = { - { "wm5100" }, + { .name = "wm5100" }, { } }; MODULE_DEVICE_TABLE(i2c, wm5100_i2c_id); diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index bebee333d3fd..137dcd3d7487 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -7,7 +7,6 @@ * Author: Liam Girdwood <lrg@slimlogic.co.uk> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -669,7 +668,7 @@ static int wm8510_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8510_i2c_id[] = { - { "wm8510" }, + { .name = "wm8510" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8510_i2c_id); diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index f003f19766e2..b8832a1d61fe 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -7,7 +7,6 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -518,7 +517,7 @@ err_enable: } static const struct i2c_device_id wm8523_i2c_id[] = { - { "wm8523" }, + { .name = "wm8523" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8523_i2c_id); diff --git a/sound/soc/codecs/wm8524.c b/sound/soc/codecs/wm8524.c index 6b1a7450b0ac..23daf158f35f 100644 --- a/sound/soc/codecs/wm8524.c +++ b/sound/soc/codecs/wm8524.c @@ -8,7 +8,6 @@ * Based on WM8523 ALSA SoC Audio driver written by Mark Brown */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index 2be265bb0751..eb374ba6e5b5 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -15,7 +15,6 @@ * the secondary audio interfaces are not. */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/kernel.h> @@ -1034,8 +1033,8 @@ static const struct of_device_id wm8580_of_match[] = { MODULE_DEVICE_TABLE(of, wm8580_of_match); static const struct i2c_device_id wm8580_i2c_id[] = { - { "wm8580", (kernel_ulong_t)&wm8580_data }, - { "wm8581", (kernel_ulong_t)&wm8581_data }, + { .name = "wm8580", .driver_data = (kernel_ulong_t)&wm8580_data }, + { .name = "wm8581", .driver_data = (kernel_ulong_t)&wm8581_data }, { } }; MODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 2bab9d189519..2db8829661c3 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -9,7 +9,6 @@ * Based on wm8731.c by Richard Purdie */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -455,7 +454,7 @@ static int wm8711_i2c_probe(struct i2c_client *client) } static const struct i2c_device_id wm8711_i2c_id[] = { - { "wm8711" }, + { .name = "wm8711" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8711_i2c_id); diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index 4c1a80561f06..3109a6a0df74 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -7,7 +7,6 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -296,7 +295,7 @@ static int wm8728_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8728_i2c_id[] = { - { "wm8728" }, + { .name = "wm8728" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8728_i2c_id); diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c index 1254e583af51..f44f4d3d9394 100644 --- a/sound/soc/codecs/wm8731-i2c.c +++ b/sound/soc/codecs/wm8731-i2c.c @@ -11,7 +11,6 @@ */ #include <linux/i2c.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include "wm8731.h" @@ -47,7 +46,7 @@ static int wm8731_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8731_i2c_id[] = { - { "wm8731" }, + { .name = "wm8731" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8731_i2c_id); diff --git a/sound/soc/codecs/wm8731-spi.c b/sound/soc/codecs/wm8731-spi.c index c02086afa7fb..29e58e1e6b79 100644 --- a/sound/soc/codecs/wm8731-spi.c +++ b/sound/soc/codecs/wm8731-spi.c @@ -11,7 +11,6 @@ */ #include <linux/spi/spi.h> -#include <linux/mod_devicetable.h> #include <linux/module.h> #include "wm8731.h" diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index fee8a37ed1df..33a3f88fffb3 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -7,7 +7,6 @@ * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -641,7 +640,7 @@ static int wm8737_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8737_i2c_id[] = { - { "wm8737" }, + { .name = "wm8737" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8737_i2c_id); diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index 4dfbb33edb09..ca56fdfb5088 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -606,7 +606,7 @@ static int wm8741_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8741_i2c_id[] = { - { "wm8741" }, + { .name = "wm8741" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8741_i2c_id); diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 0e1d3ebb15c4..d3f1178c2d34 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -803,8 +803,8 @@ static int wm8750_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8750_i2c_id[] = { - { "wm8750" }, - { "wm8987" }, + { .name = "wm8750" }, + { .name = "wm8987" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8750_i2c_id); diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index a532a95e8048..ac4008b4832d 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -26,7 +26,6 @@ * an alsa kcontrol. This allows the PCM to remain open. */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> @@ -1581,7 +1580,7 @@ static int wm8753_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8753_i2c_id[] = { - { "wm8753" }, + { .name = "wm8753" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8753_i2c_id); diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c index d382b476c89c..b8b4d1e823e6 100644 --- a/sound/soc/codecs/wm8770.c +++ b/sound/soc/codecs/wm8770.c @@ -7,7 +7,6 @@ * Author: Dimitris Papastamos <dp@opensource.wolfsonmicro.com> */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index c3f340657f0c..a8e4f71c77d1 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -9,7 +9,6 @@ * TODO: Input ALC/limiter support */ -#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> @@ -513,8 +512,8 @@ static int wm8776_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8776_i2c_id[] = { - { "wm8775", WM8775 }, - { "wm8776", WM8776 }, + { .name = "wm8775", .driver_data = WM8775 }, + { .name = "wm8776", .driver_data = WM8776 }, { } }; MODULE_DEVICE_TABLE(i2c, wm8776_i2c_id); diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index 3380d7301b17..3c49abd33d28 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -31,7 +31,7 @@ static void wm8804_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id wm8804_i2c_id[] = { - { "wm8804" }, + { .name = "wm8804" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8804_i2c_id); diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index fea629541acd..a9128cfa6ff1 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1286,11 +1286,8 @@ static int wm8900_i2c_probe(struct i2c_client *i2c) return ret; } -static void wm8900_i2c_remove(struct i2c_client *client) -{} - static const struct i2c_device_id wm8900_i2c_id[] = { - { "wm8900" }, + { .name = "wm8900" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8900_i2c_id); @@ -1299,8 +1296,7 @@ static struct i2c_driver wm8900_i2c_driver = { .driver = { .name = "wm8900", }, - .probe = wm8900_i2c_probe, - .remove = wm8900_i2c_remove, + .probe = wm8900_i2c_probe, .id_table = wm8900_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index f73f6ad06b38..320d7737699d 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2203,7 +2203,7 @@ static const struct of_device_id wm8903_of_match[] = { MODULE_DEVICE_TABLE(of, wm8903_of_match); static const struct i2c_device_id wm8903_i2c_id[] = { - { "wm8903" }, + { .name = "wm8903" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8903_i2c_id); diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 4c73a340f25f..9e5782e50f47 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2621,9 +2621,9 @@ err_enable: } static const struct i2c_device_id wm8904_i2c_id[] = { - { "wm8904", WM8904 }, - { "wm8912", WM8912 }, - { "wm8918", WM8904 }, /* Actually a subset, updates to follow */ + { .name = "wm8904", .driver_data = WM8904 }, + { .name = "wm8912", .driver_data = WM8912 }, + { .name = "wm8918", .driver_data = WM8904 }, /* Actually a subset, updates to follow */ { } }; MODULE_DEVICE_TABLE(i2c, wm8904_i2c_id); diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 2f55d0c572a4..e631ec072249 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -851,7 +851,7 @@ static int wm8940_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8940_i2c_id[] = { - { "wm8940" }, + { .name = "wm8940" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8940_i2c_id); diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index e1c61e026cbc..c897a9ab764d 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -996,7 +996,7 @@ static int wm8955_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8955_i2c_id[] = { - { "wm8955" }, + { .name = "wm8955" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8955_i2c_id); diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 384e8e703446..a810732c6af0 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -1551,7 +1551,7 @@ static void wm8960_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id wm8960_i2c_id[] = { - { "wm8960" }, + { .name = "wm8960" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index cfb8cfc91873..504fc59e4be2 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -967,7 +967,7 @@ static int wm8961_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8961_i2c_id[] = { - { "wm8961" }, + { .name = "wm8961" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8961_i2c_id); diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index 8d2435bf44ea..de18b1f85a32 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3991,7 +3991,7 @@ static const struct dev_pm_ops wm8962_pm = { }; static const struct i2c_device_id wm8962_i2c_id[] = { - { "wm8962" }, + { .name = "wm8962" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8962_i2c_id); diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 46aa556b44fa..a5d2e91b66bb 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -692,7 +692,7 @@ static int wm8971_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8971_i2c_id[] = { - { "wm8971" }, + { .name = "wm8971" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8971_i2c_id); diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 0bb5e947f46d..4656652b8e97 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -712,7 +712,7 @@ static int wm8974_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8974_i2c_id[] = { - { "wm8974" }, + { .name = "wm8974" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8974_i2c_id); diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index 935761e50865..ad8064bbaaac 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -1059,7 +1059,7 @@ static int wm8978_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8978_i2c_id[] = { - { "wm8978" }, + { .name = "wm8978" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id); diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index cd34f71cf42a..fab25f35fd14 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -1060,7 +1060,7 @@ static int wm8983_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8983_i2c_id[] = { - { "wm8983" }, + { .name = "wm8983" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8983_i2c_id); diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index be23c0c608d1..2a64d5a851da 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1195,8 +1195,8 @@ static int wm8985_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8985_i2c_id[] = { - { "wm8985", WM8985 }, - { "wm8758", WM8758 }, + { .name = "wm8985", .driver_data = WM8985 }, + { .name = "wm8758", .driver_data = WM8758 }, { } }; MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id); diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 9bffe7a6ccec..741aecc7641a 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -897,7 +897,7 @@ static int wm8988_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8988_i2c_id[] = { - { "wm8988" }, + { .name = "wm8988" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8988_i2c_id); diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 9f2b42025ec9..11dfbbbdcd92 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1241,7 +1241,7 @@ static int wm8990_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8990_i2c_id[] = { - { "wm8990" }, + { .name = "wm8990" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8990_i2c_id); diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index b8ed2a3e699b..225c235d3d89 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1315,7 +1315,7 @@ static int wm8991_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8991_i2c_id[] = { - { "wm8991" }, + { .name = "wm8991" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8991_i2c_id); diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 1c9299979898..2a94b23319b5 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1735,7 +1735,7 @@ static void wm8993_i2c_remove(struct i2c_client *i2c) } static const struct i2c_device_id wm8993_i2c_id[] = { - { "wm8993" }, + { .name = "wm8993" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8993_i2c_id); diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index 104ce09c02e0..799989a5bf0f 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2259,8 +2259,8 @@ static int wm8995_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm8995_i2c_id[] = { - {"wm8995"}, - {} + { .name = "wm8995" }, + { } }; MODULE_DEVICE_TABLE(i2c, wm8995_i2c_id); diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index 2d9cbf66f7d4..dface555f928 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -3072,7 +3072,7 @@ static void wm8996_i2c_remove(struct i2c_client *client) } static const struct i2c_device_id wm8996_i2c_id[] = { - { "wm8996" }, + { .name = "wm8996" }, { } }; MODULE_DEVICE_TABLE(i2c, wm8996_i2c_id); diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 5bfe43c6c1f4..2e4e0a76499d 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1357,11 +1357,8 @@ static int wm9081_i2c_probe(struct i2c_client *i2c) return 0; } -static void wm9081_i2c_remove(struct i2c_client *client) -{} - static const struct i2c_device_id wm9081_i2c_id[] = { - { "wm9081" }, + { .name = "wm9081" }, { } }; MODULE_DEVICE_TABLE(i2c, wm9081_i2c_id); @@ -1370,8 +1367,7 @@ static struct i2c_driver wm9081_i2c_driver = { .driver = { .name = "wm9081", }, - .probe = wm9081_i2c_probe, - .remove = wm9081_i2c_remove, + .probe = wm9081_i2c_probe, .id_table = wm9081_i2c_id, }; diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index 5182f0839b7c..8fc24b19313d 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -607,8 +607,8 @@ static int wm9090_i2c_probe(struct i2c_client *i2c) } static const struct i2c_device_id wm9090_id[] = { - { "wm9090" }, - { "wm9093" }, + { .name = "wm9090" }, + { .name = "wm9093" }, { } }; MODULE_DEVICE_TABLE(i2c, wm9090_id); diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index a637e22c3929..baa75e7ff53b 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -679,6 +679,9 @@ static void wm_adsp_control_remove(struct cs_dsp_coeff_ctl *cs_ctl) { struct wm_coeff_ctl *ctl = cs_ctl->priv; + if (!ctl) + return; + cancel_work_sync(&ctl->work); kfree(ctl->name); @@ -1167,7 +1170,14 @@ EXPORT_SYMBOL_GPL(wm_adsp2_component_probe); int wm_adsp2_component_remove(struct wm_adsp *dsp, struct snd_soc_component *component) { + if (!dsp) + return 0; + + if (!dsp->component) + return 0; + cs_dsp_cleanup_debugfs(&dsp->cs_dsp); + dsp->component = NULL; return 0; } diff --git a/sound/soc/codecs/wsa881x.c b/sound/soc/codecs/wsa881x.c index 2fc234adca5f..5174614c3e83 100644 --- a/sound/soc/codecs/wsa881x.c +++ b/sound/soc/codecs/wsa881x.c @@ -3,7 +3,6 @@ // Copyright (c) 2019, Linaro Limited #include <linux/bitops.h> -#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/regmap.h> @@ -672,11 +671,6 @@ struct wsa881x_priv { struct sdw_stream_runtime *sruntime; struct sdw_port_config port_config[WSA881X_MAX_SWR_PORTS]; struct gpio_desc *sd_n; - /* - * Logical state for SD_N GPIO: high for shutdown, low for enable. - * For backwards compatibility. - */ - unsigned int sd_n_val; int active_ports; bool hw_init; bool port_prepared[WSA881X_MAX_SWR_PORTS]; @@ -1121,31 +1115,11 @@ static int wsa881x_probe(struct sdw_slave *pdev, if (!wsa881x) return -ENOMEM; - wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", 0); + wsa881x->sd_n = devm_gpiod_get_optional(dev, "powerdown", GPIOD_OUT_LOW); if (IS_ERR(wsa881x->sd_n)) return dev_err_probe(dev, PTR_ERR(wsa881x->sd_n), "Shutdown Control GPIO not found\n"); - /* - * Backwards compatibility work-around. - * - * The SD_N GPIO is active low, however upstream DTS used always active - * high. Changing the flag in driver and DTS will break backwards - * compatibility, so add a simple value inversion to work with both old - * and new DTS. - * - * This won't work properly with DTS using the flags properly in cases: - * 1. Old DTS with proper ACTIVE_LOW, however such case was broken - * before as the driver required the active high. - * 2. New DTS with proper ACTIVE_HIGH (intended), which is rare case - * (not existing upstream) but possible. This is the price of - * backwards compatibility, therefore this hack should be removed at - * some point. - */ - wsa881x->sd_n_val = gpiod_is_active_low(wsa881x->sd_n); - if (!wsa881x->sd_n_val) - dev_warn(dev, "Using ACTIVE_HIGH for shutdown GPIO. Your DTB might be outdated or you use unsupported configuration for the GPIO."); - dev_set_drvdata(dev, wsa881x); wsa881x->slave = pdev; wsa881x->dev = dev; @@ -1158,7 +1132,6 @@ static int wsa881x_probe(struct sdw_slave *pdev, pdev->prop.sink_dpn_prop = wsa_sink_dpn_prop; pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; pdev->prop.clk_stop_mode1 = true; - gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); wsa881x->regmap = devm_regmap_init_sdw(pdev, &wsa881x_regmap_config); if (IS_ERR(wsa881x->regmap)) @@ -1181,7 +1154,7 @@ static int wsa881x_runtime_suspend(struct device *dev) struct regmap *regmap = dev_get_regmap(dev, NULL); struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); - gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); + gpiod_direction_output(wsa881x->sd_n, 1); regcache_cache_only(regmap, true); regcache_mark_dirty(regmap); @@ -1194,16 +1167,14 @@ static int wsa881x_runtime_resume(struct device *dev) struct sdw_slave *slave = dev_to_sdw_dev(dev); struct regmap *regmap = dev_get_regmap(dev, NULL); struct wsa881x_priv *wsa881x = dev_get_drvdata(dev); - unsigned long time; + int ret; - gpiod_direction_output(wsa881x->sd_n, !wsa881x->sd_n_val); + gpiod_direction_output(wsa881x->sd_n, 0); - time = wait_for_completion_timeout(&slave->initialization_complete, - msecs_to_jiffies(WSA881X_PROBE_TIMEOUT)); - if (!time) { - dev_err(dev, "Initialization not complete, timed out\n"); - gpiod_direction_output(wsa881x->sd_n, wsa881x->sd_n_val); - return -ETIMEDOUT; + ret = sdw_slave_wait_for_init(slave, WSA881X_PROBE_TIMEOUT); + if (ret) { + gpiod_direction_output(wsa881x->sd_n, 1); + return ret; } regcache_cache_only(regmap, false); |
