diff options
Diffstat (limited to 'sound/soc/codecs')
241 files changed, 10605 insertions, 478 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig index 8020097d4e4c..0cd107fa112f 100644 --- a/sound/soc/codecs/Kconfig +++ b/sound/soc/codecs/Kconfig @@ -57,6 +57,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_AW88395 imply SND_SOC_BT_SCO imply SND_SOC_BD28623 + imply SND_SOC_CHV3_CODEC imply SND_SOC_CQ0093VC imply SND_SOC_CROS_EC_CODEC imply SND_SOC_CS35L32 @@ -136,6 +137,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_MAX98363 imply SND_SOC_MAX98373_I2C imply SND_SOC_MAX98373_SDW + imply SND_SOC_MAX98388 imply SND_SOC_MAX98390 imply SND_SOC_MAX98396 imply SND_SOC_MAX9850 @@ -207,6 +209,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_RT712_SDCA_DMIC_SDW imply SND_SOC_RT715_SDW imply SND_SOC_RT715_SDCA_SDW + imply SND_SOC_RT722_SDCA_SDW imply SND_SOC_RT1308_SDW imply SND_SOC_RT1316_SDW imply SND_SOC_RT1318_SDW @@ -234,6 +237,9 @@ config SND_SOC_ALL_CODECS imply SND_SOC_TAS2764 imply SND_SOC_TAS2770 imply SND_SOC_TAS2780 + imply SND_SOC_TAS2781_COMLIB + imply SND_SOC_TAS2781_FMWLIB + imply SND_SOC_TAS2781_I2C imply SND_SOC_TAS5086 imply SND_SOC_TAS571X imply SND_SOC_TAS5720 @@ -324,6 +330,7 @@ config SND_SOC_ALL_CODECS imply SND_SOC_WM9713 imply SND_SOC_WSA881X imply SND_SOC_WSA883X + imply SND_SOC_WSA884X imply SND_SOC_ZL38060 help Normally ASoC codec drivers are only built if a machine driver which @@ -643,6 +650,13 @@ config SND_SOC_BD28623 config SND_SOC_BT_SCO tristate "Dummy BT SCO codec driver" +config SND_SOC_CHV3_CODEC + tristate "Google Chameleon v3 codec driver" + help + Enable support for the Google Chameleon v3 audio codec. + This codec does not have a control interface, it always outputs + 8 channel S32_LE audio. + config SND_SOC_CPCAP tristate "Motorola CPCAP codec" depends on MFD_CPCAP || COMPILE_TEST @@ -1166,6 +1180,15 @@ config SND_SOC_MAX98373_SDW interface for control data. Select this if MAX98373 is connected via soundwire. +config SND_SOC_MAX98388 + tristate "Analog Devices MAX98388 Speaker Amplifier" + depends on I2C + help + Enable support for Analog Devices MAX98388 audio + amplifier. The device provides a PCM interface for + audio data and a standard I2C interface for control + data communication. + config SND_SOC_MAX98390 tristate "Maxim Integrated MAX98390 Speaker Amplifier" depends on I2C @@ -1539,6 +1562,12 @@ config SND_SOC_RT712_SDCA_DMIC_SDW select REGMAP_SOUNDWIRE select REGMAP_SOUNDWIRE_MBQ +config SND_SOC_RT722_SDCA_SDW + tristate "Realtek RT722 SDCA Codec - SDW" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + select REGMAP_SOUNDWIRE_MBQ + config SND_SOC_RT715 tristate @@ -1652,6 +1681,12 @@ config SND_SOC_SSM2602_I2C select SND_SOC_SSM2602 select REGMAP_I2C +config SND_SOC_SSM3515 + tristate "Analog Devices SSM3515 amplifier driver" + select REGMAP_I2C + depends on I2C + depends on OF + config SND_SOC_SSM4567 tristate "Analog Devices ssm4567 amplifier driver support" depends on I2C @@ -1699,6 +1734,29 @@ config SND_SOC_TAS2780 Enable support for Texas Instruments TAS2780 high-efficiency digital input mono Class-D audio power amplifiers. +config SND_SOC_TAS2781_COMLIB + depends on I2C + select CRC8 + select REGMAP_I2C + tristate + +config SND_SOC_TAS2781_FMWLIB + depends on SND_SOC_TAS2781_COMLIB + tristate + default n + +config SND_SOC_TAS2781_I2C + tristate "Texas Instruments TAS2781 speaker amplifier based on I2C" + depends on I2C + select SND_SOC_TAS2781_COMLIB + select SND_SOC_TAS2781_FMWLIB + help + Enable support for Texas Instruments TAS2781 Smart Amplifier + Digital input mono Class-D and DSP-inside audio power amplifiers. + Note the TAS2781 driver implements a flexible and configurable + algo coefficient setting, for one, two or even multiple TAS2781 + chips. + config SND_SOC_TAS5086 tristate "Texas Instruments TAS5086 speaker amplifier" depends on I2C @@ -2162,6 +2220,15 @@ config SND_SOC_WSA883X This enables support for Qualcomm WSA8830/WSA8835 Class-D Smart Speaker Amplifier. +config SND_SOC_WSA884X + tristate "WSA884X Codec" + depends on SOUNDWIRE + select REGMAP_SOUNDWIRE + tristate + help + This enables support for Qualcomm WSA8840/WSA8845/WSA8845H Class-D + Smart Speaker Amplifier. + config SND_SOC_ZL38060 tristate "Microsemi ZL38060 Connected Home Audio Processor" depends on SPI_MASTER diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile index 5cdbae88e6e3..b48a9a323b84 100644 --- a/sound/soc/codecs/Makefile +++ b/sound/soc/codecs/Makefile @@ -51,6 +51,7 @@ snd-soc-aw88395-objs := aw88395/aw88395.o \ aw88395/aw88395_device.o snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o +snd-soc-chv3-codec-objs := chv3-codec.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o snd-soc-cros-ec-codec-objs := cros_ec_codec.o @@ -152,6 +153,7 @@ snd-soc-max98363-objs := max98363.o snd-soc-max98373-objs := max98373.o snd-soc-max98373-i2c-objs := max98373-i2c.o snd-soc-max98373-sdw-objs := max98373-sdw.o +snd-soc-max98388-objs := max98388.o snd-soc-max98390-objs := max98390.o snd-soc-max98396-objs := max98396.o snd-soc-max9850-objs := max9850.o @@ -237,6 +239,7 @@ snd-soc-rt712-sdca-objs := rt712-sdca.o rt712-sdca-sdw.o snd-soc-rt712-sdca-dmic-objs := rt712-sdca-dmic.o snd-soc-rt715-objs := rt715.o rt715-sdw.o snd-soc-rt715-sdca-objs := rt715-sdca.o rt715-sdca-sdw.o +snd-soc-rt722-sdca-objs := rt722-sdca.o rt722-sdca-sdw.o snd-soc-rt9120-objs := rt9120.o snd-soc-sdw-mockup-objs := sdw-mockup.o snd-soc-sgtl5000-objs := sgtl5000.o @@ -256,6 +259,7 @@ snd-soc-ssm2518-objs := ssm2518.o snd-soc-ssm2602-objs := ssm2602.o snd-soc-ssm2602-spi-objs := ssm2602-spi.o snd-soc-ssm2602-i2c-objs := ssm2602-i2c.o +snd-soc-ssm3515-objs := ssm3515.o snd-soc-ssm4567-objs := ssm4567.o snd-soc-sta32x-objs := sta32x.o snd-soc-sta350-objs := sta350.o @@ -269,6 +273,9 @@ snd-soc-tas5805m-objs := tas5805m.o snd-soc-tas6424-objs := tas6424.o snd-soc-tda7419-objs := tda7419.o snd-soc-tas2770-objs := tas2770.o +snd-soc-tas2781-comlib-objs := tas2781-comlib.o +snd-soc-tas2781-fmwlib-objs := tas2781-fmwlib.o +snd-soc-tas2781-i2c-objs := tas2781-i2c.o snd-soc-tfa9879-objs := tfa9879.o snd-soc-tfa989x-objs := tfa989x.o snd-soc-tlv320adc3xxx-objs := tlv320adc3xxx.o @@ -359,6 +366,7 @@ snd-soc-wm9713-objs := wm9713.o snd-soc-wm-hubs-objs := wm_hubs.o snd-soc-wsa881x-objs := wsa881x.o snd-soc-wsa883x-objs := wsa883x.o +snd-soc-wsa884x-objs := wsa884x.o snd-soc-zl38060-objs := zl38060.o # Amp snd-soc-max9877-objs := max9877.o @@ -425,6 +433,7 @@ obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o +obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o @@ -521,6 +530,7 @@ obj-$(CONFIG_SND_SOC_MAX98363) += snd-soc-max98363.o obj-$(CONFIG_SND_SOC_MAX98373) += snd-soc-max98373.o obj-$(CONFIG_SND_SOC_MAX98373_I2C) += snd-soc-max98373-i2c.o obj-$(CONFIG_SND_SOC_MAX98373_SDW) += snd-soc-max98373-sdw.o +obj-$(CONFIG_SND_SOC_MAX98388) += snd-soc-max98388.o obj-$(CONFIG_SND_SOC_MAX98390) += snd-soc-max98390.o obj-$(CONFIG_SND_SOC_MAX98396) += snd-soc-max98396.o obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o @@ -607,6 +617,7 @@ obj-$(CONFIG_SND_SOC_RT712_SDCA_SDW) += snd-soc-rt712-sdca.o obj-$(CONFIG_SND_SOC_RT712_SDCA_DMIC_SDW) += snd-soc-rt712-sdca-dmic.o obj-$(CONFIG_SND_SOC_RT715) += snd-soc-rt715.o obj-$(CONFIG_SND_SOC_RT715_SDCA_SDW) += snd-soc-rt715-sdca.o +obj-$(CONFIG_SND_SOC_RT722_SDCA_SDW) += snd-soc-rt722-sdca.o obj-$(CONFIG_SND_SOC_RT9120) += snd-soc-rt9120.o obj-$(CONFIG_SND_SOC_SDW_MOCKUP) += snd-soc-sdw-mockup.o obj-$(CONFIG_SND_SOC_SGTL5000) += snd-soc-sgtl5000.o @@ -623,6 +634,7 @@ obj-$(CONFIG_SND_SOC_SSM2518) += snd-soc-ssm2518.o obj-$(CONFIG_SND_SOC_SSM2602) += snd-soc-ssm2602.o obj-$(CONFIG_SND_SOC_SSM2602_SPI) += snd-soc-ssm2602-spi.o obj-$(CONFIG_SND_SOC_SSM2602_I2C) += snd-soc-ssm2602-i2c.o +obj-$(CONFIG_SND_SOC_SSM3515) += snd-soc-ssm3515.o obj-$(CONFIG_SND_SOC_SSM4567) += snd-soc-ssm4567.o obj-$(CONFIG_SND_SOC_STA32X) += snd-soc-sta32x.o obj-$(CONFIG_SND_SOC_STA350) += snd-soc-sta350.o @@ -633,6 +645,9 @@ obj-$(CONFIG_SND_SOC_TAS2552) += snd-soc-tas2552.o obj-$(CONFIG_SND_SOC_TAS2562) += snd-soc-tas2562.o obj-$(CONFIG_SND_SOC_TAS2764) += snd-soc-tas2764.o obj-$(CONFIG_SND_SOC_TAS2780) += snd-soc-tas2780.o +obj-$(CONFIG_SND_SOC_TAS2781_COMLIB) += snd-soc-tas2781-comlib.o +obj-$(CONFIG_SND_SOC_TAS2781_FMWLIB) += snd-soc-tas2781-fmwlib.o +obj-$(CONFIG_SND_SOC_TAS2781_I2C) += snd-soc-tas2781-i2c.o obj-$(CONFIG_SND_SOC_TAS5086) += snd-soc-tas5086.o obj-$(CONFIG_SND_SOC_TAS571X) += snd-soc-tas571x.o obj-$(CONFIG_SND_SOC_TAS5720) += snd-soc-tas5720.o @@ -733,6 +748,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o obj-$(CONFIG_SND_SOC_WSA881X) += snd-soc-wsa881x.o obj-$(CONFIG_SND_SOC_WSA883X) += snd-soc-wsa883x.o +obj-$(CONFIG_SND_SOC_WSA884X) += snd-soc-wsa884x.o obj-$(CONFIG_SND_SOC_ZL38060) += snd-soc-zl38060.o # Amp diff --git a/sound/soc/codecs/ad193x-i2c.c b/sound/soc/codecs/ad193x-i2c.c index 4cb8d87f9011..15d74bb31c4c 100644 --- a/sound/soc/codecs/ad193x-i2c.c +++ b/sound/soc/codecs/ad193x-i2c.c @@ -38,7 +38,7 @@ static struct i2c_driver ad193x_i2c_driver = { .driver = { .name = "ad193x", }, - .probe_new = ad193x_i2c_probe, + .probe = ad193x_i2c_probe, .id_table = ad193x_id, }; module_i2c_driver(ad193x_i2c_driver); diff --git a/sound/soc/codecs/adau1372-i2c.c b/sound/soc/codecs/adau1372-i2c.c index 8ed0ffdedbc9..132b9e2cca59 100644 --- a/sound/soc/codecs/adau1372-i2c.c +++ b/sound/soc/codecs/adau1372-i2c.c @@ -30,7 +30,7 @@ static struct i2c_driver adau1372_i2c_driver = { .driver = { .name = "adau1372", }, - .probe_new = adau1372_i2c_probe, + .probe = adau1372_i2c_probe, .id_table = adau1372_i2c_ids, }; module_i2c_driver(adau1372_i2c_driver); diff --git a/sound/soc/codecs/adau1373.c b/sound/soc/codecs/adau1373.c index 37e178e8a1d0..c5b087b8fffc 100644 --- a/sound/soc/codecs/adau1373.c +++ b/sound/soc/codecs/adau1373.c @@ -1505,7 +1505,7 @@ static struct i2c_driver adau1373_i2c_driver = { .driver = { .name = "adau1373", }, - .probe_new = adau1373_i2c_probe, + .probe = adau1373_i2c_probe, .id_table = adau1373_i2c_id, }; diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c index 135a7db7fcf9..8c8de3b3c901 100644 --- a/sound/soc/codecs/adau1701.c +++ b/sound/soc/codecs/adau1701.c @@ -876,7 +876,7 @@ static struct i2c_driver adau1701_i2c_driver = { .name = "adau1701", .of_match_table = of_match_ptr(adau1701_dt_ids), }, - .probe_new = adau1701_i2c_probe, + .probe = adau1701_i2c_probe, .id_table = adau1701_i2c_id, }; diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c index 0cefff49569c..a554255186ae 100644 --- a/sound/soc/codecs/adau1761-i2c.c +++ b/sound/soc/codecs/adau1761-i2c.c @@ -60,7 +60,7 @@ static struct i2c_driver adau1761_i2c_driver = { .name = "adau1761", .of_match_table = of_match_ptr(adau1761_i2c_dt_ids), }, - .probe_new = adau1761_i2c_probe, + .probe = adau1761_i2c_probe, .remove = adau1761_i2c_remove, .id_table = adau1761_i2c_ids, }; diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c index 39021b8cfb62..3a170fd78ff3 100644 --- a/sound/soc/codecs/adau1781-i2c.c +++ b/sound/soc/codecs/adau1781-i2c.c @@ -56,7 +56,7 @@ static struct i2c_driver adau1781_i2c_driver = { .name = "adau1781", .of_match_table = of_match_ptr(adau1781_i2c_dt_ids), }, - .probe_new = adau1781_i2c_probe, + .probe = adau1781_i2c_probe, .remove = adau1781_i2c_remove, .id_table = adau1781_i2c_ids, }; diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c index 634d4dbca5ec..f2932713b4de 100644 --- a/sound/soc/codecs/adau17x1.c +++ b/sound/soc/codecs/adau17x1.c @@ -1059,13 +1059,12 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap, if (!adau) return -ENOMEM; - adau->mclk = devm_clk_get(dev, "mclk"); - if (IS_ERR(adau->mclk)) { - if (PTR_ERR(adau->mclk) != -ENOENT) - return PTR_ERR(adau->mclk); - /* Clock is optional (for the driver) */ - adau->mclk = NULL; - } else if (adau->mclk) { + /* Clock is optional (for the driver) */ + adau->mclk = devm_clk_get_optional(dev, "mclk"); + if (IS_ERR(adau->mclk)) + return PTR_ERR(adau->mclk); + + if (adau->mclk) { adau->clk_src = ADAU17X1_CLK_SRC_PLL_AUTO; /* diff --git a/sound/soc/codecs/adau1977-i2c.c b/sound/soc/codecs/adau1977-i2c.c index 9f137a0634d5..24c7b9c84c19 100644 --- a/sound/soc/codecs/adau1977-i2c.c +++ b/sound/soc/codecs/adau1977-i2c.c @@ -42,7 +42,7 @@ static struct i2c_driver adau1977_i2c_driver = { .driver = { .name = "adau1977", }, - .probe_new = adau1977_i2c_probe, + .probe = adau1977_i2c_probe, .id_table = adau1977_i2c_ids, }; module_i2c_driver(adau1977_i2c_driver); diff --git a/sound/soc/codecs/adau7118-i2c.c b/sound/soc/codecs/adau7118-i2c.c index afed48401b25..73f181f7757e 100644 --- a/sound/soc/codecs/adau7118-i2c.c +++ b/sound/soc/codecs/adau7118-i2c.c @@ -78,7 +78,7 @@ static struct i2c_driver adau7118_driver = { .name = "adau7118", .of_match_table = adau7118_of_match, }, - .probe_new = adau7118_probe_i2c, + .probe = adau7118_probe_i2c, .id_table = adau7118_id, }; module_i2c_driver(adau7118_driver); diff --git a/sound/soc/codecs/adav803.c b/sound/soc/codecs/adav803.c index bf181bbaabed..78a317947df9 100644 --- a/sound/soc/codecs/adav803.c +++ b/sound/soc/codecs/adav803.c @@ -29,7 +29,7 @@ static struct i2c_driver adav803_driver = { .driver = { .name = "adav803", }, - .probe_new = adav803_probe, + .probe = adav803_probe, .id_table = adav803_id, }; module_i2c_driver(adav803_driver); diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c index b6d9a10bdccd..e34e5533765c 100644 --- a/sound/soc/codecs/ak4118.c +++ b/sound/soc/codecs/ak4118.c @@ -264,8 +264,6 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data) struct ak4118_priv *ak4118 = data; struct snd_soc_component *component = ak4118->component; struct snd_kcontrol_new *kctl_new; - struct snd_kcontrol *kctl; - struct snd_ctl_elem_id *id; unsigned int i; if (!component) @@ -273,13 +271,8 @@ static irqreturn_t ak4118_irq_handler(int irq, void *data) for (i = 0; i < ARRAY_SIZE(ak4118_iec958_controls); i++) { kctl_new = &ak4118_iec958_controls[i]; - kctl = snd_soc_card_get_kcontrol(component->card, - kctl_new->name); - if (!kctl) - continue; - id = &kctl->id; - snd_ctl_notify(component->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, id); + + snd_soc_component_notify_control(component, kctl_new->name); } return IRQ_HANDLED; @@ -414,7 +407,7 @@ static struct i2c_driver ak4118_i2c_driver = { .of_match_table = of_match_ptr(ak4118_of_match), }, .id_table = ak4118_id_table, - .probe_new = ak4118_i2c_probe, + .probe = ak4118_i2c_probe, }; module_i2c_driver(ak4118_i2c_driver); diff --git a/sound/soc/codecs/ak4375.c b/sound/soc/codecs/ak4375.c index 573389e402f8..f287acb98646 100644 --- a/sound/soc/codecs/ak4375.c +++ b/sound/soc/codecs/ak4375.c @@ -597,7 +597,7 @@ static struct i2c_driver ak4375_i2c_driver = { .pm = &ak4375_pm, .of_match_table = ak4375_of_match, }, - .probe_new = ak4375_i2c_probe, + .probe = ak4375_i2c_probe, .remove = ak4375_i2c_remove, }; module_i2c_driver(ak4375_i2c_driver); diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c index 8c69c8cf9b67..77678f85ad94 100644 --- a/sound/soc/codecs/ak4458.c +++ b/sound/soc/codecs/ak4458.c @@ -818,7 +818,7 @@ static struct i2c_driver ak4458_i2c_driver = { .pm = &ak4458_pm, .of_match_table = ak4458_of_match, }, - .probe_new = ak4458_i2c_probe, + .probe = ak4458_i2c_probe, .remove = ak4458_i2c_remove, }; diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c index 8c8c93eea704..904bf91090aa 100644 --- a/sound/soc/codecs/ak4535.c +++ b/sound/soc/codecs/ak4535.c @@ -439,7 +439,7 @@ static struct i2c_driver ak4535_i2c_driver = { .driver = { .name = "ak4535", }, - .probe_new = ak4535_i2c_probe, + .probe = ak4535_i2c_probe, .id_table = ak4535_i2c_id, }; diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c index f75c19ef3551..ad56caec9dac 100644 --- a/sound/soc/codecs/ak4613.c +++ b/sound/soc/codecs/ak4613.c @@ -925,7 +925,7 @@ static struct i2c_driver ak4613_i2c_driver = { .name = "ak4613-codec", .of_match_table = ak4613_of_match, }, - .probe_new = ak4613_i2c_probe, + .probe = ak4613_i2c_probe, .id_table = ak4613_i2c_id, }; diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c index 0d3ee195b3cc..5b7df2f0dd6a 100644 --- a/sound/soc/codecs/ak4641.c +++ b/sound/soc/codecs/ak4641.c @@ -628,7 +628,7 @@ static struct i2c_driver ak4641_i2c_driver = { .driver = { .name = "ak4641", }, - .probe_new = ak4641_i2c_probe, + .probe = ak4641_i2c_probe, .remove = ak4641_i2c_remove, .id_table = ak4641_i2c_id, }; diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c index 914d5b1969f8..2a8984c1fa9c 100644 --- a/sound/soc/codecs/ak4642.c +++ b/sound/soc/codecs/ak4642.c @@ -698,7 +698,7 @@ static struct i2c_driver ak4642_i2c_driver = { .name = "ak4642-codec", .of_match_table = ak4642_of_match, }, - .probe_new = ak4642_i2c_probe, + .probe = ak4642_i2c_probe, .id_table = ak4642_i2c_id, }; diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c index cd76765f8cc0..5b849b390c2a 100644 --- a/sound/soc/codecs/ak4671.c +++ b/sound/soc/codecs/ak4671.c @@ -655,7 +655,7 @@ static struct i2c_driver ak4671_i2c_driver = { .driver = { .name = "ak4671-codec", }, - .probe_new = ak4671_i2c_probe, + .probe = ak4671_i2c_probe, .id_table = ak4671_i2c_id, }; diff --git a/sound/soc/codecs/ak5558.c b/sound/soc/codecs/ak5558.c index 60abcffe6a0c..442e2cb42df4 100644 --- a/sound/soc/codecs/ak5558.c +++ b/sound/soc/codecs/ak5558.c @@ -497,7 +497,7 @@ static struct i2c_driver ak5558_i2c_driver = { .of_match_table = of_match_ptr(ak5558_i2c_dt_ids), .pm = &ak5558_pm, }, - .probe_new = ak5558_i2c_probe, + .probe = ak5558_i2c_probe, .remove = ak5558_i2c_remove, }; diff --git a/sound/soc/codecs/alc5623.c b/sound/soc/codecs/alc5623.c index 9ef01b1dd294..b24c32206884 100644 --- a/sound/soc/codecs/alc5623.c +++ b/sound/soc/codecs/alc5623.c @@ -1083,7 +1083,7 @@ static struct i2c_driver alc5623_i2c_driver = { .name = "alc562x-codec", .of_match_table = of_match_ptr(alc5623_of_match), }, - .probe_new = alc5623_i2c_probe, + .probe = alc5623_i2c_probe, .id_table = alc5623_i2c_table, }; diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c index a770704a4e17..d5021f266930 100644 --- a/sound/soc/codecs/alc5632.c +++ b/sound/soc/codecs/alc5632.c @@ -1182,7 +1182,7 @@ static struct i2c_driver alc5632_i2c_driver = { .name = "alc5632", .of_match_table = of_match_ptr(alc5632_of_match), }, - .probe_new = alc5632_i2c_probe, + .probe = alc5632_i2c_probe, .id_table = alc5632_i2c_table, }; diff --git a/sound/soc/codecs/aw88395/aw88395.c b/sound/soc/codecs/aw88395/aw88395.c index afdce6b7fa26..9dcd75dd799a 100644 --- a/sound/soc/codecs/aw88395/aw88395.c +++ b/sound/soc/codecs/aw88395/aw88395.c @@ -570,7 +570,7 @@ static struct i2c_driver aw88395_i2c_driver = { .driver = { .name = AW88395_I2C_NAME, }, - .probe_new = aw88395_i2c_probe, + .probe = aw88395_i2c_probe, .id_table = aw88395_i2c_id, }; module_i2c_driver(aw88395_i2c_driver); diff --git a/sound/soc/codecs/chv3-codec.c b/sound/soc/codecs/chv3-codec.c new file mode 100644 index 000000000000..ab99effa6874 --- /dev/null +++ b/sound/soc/codecs/chv3-codec.c @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include <linux/module.h> +#include <sound/soc.h> + +static struct snd_soc_dai_driver chv3_codec_dai = { + .name = "chv3-codec-hifi", + .capture = { + .stream_name = "Capture", + .channels_min = 8, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S32_LE, + }, +}; + +static const struct snd_soc_component_driver soc_component_dev_chv3_codec = { +}; + +static int chv3_codec_probe(struct platform_device *pdev) +{ + return devm_snd_soc_register_component(&pdev->dev, + &soc_component_dev_chv3_codec, &chv3_codec_dai, 1); +} + +static const struct of_device_id chv3_codec_of_match[] = { + { .compatible = "google,chv3-codec", }, + { } +}; + +static struct platform_driver chv3_codec_platform_driver = { + .driver = { + .name = "chv3-codec", + .of_match_table = chv3_codec_of_match, + }, + .probe = chv3_codec_probe, +}; +module_platform_driver(chv3_codec_platform_driver); + +MODULE_DESCRIPTION("ASoC Chameleon v3 codec driver"); +MODULE_AUTHOR("Pawel Anikiel <pan@semihalf.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c index dc7a58d68076..6e658bb16fb0 100644 --- a/sound/soc/codecs/cs35l32.c +++ b/sound/soc/codecs/cs35l32.c @@ -260,7 +260,7 @@ static const struct regmap_config cs35l32_regmap = { .volatile_reg = cs35l32_volatile_register, .readable_reg = cs35l32_readable_register, .precious_reg = cs35l32_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -572,7 +572,7 @@ static struct i2c_driver cs35l32_i2c_driver = { .of_match_table = cs35l32_of_match, }, .id_table = cs35l32_id, - .probe_new = cs35l32_i2c_probe, + .probe = cs35l32_i2c_probe, .remove = cs35l32_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c index 15e79168d256..9968c2e189e6 100644 --- a/sound/soc/codecs/cs35l33.c +++ b/sound/soc/codecs/cs35l33.c @@ -852,7 +852,7 @@ static const struct regmap_config cs35l33_regmap = { .volatile_reg = cs35l33_volatile_register, .readable_reg = cs35l33_readable_register, .writeable_reg = cs35l33_writeable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1282,7 +1282,7 @@ static struct i2c_driver cs35l33_i2c_driver = { }, .id_table = cs35l33_id, - .probe_new = cs35l33_i2c_probe, + .probe = cs35l33_i2c_probe, .remove = cs35l33_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c index b3f98023e6a7..6974dd461410 100644 --- a/sound/soc/codecs/cs35l34.c +++ b/sound/soc/codecs/cs35l34.c @@ -799,7 +799,7 @@ static struct regmap_config cs35l34_regmap = { .volatile_reg = cs35l34_volatile_register, .readable_reg = cs35l34_readable_register, .precious_reg = cs35l34_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -1213,7 +1213,7 @@ static struct i2c_driver cs35l34_i2c_driver = { }, .id_table = cs35l34_id, - .probe_new = cs35l34_i2c_probe, + .probe = cs35l34_i2c_probe, .remove = cs35l34_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c index 947a440a3a47..0a4b5aa78185 100644 --- a/sound/soc/codecs/cs35l35.c +++ b/sound/soc/codecs/cs35l35.c @@ -1099,7 +1099,7 @@ static struct regmap_config cs35l35_regmap = { .volatile_reg = cs35l35_volatile_register, .readable_reg = cs35l35_readable_register, .precious_reg = cs35l35_precious_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1654,7 +1654,7 @@ static struct i2c_driver cs35l35_i2c_driver = { .of_match_table = cs35l35_of_match, }, .id_table = cs35l35_id, - .probe_new = cs35l35_i2c_probe, + .probe = cs35l35_i2c_probe, .remove = cs35l35_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c index a078dd422ea1..04ba7f25012e 100644 --- a/sound/soc/codecs/cs35l36.c +++ b/sound/soc/codecs/cs35l36.c @@ -1944,7 +1944,7 @@ static struct i2c_driver cs35l36_i2c_driver = { .of_match_table = cs35l36_of_match, }, .id_table = cs35l36_id, - .probe_new = cs35l36_i2c_probe, + .probe = cs35l36_i2c_probe, .remove = cs35l36_i2c_remove, }; module_i2c_driver(cs35l36_i2c_driver); diff --git a/sound/soc/codecs/cs35l41-i2c.c b/sound/soc/codecs/cs35l41-i2c.c index 3676b596f60b..7ea890d7d387 100644 --- a/sound/soc/codecs/cs35l41-i2c.c +++ b/sound/soc/codecs/cs35l41-i2c.c @@ -88,7 +88,7 @@ static struct i2c_driver cs35l41_i2c_driver = { .acpi_match_table = ACPI_PTR(cs35l41_acpi_match), }, .id_table = cs35l41_id_i2c, - .probe_new = cs35l41_i2c_probe, + .probe = cs35l41_i2c_probe, .remove = cs35l41_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c index 562f73df7afa..77e0f8750f37 100644 --- a/sound/soc/codecs/cs35l45-i2c.c +++ b/sound/soc/codecs/cs35l45-i2c.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-i2c.c -- CS35L45 I2C driver // @@ -65,12 +65,12 @@ static struct i2c_driver cs35l45_i2c_driver = { .pm = &cs35l45_pm_ops, }, .id_table = cs35l45_id_i2c, - .probe_new = cs35l45_i2c_probe, + .probe = cs35l45_i2c_probe, .remove = cs35l45_i2c_remove, }; module_i2c_driver(cs35l45_i2c_driver); MODULE_DESCRIPTION("I2C CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c index a00b23b4180c..5efb77530cc3 100644 --- a/sound/soc/codecs/cs35l45-spi.c +++ b/sound/soc/codecs/cs35l45-spi.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-spi.c -- CS35L45 SPI driver // @@ -74,5 +74,5 @@ module_spi_driver(cs35l45_spi_driver); MODULE_DESCRIPTION("SPI CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); MODULE_IMPORT_NS(SND_SOC_CS35L45); diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c index 46610e64e818..066f83c0c7ac 100644 --- a/sound/soc/codecs/cs35l45-tables.c +++ b/sound/soc/codecs/cs35l45-tables.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45-tables.c -- CS35L45 ALSA SoC audio driver // diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c index c31597f6bfae..d1edb9876c10 100644 --- a/sound/soc/codecs/cs35l45.c +++ b/sound/soc/codecs/cs35l45.c @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +// SPDX-License-Identifier: GPL-2.0 // // cs35l45.c - CS35L45 ALSA SoC audio driver // @@ -1296,4 +1296,4 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_pm_ops, SND_SOC_CS35L45); MODULE_DESCRIPTION("ASoC CS35L45 driver"); MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>"); -MODULE_LICENSE("Dual BSD/GPL"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h index 0da28439f628..61135a316df3 100644 --- a/sound/soc/codecs/cs35l45.h +++ b/sound/soc/codecs/cs35l45.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* SPDX-License-Identifier: GPL-2.0 */ /* * cs35l45.h - CS35L45 ALSA SoC audio driver * diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c index 295caad26224..ed2a41943d97 100644 --- a/sound/soc/codecs/cs35l56-i2c.c +++ b/sound/soc/codecs/cs35l56-i2c.c @@ -68,7 +68,7 @@ static struct i2c_driver cs35l56_i2c_driver = { .pm = &cs35l56_pm_ops_i2c_spi, }, .id_table = cs35l56_id_i2c, - .probe_new = cs35l56_i2c_probe, + .probe = cs35l56_i2c_probe, .remove = cs35l56_i2c_remove, }; diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c index e0d2b9bb2326..c03f9d3c9a13 100644 --- a/sound/soc/codecs/cs35l56.c +++ b/sound/soc/codecs/cs35l56.c @@ -822,25 +822,23 @@ static void cs35l56_system_reset(struct cs35l56_private *cs35l56) regcache_cache_only(cs35l56->regmap, false); } -static void cs35l56_dsp_work(struct work_struct *work) +static void cs35l56_secure_patch(struct cs35l56_private *cs35l56) { - struct cs35l56_private *cs35l56 = container_of(work, - struct cs35l56_private, - dsp_work); - unsigned int reg; - unsigned int val; - int ret = 0; - - if (!cs35l56->init_done) - return; - - cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", - cs35l56->secured ? "s" : "", cs35l56->rev); + int ret; - if (!cs35l56->dsp.part) - return; + /* Use wm_adsp to load and apply the firmware patch and coefficient files */ + ret = wm_adsp_power_up(&cs35l56->dsp); + if (ret) + dev_dbg(cs35l56->dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret); + else + cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_REINIT); +} - pm_runtime_get_sync(cs35l56->dev); +static void cs35l56_patch(struct cs35l56_private *cs35l56) +{ + unsigned int reg; + unsigned int val; + int ret; /* * Disable SoundWire interrupts to prevent race with IRQ work. @@ -907,9 +905,6 @@ static void cs35l56_dsp_work(struct work_struct *work) err_unlock: mutex_unlock(&cs35l56->irq_lock); err: - pm_runtime_mark_last_busy(cs35l56->dev); - pm_runtime_put_autosuspend(cs35l56->dev); - /* Re-enable SoundWire interrupts */ if (cs35l56->sdw_peripheral) { cs35l56->sdw_irq_no_unmask = false; @@ -918,6 +913,32 @@ err: } } +static void cs35l56_dsp_work(struct work_struct *work) +{ + struct cs35l56_private *cs35l56 = container_of(work, + struct cs35l56_private, + dsp_work); + + if (!cs35l56->init_done) + return; + + pm_runtime_get_sync(cs35l56->dev); + + /* + * When the device is running in secure mode the firmware files can + * only contain insecure tunings and therefore we do not need to + * shutdown the firmware to apply them and can use the lower cost + * reinit sequence instead. + */ + if (cs35l56->secured) + cs35l56_secure_patch(cs35l56); + else + cs35l56_patch(cs35l56); + + pm_runtime_mark_last_busy(cs35l56->dev); + pm_runtime_put_autosuspend(cs35l56->dev); +} + static int cs35l56_component_probe(struct snd_soc_component *component) { struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component); @@ -1505,6 +1526,12 @@ int cs35l56_init(struct cs35l56_private *cs35l56) dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n", cs35l56->secured ? "s" : "", cs35l56->rev, otpid); + /* Populate the DSP information with the revision and security state */ + cs35l56->dsp.part = devm_kasprintf(cs35l56->dev, GFP_KERNEL, "cs35l56%s-%02x", + cs35l56->secured ? "s" : "", cs35l56->rev); + if (!cs35l56->dsp.part) + return -ENOMEM; + /* Wake source and *_BLOCKED interrupts default to unmasked, so mask them */ regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff); regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_1, diff --git a/sound/soc/codecs/cs4234.c b/sound/soc/codecs/cs4234.c index dee1a6662c2e..69287ba7e955 100644 --- a/sound/soc/codecs/cs4234.c +++ b/sound/soc/codecs/cs4234.c @@ -675,7 +675,7 @@ static const struct regmap_config cs4234_regmap = { .writeable_reg = cs4234_writeable_register, .reg_defaults = cs4234_default_reg, .num_reg_defaults = ARRAY_SIZE(cs4234_default_reg), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -906,7 +906,7 @@ static struct i2c_driver cs4234_i2c_driver = { .pm = &cs4234_pm, .of_match_table = cs4234_of_match, }, - .probe_new = cs4234_i2c_probe, + .probe = cs4234_i2c_probe, .remove = cs4234_i2c_remove, }; module_i2c_driver(cs4234_i2c_driver); diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c index 3573363b7e31..0cfc5ab36a13 100644 --- a/sound/soc/codecs/cs4265.c +++ b/sound/soc/codecs/cs4265.c @@ -649,7 +649,7 @@ static struct i2c_driver cs4265_i2c_driver = { .of_match_table = cs4265_of_match, }, .id_table = cs4265_id, - .probe_new = cs4265_i2c_probe, + .probe = cs4265_i2c_probe, .remove = cs4265_i2c_remove, }; diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c index 1b640d8232ba..ab32f15e3b44 100644 --- a/sound/soc/codecs/cs4270.c +++ b/sound/soc/codecs/cs4270.c @@ -751,7 +751,7 @@ static struct i2c_driver cs4270_i2c_driver = { .of_match_table = cs4270_of_match, }, .id_table = cs4270_id, - .probe_new = cs4270_i2c_probe, + .probe = cs4270_i2c_probe, .remove = cs4270_i2c_remove, }; diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c index 4033be1c3bc1..89fe7d1665df 100644 --- a/sound/soc/codecs/cs4271-i2c.c +++ b/sound/soc/codecs/cs4271-i2c.c @@ -33,7 +33,7 @@ static struct i2c_driver cs4271_i2c_driver = { .name = "cs4271", .of_match_table = of_match_ptr(cs4271_dt_ids), }, - .probe_new = cs4271_i2c_probe, + .probe = cs4271_i2c_probe, .id_table = cs4271_i2c_id, }; module_i2c_driver(cs4271_i2c_driver); diff --git a/sound/soc/codecs/cs42l42-i2c.c b/sound/soc/codecs/cs42l42-i2c.c index 67b253287daf..2552a1e6b82f 100644 --- a/sound/soc/codecs/cs42l42-i2c.c +++ b/sound/soc/codecs/cs42l42-i2c.c @@ -92,7 +92,7 @@ static struct i2c_driver cs42l42_i2c_driver = { .acpi_match_table = ACPI_PTR(cs42l42_acpi_match), }, .id_table = cs42l42_id, - .probe_new = cs42l42_i2c_probe, + .probe = cs42l42_i2c_probe, .remove = cs42l42_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c index e3edaa1a2761..a0de0329406a 100644 --- a/sound/soc/codecs/cs42l42.c +++ b/sound/soc/codecs/cs42l42.c @@ -393,7 +393,7 @@ const struct regmap_config cs42l42_regmap = { .max_register = CS42L42_MAX_REGISTER, .reg_defaults = cs42l42_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -646,12 +646,19 @@ static const struct cs42l42_pll_params pll_ratio_table[] = { { 3072000, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, { 4000000, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96, 1}, { 4096000, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94, 1}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 4800000, 1, 0x01, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 5644800, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, { 6000000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, { 6144000, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125, 1}, + { 6144000, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 9600000, 1, 0x02, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 11289600, 0, 0, 0, 0, 0, 0, 11289600, 0, 1}, { 12000000, 0, 0, 0, 0, 0, 0, 12000000, 0, 1}, { 12288000, 0, 0, 0, 0, 0, 0, 12288000, 0, 1}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x03, 0x10, 12000000, 80, 2}, + { 19200000, 1, 0x03, 0x50, 0x000000, 0x01, 0x10, 12288000, 82, 2}, { 22579200, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 11289600, 128, 1}, { 24000000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12000000, 128, 1}, { 24576000, 1, 0x03, 0x40, 0x000000, 0x03, 0x10, 12288000, 128, 1} diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c index 85238339fbca..b2106ff6a7cb 100644 --- a/sound/soc/codecs/cs42l51-i2c.c +++ b/sound/soc/codecs/cs42l51-i2c.c @@ -43,7 +43,7 @@ static struct i2c_driver cs42l51_i2c_driver = { .of_match_table = cs42l51_of_match, .pm = &cs42l51_pm_ops, }, - .probe_new = cs42l51_i2c_probe, + .probe = cs42l51_i2c_probe, .remove = cs42l51_i2c_remove, .id_table = cs42l51_i2c_id, }; diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c index e88d9ff95cdf..a67cd3ee84e0 100644 --- a/sound/soc/codecs/cs42l51.c +++ b/sound/soc/codecs/cs42l51.c @@ -724,12 +724,9 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) dev_set_drvdata(dev, cs42l51); cs42l51->regmap = regmap; - cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); - if (IS_ERR(cs42l51->mclk_handle)) { - if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) - return PTR_ERR(cs42l51->mclk_handle); - cs42l51->mclk_handle = NULL; - } + cs42l51->mclk_handle = devm_clk_get_optional(dev, "MCLK"); + if (IS_ERR(cs42l51->mclk_handle)) + return PTR_ERR(cs42l51->mclk_handle); for (i = 0; i < ARRAY_SIZE(cs42l51->supplies); i++) cs42l51->supplies[i].supply = cs42l51_supply_names[i]; diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c index 90bf535fc5a5..1f1ded0ff0ac 100644 --- a/sound/soc/codecs/cs42l52.c +++ b/sound/soc/codecs/cs42l52.c @@ -1226,7 +1226,7 @@ static struct i2c_driver cs42l52_i2c_driver = { .of_match_table = cs42l52_of_match, }, .id_table = cs42l52_id, - .probe_new = cs42l52_i2c_probe, + .probe = cs42l52_i2c_probe, }; module_i2c_driver(cs42l52_i2c_driver); diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c index 3b0e715549c9..4c646e8d72aa 100644 --- a/sound/soc/codecs/cs42l56.c +++ b/sound/soc/codecs/cs42l56.c @@ -1341,7 +1341,7 @@ static struct i2c_driver cs42l56_i2c_driver = { .of_match_table = cs42l56_of_match, }, .id_table = cs42l56_id, - .probe_new = cs42l56_i2c_probe, + .probe = cs42l56_i2c_probe, .remove = cs42l56_i2c_remove, }; diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c index 0a146319755a..6ab67d196d10 100644 --- a/sound/soc/codecs/cs42l73.c +++ b/sound/soc/codecs/cs42l73.c @@ -1267,7 +1267,7 @@ static const struct regmap_config cs42l73_regmap = { .num_reg_defaults = ARRAY_SIZE(cs42l73_reg_defaults), .volatile_reg = cs42l73_volatile_register, .readable_reg = cs42l73_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -1384,7 +1384,7 @@ static struct i2c_driver cs42l73_i2c_driver = { .of_match_table = cs42l73_of_match, }, .id_table = cs42l73_id, - .probe_new = cs42l73_i2c_probe, + .probe = cs42l73_i2c_probe, }; diff --git a/sound/soc/codecs/cs42l83-i2c.c b/sound/soc/codecs/cs42l83-i2c.c index 37629ebd90e0..f482b6a4f5c3 100644 --- a/sound/soc/codecs/cs42l83-i2c.c +++ b/sound/soc/codecs/cs42l83-i2c.c @@ -158,7 +158,7 @@ static const struct regmap_config cs42l83_regmap = { .max_register = CS42L42_MAX_REGISTER, .reg_defaults = cs42l83_reg_defaults, .num_reg_defaults = ARRAY_SIZE(cs42l83_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -228,7 +228,7 @@ static struct i2c_driver cs42l83_i2c_driver = { .pm = &cs42l83_i2c_pm_ops, .of_match_table = of_match_ptr(cs42l83_of_match), }, - .probe_new = cs42l83_i2c_probe, + .probe = cs42l83_i2c_probe, .remove = cs42l83_i2c_remove, }; diff --git a/sound/soc/codecs/cs42xx8-i2c.c b/sound/soc/codecs/cs42xx8-i2c.c index 052ffb7dcfc6..a422472820fb 100644 --- a/sound/soc/codecs/cs42xx8-i2c.c +++ b/sound/soc/codecs/cs42xx8-i2c.c @@ -70,7 +70,7 @@ static struct i2c_driver cs42xx8_i2c_driver = { .pm = &cs42xx8_pm, .of_match_table = cs42xx8_of_match, }, - .probe_new = cs42xx8_i2c_probe, + .probe = cs42xx8_i2c_probe, .remove = cs42xx8_i2c_remove, .id_table = cs42xx8_i2c_id, }; diff --git a/sound/soc/codecs/cs43130.c b/sound/soc/codecs/cs43130.c index db39abb2a31b..3292405024bc 100644 --- a/sound/soc/codecs/cs43130.c +++ b/sound/soc/codecs/cs43130.c @@ -2357,7 +2357,7 @@ static const struct regmap_config cs43130_regmap = { .readable_reg = cs43130_readable_register, .precious_reg = cs43130_precious_register, .volatile_reg = cs43130_volatile_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, /* needed for regcache_sync */ .use_single_read = true, .use_single_write = true, @@ -2697,7 +2697,7 @@ static struct i2c_driver cs43130_i2c_driver = { .pm = &cs43130_runtime_pm, }, .id_table = cs43130_i2c_id, - .probe_new = cs43130_i2c_probe, + .probe = cs43130_i2c_probe, .remove = cs43130_i2c_remove, }; diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c index ac1696034846..2ceca5d0e5bf 100644 --- a/sound/soc/codecs/cs4341.c +++ b/sound/soc/codecs/cs4341.c @@ -258,7 +258,7 @@ static struct i2c_driver cs4341_i2c_driver = { .name = "cs4341-i2c", .of_match_table = of_match_ptr(cs4341_dt_ids), }, - .probe_new = cs4341_i2c_probe, + .probe = cs4341_i2c_probe, .id_table = cs4341_i2c_id, }; #endif diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c index ba94ffd0a7e4..8365dd0ebe2a 100644 --- a/sound/soc/codecs/cs4349.c +++ b/sound/soc/codecs/cs4349.c @@ -375,7 +375,7 @@ static struct i2c_driver cs4349_i2c_driver = { .pm = &cs4349_runtime_pm, }, .id_table = cs4349_i2c_id, - .probe_new = cs4349_i2c_probe, + .probe = cs4349_i2c_probe, .remove = cs4349_i2c_remove, }; diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c index 69db0013d243..f4065555c36e 100644 --- a/sound/soc/codecs/cs53l30.c +++ b/sound/soc/codecs/cs53l30.c @@ -911,7 +911,7 @@ static struct regmap_config cs53l30_regmap = { .volatile_reg = cs53l30_volatile_register, .writeable_reg = cs53l30_writeable_register, .readable_reg = cs53l30_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, @@ -990,14 +990,10 @@ static int cs53l30_i2c_probe(struct i2c_client *client) } /* Check if MCLK provided */ - cs53l30->mclk = devm_clk_get(dev, "mclk"); + cs53l30->mclk = devm_clk_get_optional(dev, "mclk"); if (IS_ERR(cs53l30->mclk)) { - if (PTR_ERR(cs53l30->mclk) != -ENOENT) { - ret = PTR_ERR(cs53l30->mclk); - goto error; - } - /* Otherwise mark the mclk pointer to NULL */ - cs53l30->mclk = NULL; + ret = PTR_ERR(cs53l30->mclk); + goto error; } /* Fetch the MUTE control */ @@ -1121,7 +1117,7 @@ static struct i2c_driver cs53l30_i2c_driver = { .pm = &cs53l30_runtime_pm, }, .id_table = cs53l30_id, - .probe_new = cs53l30_i2c_probe, + .probe = cs53l30_i2c_probe, .remove = cs53l30_i2c_remove, }; diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c index 5deceaa89282..082231088a26 100644 --- a/sound/soc/codecs/cx2072x.c +++ b/sound/soc/codecs/cx2072x.c @@ -1706,7 +1706,7 @@ static struct i2c_driver cx2072x_i2c_driver = { .acpi_match_table = ACPI_PTR(cx2072x_acpi_match), .pm = &cx2072x_runtime_pm, }, - .probe_new = cx2072x_i2c_probe, + .probe = cx2072x_i2c_probe, .remove = cx2072x_i2c_remove, .id_table = cx2072x_i2c_id, }; diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c index f838466bfebf..1e232d01809c 100644 --- a/sound/soc/codecs/da7210.c +++ b/sound/soc/codecs/da7210.c @@ -1248,7 +1248,7 @@ static struct i2c_driver da7210_i2c_driver = { .driver = { .name = "da7210", }, - .probe_new = da7210_i2c_probe, + .probe = da7210_i2c_probe, .id_table = da7210_i2c_id, }; #endif diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c index 1c1f211a8e2e..3a6449c44b23 100644 --- a/sound/soc/codecs/da7213.c +++ b/sound/soc/codecs/da7213.c @@ -2069,7 +2069,7 @@ static struct i2c_driver da7213_i2c_driver = { .acpi_match_table = ACPI_PTR(da7213_acpi_match), .pm = &da7213_pm, }, - .probe_new = da7213_i2c_probe, + .probe = da7213_i2c_probe, .remove = da7213_i2c_remove, .id_table = da7213_i2c_id, }; diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c index d9c28e701613..3f456b08b809 100644 --- a/sound/soc/codecs/da7218.c +++ b/sound/soc/codecs/da7218.c @@ -3317,7 +3317,7 @@ static struct i2c_driver da7218_i2c_driver = { .name = "da7218", .of_match_table = da7218_of_match, }, - .probe_new = da7218_i2c_probe, + .probe = da7218_i2c_probe, .id_table = da7218_i2c_id, }; diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c index 993a0d00bc48..c65256bd526d 100644 --- a/sound/soc/codecs/da7219-aad.c +++ b/sound/soc/codecs/da7219-aad.c @@ -571,16 +571,29 @@ static enum da7219_aad_jack_ins_deb } } +static enum da7219_aad_jack_ins_det_pty + da7219_aad_fw_jack_ins_det_pty(struct device *dev, const char *str) +{ + if (!strcmp(str, "low")) { + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } else if (!strcmp(str, "high")) { + return DA7219_AAD_JACK_INS_DET_PTY_HIGH; + } else { + dev_warn(dev, "Invalid jack insertion detection polarity"); + return DA7219_AAD_JACK_INS_DET_PTY_LOW; + } +} + static enum da7219_aad_jack_det_rate da7219_aad_fw_jack_det_rate(struct device *dev, const char *str) { - if (!strcmp(str, "32ms_64ms")) { + if (!strcmp(str, "32_64")) { return DA7219_AAD_JACK_DET_RATE_32_64MS; - } else if (!strcmp(str, "64ms_128ms")) { + } else if (!strcmp(str, "64_128")) { return DA7219_AAD_JACK_DET_RATE_64_128MS; - } else if (!strcmp(str, "128ms_256ms")) { + } else if (!strcmp(str, "128_256")) { return DA7219_AAD_JACK_DET_RATE_128_256MS; - } else if (!strcmp(str, "256ms_512ms")) { + } else if (!strcmp(str, "256_512")) { return DA7219_AAD_JACK_DET_RATE_256_512MS; } else { dev_warn(dev, "Invalid jack detect rate"); @@ -688,6 +701,12 @@ static struct da7219_aad_pdata *da7219_aad_fw_to_pdata(struct device *dev) else aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS; + if (!fwnode_property_read_string(aad_np, "dlg,jack-ins-det-pty", &fw_str)) + aad_pdata->jack_ins_det_pty = + da7219_aad_fw_jack_ins_det_pty(dev, fw_str); + else + aad_pdata->jack_ins_det_pty = DA7219_AAD_JACK_INS_DET_PTY_LOW; + if (!fwnode_property_read_string(aad_np, "dlg,jack-det-rate", &fw_str)) aad_pdata->jack_det_rate = da7219_aad_fw_jack_det_rate(dev, fw_str); @@ -849,6 +868,21 @@ static void da7219_aad_handle_pdata(struct snd_soc_component *component) mask |= DA7219_ADC_1_BIT_REPEAT_MASK; } snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_7, mask, cfg); + + switch (aad_pdata->jack_ins_det_pty) { + case DA7219_AAD_JACK_INS_DET_PTY_LOW: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x80); + snd_soc_component_write(component, 0xF0, 0x00); + break; + case DA7219_AAD_JACK_INS_DET_PTY_HIGH: + snd_soc_component_write(component, 0xF0, 0x8B); + snd_soc_component_write(component, 0x75, 0x00); + snd_soc_component_write(component, 0xF0, 0x00); + break; + default: + break; + } } } diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c index 7468ee4af2ea..600c2db58756 100644 --- a/sound/soc/codecs/da7219.c +++ b/sound/soc/codecs/da7219.c @@ -2714,7 +2714,7 @@ static struct i2c_driver da7219_i2c_driver = { .of_match_table = of_match_ptr(da7219_of_match), .acpi_match_table = ACPI_PTR(da7219_acpi_match), }, - .probe_new = da7219_i2c_probe, + .probe = da7219_i2c_probe, .id_table = da7219_i2c_id, }; diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c index 2c5b0b74201c..f8ca1afa8af5 100644 --- a/sound/soc/codecs/da732x.c +++ b/sound/soc/codecs/da732x.c @@ -1555,7 +1555,7 @@ static struct i2c_driver da732x_i2c_driver = { .driver = { .name = "da7320", }, - .probe_new = da732x_i2c_probe, + .probe = da732x_i2c_probe, .id_table = da732x_i2c_id, }; diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c index 28043b4530df..ae20086777b5 100644 --- a/sound/soc/codecs/da9055.c +++ b/sound/soc/codecs/da9055.c @@ -1531,7 +1531,7 @@ static struct i2c_driver da9055_i2c_driver = { .name = "da9055-codec", .of_match_table = of_match_ptr(da9055_of_match), }, - .probe_new = da9055_i2c_probe, + .probe = da9055_i2c_probe, .id_table = da9055_i2c_id, }; diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c index a27d80956459..34cf60769b62 100644 --- a/sound/soc/codecs/es8316.c +++ b/sound/soc/codecs/es8316.c @@ -52,7 +52,12 @@ static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(adc_vol_tlv, -9600, 50, 1); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_max_gain_tlv, -650, 150, 0); static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_min_gain_tlv, -1200, 150, 0); -static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(alc_target_tlv, -1650, 150, 0); + +static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(alc_target_tlv, + 0, 10, TLV_DB_SCALE_ITEM(-1650, 150, 0), + 11, 11, TLV_DB_SCALE_ITEM(-150, 0, 0), +); + static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(hpmixer_gain_tlv, 0, 4, TLV_DB_SCALE_ITEM(-1200, 150, 0), 8, 11, TLV_DB_SCALE_ITEM(-450, 150, 0), @@ -115,7 +120,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = { alc_max_gain_tlv), SOC_SINGLE_TLV("ALC Capture Min Volume", ES8316_ADC_ALC2, 0, 28, 0, alc_min_gain_tlv), - SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 10, 0, + SOC_SINGLE_TLV("ALC Capture Target Volume", ES8316_ADC_ALC3, 4, 11, 0, alc_target_tlv), SOC_SINGLE("ALC Capture Hold Time", ES8316_ADC_ALC3, 0, 10, 0), SOC_SINGLE("ALC Capture Decay Time", ES8316_ADC_ALC4, 4, 10, 0), @@ -364,13 +369,11 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, int count = 0; es8316->sysclk = freq; + es8316->sysclk_constraints.list = NULL; + es8316->sysclk_constraints.count = 0; - if (freq == 0) { - es8316->sysclk_constraints.list = NULL; - es8316->sysclk_constraints.count = 0; - + if (freq == 0) return 0; - } ret = clk_set_rate(es8316->mclk, freq); if (ret) @@ -386,8 +389,10 @@ static int es8316_set_dai_sysclk(struct snd_soc_dai *codec_dai, es8316->allowed_rates[count++] = freq / ratio; } - es8316->sysclk_constraints.list = es8316->allowed_rates; - es8316->sysclk_constraints.count = count; + if (count) { + es8316->sysclk_constraints.list = es8316->allowed_rates; + es8316->sysclk_constraints.count = count; + } return 0; } @@ -820,7 +825,7 @@ static const struct regmap_config es8316_regmap = { .use_single_write = true, .max_register = 0x53, .volatile_reg = es8316_volatile_reg, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, }; static int es8316_i2c_probe(struct i2c_client *i2c_client) @@ -887,7 +892,7 @@ static struct i2c_driver es8316_i2c_driver = { .acpi_match_table = ACPI_PTR(es8316_acpi_match), .of_match_table = of_match_ptr(es8316_of_match), }, - .probe_new = es8316_i2c_probe, + .probe = es8316_i2c_probe, .id_table = es8316_i2c_id, }; module_i2c_driver(es8316_i2c_driver); diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c index 28a0565c2a95..7cb5b57ae655 100644 --- a/sound/soc/codecs/es8326.c +++ b/sound/soc/codecs/es8326.c @@ -896,7 +896,7 @@ static struct i2c_driver es8326_i2c_driver = { .acpi_match_table = ACPI_PTR(es8326_acpi_match), .of_match_table = of_match_ptr(es8326_of_match), }, - .probe_new = es8326_i2c_probe, + .probe = es8326_i2c_probe, .id_table = es8326_i2c_id, }; module_i2c_driver(es8326_i2c_driver); diff --git a/sound/soc/codecs/es8328-i2c.c b/sound/soc/codecs/es8328-i2c.c index 68072e99fcc7..3c4aaa0032a0 100644 --- a/sound/soc/codecs/es8328-i2c.c +++ b/sound/soc/codecs/es8328-i2c.c @@ -40,7 +40,7 @@ static struct i2c_driver es8328_i2c_driver = { .name = "es8328", .of_match_table = es8328_of_match, }, - .probe_new = es8328_i2c_probe, + .probe = es8328_i2c_probe, .id_table = es8328_id, }; diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c index 160adc706cc6..0bd9ba5a11b4 100644 --- a/sound/soc/codecs/es8328.c +++ b/sound/soc/codecs/es8328.c @@ -822,7 +822,7 @@ const struct regmap_config es8328_regmap_config = { .reg_bits = 8, .val_bits = 8, .max_register = ES8328_REG_MAX, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c index 50105d72b2b7..f9456133a89a 100644 --- a/sound/soc/codecs/isabelle.c +++ b/sound/soc/codecs/isabelle.c @@ -1142,7 +1142,7 @@ static struct i2c_driver isabelle_i2c_driver = { .driver = { .name = "isabelle", }, - .probe_new = isabelle_i2c_probe, + .probe = isabelle_i2c_probe, .id_table = isabelle_i2c_id, }; diff --git a/sound/soc/codecs/lm4857.c b/sound/soc/codecs/lm4857.c index dba161305de8..e7542f71323d 100644 --- a/sound/soc/codecs/lm4857.c +++ b/sound/soc/codecs/lm4857.c @@ -137,7 +137,7 @@ static struct i2c_driver lm4857_i2c_driver = { .driver = { .name = "lm4857", }, - .probe_new = lm4857_i2c_probe, + .probe = lm4857_i2c_probe, .id_table = lm4857_i2c_id, }; diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c index a2e782cc4276..a4094689b3dd 100644 --- a/sound/soc/codecs/lm49453.c +++ b/sound/soc/codecs/lm49453.c @@ -1451,7 +1451,7 @@ static struct i2c_driver lm49453_i2c_driver = { .driver = { .name = "lm49453", }, - .probe_new = lm49453_i2c_probe, + .probe = lm49453_i2c_probe, .id_table = lm49453_i2c_id, }; diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c index d711eb1da0a8..d22b4ba51ed8 100644 --- a/sound/soc/codecs/max9768.c +++ b/sound/soc/codecs/max9768.c @@ -214,7 +214,7 @@ static struct i2c_driver max9768_i2c_driver = { .driver = { .name = "max9768", }, - .probe_new = max9768_i2c_probe, + .probe = max9768_i2c_probe, .id_table = max9768_i2c_id, }; module_i2c_driver(max9768_i2c_driver); diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c index 405ec16be2b6..8b56ee550c09 100644 --- a/sound/soc/codecs/max98088.c +++ b/sound/soc/codecs/max98088.c @@ -310,24 +310,24 @@ static const struct regmap_config max98088_regmap = { static void m98088_eq_band(struct snd_soc_component *component, unsigned int dai, unsigned int band, u16 *coefs) { - unsigned int eq_reg; - unsigned int i; + unsigned int eq_reg; + unsigned int i; if (WARN_ON(band > 4) || WARN_ON(dai > 1)) return; - /* Load the base register address */ - eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; + /* Load the base register address */ + eq_reg = dai ? M98088_REG_84_DAI2_EQ_BASE : M98088_REG_52_DAI1_EQ_BASE; - /* Add the band address offset, note adjustment for word address */ - eq_reg += band * (M98088_COEFS_PER_BAND << 1); + /* Add the band address offset, note adjustment for word address */ + eq_reg += band * (M98088_COEFS_PER_BAND << 1); - /* Step through the registers and coefs */ - for (i = 0; i < M98088_COEFS_PER_BAND; i++) { - snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i])); - snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i])); - } + /* Step through the registers and coefs */ + for (i = 0; i < M98088_COEFS_PER_BAND; i++) { + snd_soc_component_write(component, eq_reg++, M98088_BYTE1(coefs[i])); + snd_soc_component_write(component, eq_reg++, M98088_BYTE0(coefs[i])); + } } /* @@ -1789,7 +1789,7 @@ static struct i2c_driver max98088_i2c_driver = { .name = "max98088", .of_match_table = of_match_ptr(max98088_of_match), }, - .probe_new = max98088_i2c_probe, + .probe = max98088_i2c_probe, .id_table = max98088_i2c_id, }; diff --git a/sound/soc/codecs/max98090.c b/sound/soc/codecs/max98090.c index b419c49e1e08..2adf744c6526 100644 --- a/sound/soc/codecs/max98090.c +++ b/sound/soc/codecs/max98090.c @@ -10,7 +10,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/acpi.h> @@ -1582,7 +1581,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, struct snd_soc_component *component = codec_dai->component; struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); struct max98090_cdata *cdata; - u8 regval; + u8 regval, tdm_regval; max98090->dai_fmt = fmt; cdata = &max98090->dai[0]; @@ -1591,6 +1590,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, cdata->fmt = fmt; regval = 0; + tdm_regval = 0; switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) { case SND_SOC_DAIFMT_CBC_CFC: /* Set to consumer mode PLL - MAS mode off */ @@ -1636,7 +1636,8 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, regval |= M98090_RJ_MASK; break; case SND_SOC_DAIFMT_DSP_A: - /* Not supported mode */ + tdm_regval |= M98090_TDM_MASK; + break; default: dev_err(component->dev, "DAI format unsupported"); return -EINVAL; @@ -1665,11 +1666,20 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai, * seen for the case of TDM mode. The remaining cases have * normal logic. */ - if (max98090->tdm_slots > 1) + if (tdm_regval) regval ^= M98090_BCI_MASK; snd_soc_component_write(component, M98090_REG_INTERFACE_FORMAT, regval); + + regval = 0; + if (tdm_regval) + regval = max98090->tdm_lslot << M98090_TDM_SLOTL_SHIFT | + max98090->tdm_rslot << M98090_TDM_SLOTR_SHIFT | + 0 << M98090_TDM_SLOTDLY_SHIFT; + + snd_soc_component_write(component, M98090_REG_TDM_FORMAT, regval); + snd_soc_component_write(component, M98090_REG_TDM_CONTROL, tdm_regval); } return 0; @@ -1680,33 +1690,22 @@ static int max98090_set_tdm_slot(struct snd_soc_dai *codec_dai, { struct snd_soc_component *component = codec_dai->component; struct max98090_priv *max98090 = snd_soc_component_get_drvdata(component); - struct max98090_cdata *cdata; - cdata = &max98090->dai[0]; if (slots < 0 || slots > 4) return -EINVAL; - max98090->tdm_slots = slots; - max98090->tdm_width = slot_width; - - if (max98090->tdm_slots > 1) { - /* SLOTL SLOTR SLOTDLY */ - snd_soc_component_write(component, M98090_REG_TDM_FORMAT, - 0 << M98090_TDM_SLOTL_SHIFT | - 1 << M98090_TDM_SLOTR_SHIFT | - 0 << M98090_TDM_SLOTDLY_SHIFT); - - /* FSW TDM */ - snd_soc_component_update_bits(component, M98090_REG_TDM_CONTROL, - M98090_TDM_MASK, - M98090_TDM_MASK); - } + if (slot_width != 16) + return -EINVAL; - /* - * Normally advisable to set TDM first, but this permits either order - */ - cdata->fmt = 0; - max98090_dai_set_fmt(codec_dai, max98090->dai_fmt); + if (rx_mask != tx_mask) + return -EINVAL; + + if (!rx_mask) + return -EINVAL; + + max98090->tdm_slots = slots; + max98090->tdm_lslot = ffs(rx_mask) - 1; + max98090->tdm_rslot = fls(rx_mask) - 1; return 0; } @@ -2409,6 +2408,9 @@ static int max98090_probe(struct snd_soc_component *component) max98090->pa1en = 0; max98090->pa2en = 0; + max98090->tdm_lslot = 0; + max98090->tdm_rslot = 1; + ret = snd_soc_component_read(component, M98090_REG_REVISION_ID); if (ret < 0) { dev_err(component->dev, "Failed to read device revision: %d\n", @@ -2691,7 +2693,7 @@ static struct i2c_driver max98090_i2c_driver = { .of_match_table = of_match_ptr(max98090_of_match), .acpi_match_table = ACPI_PTR(max98090_acpi_match), }, - .probe_new = max98090_i2c_probe, + .probe = max98090_i2c_probe, .shutdown = max98090_i2c_shutdown, .remove = max98090_i2c_remove, .id_table = max98090_i2c_id, diff --git a/sound/soc/codecs/max98090.h b/sound/soc/codecs/max98090.h index a197114b0dad..6ce8dd176e48 100644 --- a/sound/soc/codecs/max98090.h +++ b/sound/soc/codecs/max98090.h @@ -1533,7 +1533,8 @@ struct max98090_priv { struct snd_soc_jack *jack; unsigned int dai_fmt; int tdm_slots; - int tdm_width; + int tdm_lslot; + int tdm_rslot; u8 lin_state; unsigned int pa1en; unsigned int pa2en; diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c index 44aa58fcc23f..7e525d49328d 100644 --- a/sound/soc/codecs/max98095.c +++ b/sound/soc/codecs/max98095.c @@ -2155,7 +2155,7 @@ static struct i2c_driver max98095_i2c_driver = { .name = "max98095", .of_match_table = of_match_ptr(max98095_of_match), }, - .probe_new = max98095_i2c_probe, + .probe = max98095_i2c_probe, .id_table = max98095_i2c_id, }; diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c index e6b84e222b50..b5c69bba0e48 100644 --- a/sound/soc/codecs/max98363.c +++ b/sound/soc/codecs/max98363.c @@ -15,11 +15,6 @@ #include "max98363.h" static struct reg_default max98363_reg[] = { - {MAX98363_R2001_INTR_RAW, 0x0}, - {MAX98363_R2003_INTR_STATE, 0x0}, - {MAX98363_R2005_INTR_FALG, 0x0}, - {MAX98363_R2007_INTR_EN, 0x0}, - {MAX98363_R2009_INTR_CLR, 0x0}, {MAX98363_R2021_ERR_MON_CTRL, 0x0}, {MAX98363_R2022_SPK_MON_THRESH, 0x0}, {MAX98363_R2023_SPK_MON_DURATION, 0x0}, @@ -28,7 +23,6 @@ static struct reg_default max98363_reg[] = { {MAX98363_R2040_AMP_VOL, 0x0}, {MAX98363_R2041_AMP_GAIN, 0x5}, {MAX98363_R2042_DSP_CFG, 0x0}, - {MAX98363_R21FF_REV_ID, 0x0}, }; static bool max98363_readable_register(struct device *dev, unsigned int reg) diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c index bac9d1bcf60a..f0e49179c38f 100644 --- a/sound/soc/codecs/max98371.c +++ b/sound/soc/codecs/max98371.c @@ -419,7 +419,7 @@ static struct i2c_driver max98371_i2c_driver = { .name = "max98371", .of_match_table = of_match_ptr(max98371_of_match), }, - .probe_new = max98371_i2c_probe, + .probe = max98371_i2c_probe, .id_table = max98371_i2c_id, }; diff --git a/sound/soc/codecs/max98373-i2c.c b/sound/soc/codecs/max98373-i2c.c index ec0905df65d1..0fa5ceca62a2 100644 --- a/sound/soc/codecs/max98373-i2c.c +++ b/sound/soc/codecs/max98373-i2c.c @@ -9,7 +9,7 @@ #include <linux/mod_devicetable.h> #include <linux/of.h> #include <linux/of_gpio.h> -#include <linux/pm_runtime.h> +#include <linux/pm.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/cdev.h> @@ -624,7 +624,7 @@ static struct i2c_driver max98373_i2c_driver = { .acpi_match_table = ACPI_PTR(max98373_acpi_match), .pm = &max98373_pm, }, - .probe_new = max98373_i2c_probe, + .probe = max98373_i2c_probe, .id_table = max98373_i2c_id, }; diff --git a/sound/soc/codecs/max98388.c b/sound/soc/codecs/max98388.c new file mode 100644 index 000000000000..cde5e85946cb --- /dev/null +++ b/sound/soc/codecs/max98388.c @@ -0,0 +1,1013 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2022, Analog Devices Inc. + +#include <linux/acpi.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/cdev.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include "max98388.h" + +static struct reg_default max98388_reg[] = { + {MAX98388_R2000_SW_RESET, 0x00}, + {MAX98388_R2001_INT_RAW1, 0x00}, + {MAX98388_R2002_INT_RAW2, 0x00}, + {MAX98388_R2004_INT_STATE1, 0x00}, + {MAX98388_R2005_INT_STATE2, 0x00}, + {MAX98388_R2020_THERM_WARN_THRESH, 0x0A}, + {MAX98388_R2031_SPK_MON_THRESH, 0x58}, + {MAX98388_R2032_SPK_MON_LD_SEL, 0x08}, + {MAX98388_R2033_SPK_MON_DURATION, 0x02}, + {MAX98388_R2037_ERR_MON_CTRL, 0x01}, + {MAX98388_R2040_PCM_MODE_CFG, 0xC0}, + {MAX98388_R2041_PCM_CLK_SETUP, 0x04}, + {MAX98388_R2042_PCM_SR_SETUP, 0x88}, + {MAX98388_R2044_PCM_TX_CTRL1, 0x00}, + {MAX98388_R2045_PCM_TX_CTRL2, 0x00}, + {MAX98388_R2050_PCM_TX_HIZ_CTRL1, 0xFF}, + {MAX98388_R2051_PCM_TX_HIZ_CTRL2, 0xFF}, + {MAX98388_R2052_PCM_TX_HIZ_CTRL3, 0xFF}, + {MAX98388_R2053_PCM_TX_HIZ_CTRL4, 0xFF}, + {MAX98388_R2054_PCM_TX_HIZ_CTRL5, 0xFF}, + {MAX98388_R2055_PCM_TX_HIZ_CTRL6, 0xFF}, + {MAX98388_R2056_PCM_TX_HIZ_CTRL7, 0xFF}, + {MAX98388_R2057_PCM_TX_HIZ_CTRL8, 0xFF}, + {MAX98388_R2058_PCM_RX_SRC1, 0x00}, + {MAX98388_R2059_PCM_RX_SRC2, 0x01}, + {MAX98388_R205C_PCM_TX_DRIVE_STRENGTH, 0x00}, + {MAX98388_R205D_PCM_TX_SRC_EN, 0x00}, + {MAX98388_R205E_PCM_RX_EN, 0x00}, + {MAX98388_R205F_PCM_TX_EN, 0x00}, + {MAX98388_R2090_SPK_CH_VOL_CTRL, 0x00}, + {MAX98388_R2091_SPK_CH_CFG, 0x02}, + {MAX98388_R2092_SPK_AMP_OUT_CFG, 0x03}, + {MAX98388_R2093_SPK_AMP_SSM_CFG, 0x01}, + {MAX98388_R2094_SPK_AMP_ER_CTRL, 0x00}, + {MAX98388_R209E_SPK_CH_PINK_NOISE_EN, 0x00}, + {MAX98388_R209F_SPK_CH_AMP_EN, 0x00}, + {MAX98388_R20A0_IV_DATA_DSP_CTRL, 0x10}, + {MAX98388_R20A7_IV_DATA_EN, 0x00}, + {MAX98388_R20E0_BP_ALC_THRESH, 0x04}, + {MAX98388_R20E1_BP_ALC_RATES, 0x20}, + {MAX98388_R20E2_BP_ALC_ATTEN, 0x06}, + {MAX98388_R20E3_BP_ALC_REL, 0x02}, + {MAX98388_R20E4_BP_ALC_MUTE, 0x33}, + {MAX98388_R20EE_BP_INF_HOLD_REL, 0x00}, + {MAX98388_R20EF_BP_ALC_EN, 0x00}, + {MAX98388_R210E_AUTO_RESTART, 0x00}, + {MAX98388_R210F_GLOBAL_EN, 0x00}, + {MAX98388_R22FF_REV_ID, 0x00}, +}; + +static int max98388_dac_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + max98388->tdm_mode = false; + break; + default: + return 0; + } + return 0; +} + +static const char * const max98388_monomix_switch_text[] = { + "Left", "Right", "LeftRight"}; + +static const struct soc_enum dai_sel_enum = + SOC_ENUM_SINGLE(MAX98388_R2058_PCM_RX_SRC1, + MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT, + 3, max98388_monomix_switch_text); + +static const struct snd_kcontrol_new max98388_dai_controls = + SOC_DAPM_ENUM("DAI Sel", dai_sel_enum); + +static const struct snd_kcontrol_new max98388_vi_control = + SOC_DAPM_SINGLE("Switch", MAX98388_R205F_PCM_TX_EN, 0, 1, 0); + +static const struct snd_soc_dapm_widget max98388_dapm_widgets[] = { + SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", + MAX98388_R205E_PCM_RX_EN, 0, 0, max98388_dac_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0, + &max98388_dai_controls), + SND_SOC_DAPM_OUTPUT("BE_OUT"), + SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0, + MAX98388_R20A7_IV_DATA_EN, 1, 0), + SND_SOC_DAPM_ADC("ADC Voltage", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 0, 0), + SND_SOC_DAPM_ADC("ADC Current", NULL, + MAX98388_R205D_PCM_TX_SRC_EN, 1, 0), + SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0, + &max98388_vi_control), + SND_SOC_DAPM_SIGGEN("VMON"), + SND_SOC_DAPM_SIGGEN("IMON"), +}; + +static DECLARE_TLV_DB_SCALE(max98388_digital_tlv, -6350, 50, 1); +static DECLARE_TLV_DB_SCALE(max98388_amp_gain_tlv, -300, 300, 0); + +static const char * const max98388_alc_max_atten_text[] = { + "0dBFS", "-1dBFS", "-2dBFS", "-3dBFS", "-4dBFS", "-5dBFS", + "-6dBFS", "-7dBFS", "-8dBFS", "-9dBFS", "-10dBFS", "-11dBFS", + "-12dBFS", "-13dBFS", "-14dBFS", "-15dBFS" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_max_atten_enum, + MAX98388_R20E2_BP_ALC_ATTEN, + MAX98388_ALC_MAX_ATTEN_SHIFT, + max98388_alc_max_atten_text); + +static const char * const max98388_thermal_warn_text[] = { + "95C", "105C", "115C", "125C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_warning_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_WARN_THRESH_SHIFT, + max98388_thermal_warn_text); + +static const char * const max98388_thermal_shutdown_text[] = { + "135C", "145C", "155C", "165C" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_thermal_shutdown_thresh_enum, + MAX98388_R2020_THERM_WARN_THRESH, + MAX98388_THERM_SHDN_THRESH_SHIFT, + max98388_thermal_shutdown_text); + +static const char * const max98388_alc_thresh_single_text[] = { + "3.625V", "3.550V", "3.475V", "3.400V", "3.325V", "3.250V", + "3.175V", "3.100V", "3.025V", "2.950V", "2.875V", "2.800V", + "2.725V", "2.650V", "2.575V", "2.500V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_thresh_single_enum, + MAX98388_R20E0_BP_ALC_THRESH, + MAX98388_ALC_THRESH_SHIFT, + max98388_alc_thresh_single_text); + +static const char * const max98388_alc_attack_rate_text[] = { + "0", "10us", "20us", "40us", "80us", "160us", + "320us", "640us", "1.28ms", "2.56ms", "5.12ms", "10.24ms", + "20.48ms", "40.96ms", "81.92ms", "163.84ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_attack_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_ATTACK_RATE_SHIFT, + max98388_alc_attack_rate_text); + +static const char * const max98388_alc_release_rate_text[] = { + "20us", "40us", "80us", "160us", "320us", "640us", + "1.28ms", "2.56ms", "5.12ms", "10.24ms", "20.48ms", "40.96ms", + "81.92ms", "163.84ms", "327.68ms", "655.36ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_release_rate_enum, + MAX98388_R20E1_BP_ALC_RATES, + MAX98388_ALC_RELEASE_RATE_SHIFT, + max98388_alc_release_rate_text); + +static const char * const max98388_alc_debounce_text[] = { + "0.01ms", "0.1ms", "1ms", "10ms", "100ms", "250ms", "500ms", "hold" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_debouce_enum, + MAX98388_R20E3_BP_ALC_REL, + MAX98388_ALC_DEBOUNCE_TIME_SHIFT, + max98388_alc_debounce_text); + +static const char * const max98388_alc_mute_delay_text[] = { + "0.01ms", "0.05ms", "0.1ms", "0.5ms", "1ms", "5ms", "25ms", "250ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_alc_mute_delay_enum, + MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_DELAY_SHIFT, + max98388_alc_mute_delay_text); + +static const char * const max98388_spkmon_duration_text[] = { + "10ms", "25ms", "50ms", "75ms", "100ms", "200ms", "300ms", "400ms", + "500ms", "600ms", "700ms", "800ms", "900ms", "1000ms", "1100ms", "1200ms" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_duration_enum, + MAX98388_R2033_SPK_MON_DURATION, + MAX98388_SPKMON_DURATION_SHIFT, + max98388_spkmon_duration_text); + +static const char * const max98388_spkmon_thresh_text[] = { + "0.03V", "0.06V", "0.09V", "0.12V", "0.15V", "0.18V", "0.20V", "0.23V", + "0.26V", "0.29V", "0.32V", "0.35V", "0.38V", "0.41V", "0.44V", "0.47V", + "0.50V", "0.53V", "0.56V", "0.58V", "0.61V", "0.64V", "0.67V", "0.70V", + "0.73V", "0.76V", "0.79V", "0.82V", "0.85V", "0.88V", "0.91V", "0.94V", + "0.96V", "0.99V", "1.02V", "1.05V", "1.08V", "1.11V", "1.14V", "1.17V", + "1.20V", "1.23V", "1.26V", "1.29V", "1.32V", "1.35V", "1.37V", "1.40V", + "1.43V", "1.46V", "1.49V", "1.52V", "1.55V", "1.58V", "1.61V", "1.64V", + "1.67V", "1.70V", "1.73V", "1.75V", "1.78V", "1.81V", "1.84V", "1.87V", + "1.90V", "1.93V", "1.96V", "1.99V", "2.02V", "2.05V", "2.08V", "2.11V", + "2.13V", "2.16V", "2.19V", "2.22V", "2.25V", "2.28V", "2.31V", "2.34V", + "2.37V", "2.40V", "2.43V", "2.46V", "2.49V", "2.51V", "2.54V", "2.57V", + "2.60V", "2.63V", "2.66V", "2.69V", "2.72V", "2.75V", "2.78V", "2.81V", + "2.84V", "2.87V", "2.89V", "2.92V", "2.95V", "2.98V", "3.01V", "3.04V", + "3.07V", "3.10V", "3.13V", "3.16V", "3.19V", "3.22V", "3.25V", "3.27V", + "3.30V", "3.33V", "3.36V", "3.39V", "3.42V", "3.45V", "3.48V", "3.51V", + "3.54V", "3.57V", "3.60V", "3.63V", "3.66V", "3.68V", "3.71V", "3.74V" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_thresh_enum, + MAX98388_R2031_SPK_MON_THRESH, + MAX98388_SPKMON_THRESH_SHIFT, + max98388_spkmon_thresh_text); + +static const char * const max98388_spkmon_load_text[] = { + "2.00ohm", "2.25ohm", "2.50ohm", "2.75ohm", "3.00ohm", "3.25ohm", + "3.50ohm", "3.75ohm", "4.00ohm", "4.25ohm", "4.50ohm", "4.75ohm", + "5.00ohm", "5.25ohm", "5.50ohm", "5.75ohm", "6.00ohm", "6.25ohm", + "6.50ohm", "6.75ohm", "7.00ohm", "7.25ohm", "7.50ohm", "7.75ohm", + "8.00ohm", "8.25ohm", "8.50ohm", "8.75ohm", "9.00ohm", "9.25ohm", + "9.50ohm", "9.75ohm", "10.00ohm", "10.25ohm", "10.50ohm", "10.75ohm", + "11.00ohm", "11.25ohm", "11.50ohm", "11.75ohm", "12.00ohm", "12.25ohm", + "12.50ohm", "12.75ohm", "13.00ohm", "13.25ohm", "13.50ohm", "13.75ohm", + "14.00ohm", "14.25ohm", "14.50ohm", "14.75ohm", "15.00ohm", "15.25ohm", + "15.50ohm", "15.75ohm", "16.00ohm", "16.25ohm", "16.50ohm", "16.75ohm", + "17.00ohm", "17.25ohm", "17.50ohm", "17.75ohm", "18.00ohm", "18.25ohm", + "18.50ohm", "18.75ohm", "19.00ohm", "19.25ohm", "19.50ohm", "19.75ohm", + "20.00ohm", "20.25ohm", "20.50ohm", "20.75ohm", "21.00ohm", "21.25ohm", + "21.50ohm", "21.75ohm", "22.00ohm", "22.25ohm", "22.50ohm", "22.75ohm", + "23.00ohm", "23.25ohm", "23.50ohm", "23.75ohm", "24.00ohm", "24.25ohm", + "24.50ohm", "24.75ohm", "25.00ohm", "25.25ohm", "25.50ohm", "25.75ohm", + "26.00ohm", "26.25ohm", "26.50ohm", "26.75ohm", "27.00ohm", "27.25ohm", + "27.50ohm", "27.75ohm", "28.00ohm", "28.25ohm", "28.50ohm", "28.75ohm", + "29.00ohm", "29.25ohm", "29.50ohm", "29.75ohm", "30.00ohm", "30.25ohm", + "30.50ohm", "30.75ohm", "31.00ohm", "31.25ohm", "31.50ohm", "31.75ohm", + "32.00ohm", "32.25ohm", "32.50ohm", "32.75ohm", "33.00ohm", "33.25ohm", + "33.50ohm", "33.75ohm" +}; + +static SOC_ENUM_SINGLE_DECL(max98388_spkmon_load_enum, + MAX98388_R2032_SPK_MON_LD_SEL, + MAX98388_SPKMON_LOAD_SHIFT, + max98388_spkmon_load_text); + +static const char * const max98388_edge_rate_text[] = { + "Normal", "Reduced", "Maximum", "Increased", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_falling_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_FALL_SHIFT, + max98388_edge_rate_text); + +static SOC_ENUM_SINGLE_DECL(max98388_edge_rate_rising_enum, + MAX98388_R2094_SPK_AMP_ER_CTRL, + MAX98388_EDGE_RATE_RISE_SHIFT, + max98388_edge_rate_text); + +static const char * const max98388_ssm_mod_text[] = { + "1.5%", "3.0%", "4.5%", "6.0%", +}; + +static SOC_ENUM_SINGLE_DECL(max98388_ssm_mod_enum, + MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_MOD_SHIFT, + max98388_ssm_mod_text); + +static const struct snd_kcontrol_new max98388_snd_controls[] = { + SOC_SINGLE("Ramp Up Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPUP_SHIFT, 1, 0), + SOC_SINGLE("Ramp Down Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_VOL_RMPDN_SHIFT, 1, 0), + /* Two Cell Mode Enable */ + SOC_SINGLE("OP Mode Switch", MAX98388_R2092_SPK_AMP_OUT_CFG, + MAX98388_SPK_AMP_OUT_MODE_SHIFT, 1, 0), + /* Speaker Amplifier Overcurrent Automatic Restart Enable */ + SOC_SINGLE("OVC Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_OVC_AUTORESTART_SHIFT, 1, 0), + /* Thermal Shutdown Automatic Restart Enable */ + SOC_SINGLE("THERM Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_THERM_AUTORESTART_SHIFT, 1, 0), + /* PVDD UVLO Auto Restart */ + SOC_SINGLE("UVLO Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_PVDD_UVLO_AUTORESTART_SHIFT, 1, 0), + /* Clock Monitor Automatic Restart Enable */ + SOC_SINGLE("CMON Autorestart Switch", MAX98388_R210E_AUTO_RESTART, + MAX98388_CMON_AUTORESTART_SHIFT, 1, 0), + SOC_SINGLE("CLK Monitor Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_CLOCK_MON_SHIFT, 1, 0), + /* Pinknoise Generator Enable */ + SOC_SINGLE("Pinknoise Gen Switch", MAX98388_R209E_SPK_CH_PINK_NOISE_EN, + MAX98388_PINK_NOISE_GEN_SHIFT, 1, 0), + /* Dither Enable */ + SOC_SINGLE("Dither Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DITH_EN_SHIFT, 1, 0), + SOC_SINGLE("VI Dither Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_DITH_SHIFT, 1, 0), + /* DC Blocker Enable */ + SOC_SINGLE("DC Blocker Switch", MAX98388_R2091_SPK_CH_CFG, + MAX98388_SPK_CFG_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Voltage DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT, 1, 0), + SOC_SINGLE("Current DC Blocker Switch", MAX98388_R20A0_IV_DATA_DSP_CTRL, + MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT, 1, 0), + /* Digital Volume */ + SOC_SINGLE_TLV("Digital Volume", MAX98388_R2090_SPK_CH_VOL_CTRL, + 0, 0x7F, 1, max98388_digital_tlv), + /* Speaker Volume */ + SOC_SINGLE_TLV("Speaker Volume", MAX98388_R2092_SPK_AMP_OUT_CFG, + 0, 5, 0, max98388_amp_gain_tlv), + SOC_ENUM("Thermal Warn Thresh", max98388_thermal_warning_thresh_enum), + SOC_ENUM("Thermal SHDN Thresh", max98388_thermal_shutdown_thresh_enum), + /* Brownout Protection Automatic Level Control */ + SOC_SINGLE("ALC Switch", MAX98388_R20EF_BP_ALC_EN, 0, 1, 0), + SOC_ENUM("ALC Thresh", max98388_alc_thresh_single_enum), + SOC_ENUM("ALC Attack Rate", max98388_alc_attack_rate_enum), + SOC_ENUM("ALC Release Rate", max98388_alc_release_rate_enum), + SOC_ENUM("ALC Max Atten", max98388_alc_max_atten_enum), + SOC_ENUM("ALC Debounce Time", max98388_alc_debouce_enum), + SOC_SINGLE("ALC Unmute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Ramp Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_RAMP_EN_SHIFT, 1, 0), + SOC_SINGLE("ALC Mute Switch", MAX98388_R20E4_BP_ALC_MUTE, + MAX98388_ALC_MUTE_EN_SHIFT, 1, 0), + SOC_ENUM("ALC Mute Delay", max98388_alc_mute_delay_enum), + /* Speaker Monitor */ + SOC_SINGLE("SPKMON Switch", MAX98388_R2037_ERR_MON_CTRL, + MAX98388_SPK_MON_SHIFT, 1, 0), + SOC_ENUM("SPKMON Thresh", max98388_spkmon_thresh_enum), + SOC_ENUM("SPKMON Load", max98388_spkmon_load_enum), + SOC_ENUM("SPKMON Duration", max98388_spkmon_duration_enum), + /* General Parameters */ + SOC_ENUM("Fall Slew Rate", max98388_edge_rate_falling_enum), + SOC_ENUM("Rise Slew Rate", max98388_edge_rate_rising_enum), + SOC_SINGLE("AMP SSM Switch", MAX98388_R2093_SPK_AMP_SSM_CFG, + MAX98388_SPK_AMP_SSM_EN_SHIFT, 1, 0), + SOC_ENUM("AMP SSM Mod", max98388_ssm_mod_enum), +}; + +static const struct snd_soc_dapm_route max98388_audio_map[] = { + /* Plabyack */ + {"DAI Sel Mux", "Left", "Amp Enable"}, + {"DAI Sel Mux", "Right", "Amp Enable"}, + {"DAI Sel Mux", "LeftRight", "Amp Enable"}, + {"BE_OUT", NULL, "DAI Sel Mux"}, + /* Capture */ + { "ADC Voltage", NULL, "VMON"}, + { "ADC Current", NULL, "IMON"}, + { "VI Sense", "Switch", "ADC Voltage"}, + { "VI Sense", "Switch", "ADC Current"}, + { "Voltage Sense", NULL, "VI Sense"}, + { "Current Sense", NULL, "VI Sense"}, +}; + +static void max98388_reset(struct max98388_priv *max98388, struct device *dev) +{ + int ret, reg, count; + + /* Software Reset */ + ret = regmap_update_bits(max98388->regmap, + MAX98388_R2000_SW_RESET, + MAX98388_SOFT_RESET, + MAX98388_SOFT_RESET); + if (ret) + dev_err(dev, "Reset command failed. (ret:%d)\n", ret); + + count = 0; + while (count < 3) { + usleep_range(10000, 11000); + /* Software Reset Verification */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (!ret) { + dev_info(dev, "Reset completed (retry:%d)\n", count); + return; + } + count++; + } + dev_err(dev, "Reset failed. (ret:%d)\n", ret); +} + +static int max98388_probe(struct snd_soc_component *component) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + + /* Software Reset */ + max98388_reset(max98388, component->dev); + + /* General channel source configuration */ + regmap_write(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + 0x10); + + /* Enable DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R2091_SPK_CH_CFG, + 0x1); + /* Enable IMON VMON DC blocker */ + regmap_write(max98388->regmap, + MAX98388_R20A0_IV_DATA_DSP_CTRL, + 0x3); + /* TX slot configuration */ + regmap_write(max98388->regmap, + MAX98388_R2044_PCM_TX_CTRL1, + max98388->v_slot); + + regmap_write(max98388->regmap, + MAX98388_R2045_PCM_TX_CTRL2, + max98388->i_slot); + /* Enable Auto-restart behavior by default */ + regmap_write(max98388->regmap, + MAX98388_R210E_AUTO_RESTART, 0xF); + /* Set interleave mode */ + if (max98388->interleave_mode) + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK, + MAX98388_PCM_TX_CH_INTERLEAVE_MASK); + + /* Speaker Amplifier Channel Enable */ + regmap_update_bits(max98388->regmap, + MAX98388_R209F_SPK_CH_AMP_EN, + MAX98388_SPK_EN_MASK, 1); + + return 0; +} + +static int max98388_dai_set_fmt(struct snd_soc_dai *codec_dai, + unsigned int fmt) +{ + struct snd_soc_component *component = codec_dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int format = 0; + unsigned int invert = 0; + + dev_dbg(component->dev, "%s: fmt 0x%08X\n", __func__, fmt); + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_NF: + invert = MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE; + break; + default: + dev_err(component->dev, "DAI invert mode unsupported\n"); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE, + invert); + + /* interface format */ + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + format = MAX98388_PCM_FORMAT_I2S; + break; + case SND_SOC_DAIFMT_LEFT_J: + format = MAX98388_PCM_FORMAT_LJ; + break; + case SND_SOC_DAIFMT_DSP_A: + format = MAX98388_PCM_FORMAT_TDM_MODE1; + break; + case SND_SOC_DAIFMT_DSP_B: + format = MAX98388_PCM_FORMAT_TDM_MODE0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_FORMAT_MASK, + format << MAX98388_PCM_MODE_CFG_FORMAT_SHIFT); + + return 0; +} + +/* BCLKs per LRCLK */ +static const int bclk_sel_table[] = { + 32, 48, 64, 96, 128, 192, 256, 384, 512, 320, +}; + +static int max98388_get_bclk_sel(int bclk) +{ + int i; + /* match BCLKs per LRCLK */ + for (i = 0; i < ARRAY_SIZE(bclk_sel_table); i++) { + if (bclk_sel_table[i] == bclk) + return i + 2; + } + return 0; +} + +static int max98388_set_clock(struct snd_soc_component *component, + struct snd_pcm_hw_params *params) +{ + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + /* BCLK/LRCLK ratio calculation */ + int blr_clk_ratio = params_channels(params) * max98388->ch_size; + int value; + + if (!max98388->tdm_mode) { + /* BCLK configuration */ + value = max98388_get_bclk_sel(blr_clk_ratio); + if (!value) { + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + value); + } + return 0; +} + +static int max98388_dai_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + unsigned int sampling_rate = 0; + unsigned int chan_sz = 0; + int ret, reg; + int status = 0; + + /* pcm mode configuration */ + switch (snd_pcm_format_width(params_format(params))) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + params_format(params)); + goto err; + } + + max98388->ch_size = snd_pcm_format_width(params_format(params)); + + ret = regmap_read(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, ®); + if (ret < 0) + goto err; + + /* GLOBAL_EN OFF prior to the channel size re-configure */ + if (chan_sz != (reg & MAX98388_PCM_MODE_CFG_CHANSZ_MASK)) { + ret = regmap_read(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, &status); + if (ret < 0) + goto err; + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 0); + usleep_range(30000, 31000); + } + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + } + dev_dbg(component->dev, "format supported %d", + params_format(params)); + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 8000: + sampling_rate = MAX98388_PCM_SR_8000; + break; + case 11025: + sampling_rate = MAX98388_PCM_SR_11025; + break; + case 12000: + sampling_rate = MAX98388_PCM_SR_12000; + break; + case 16000: + sampling_rate = MAX98388_PCM_SR_16000; + break; + case 22050: + sampling_rate = MAX98388_PCM_SR_22050; + break; + case 24000: + sampling_rate = MAX98388_PCM_SR_24000; + break; + case 32000: + sampling_rate = MAX98388_PCM_SR_32000; + break; + case 44100: + sampling_rate = MAX98388_PCM_SR_44100; + break; + case 48000: + sampling_rate = MAX98388_PCM_SR_48000; + break; + case 88200: + sampling_rate = MAX98388_PCM_SR_88200; + break; + case 96000: + sampling_rate = MAX98388_PCM_SR_96000; + break; + default: + dev_err(component->dev, "rate %d not supported\n", + params_rate(params)); + goto err; + } + + /* set DAI_SR to correct LRCLK frequency */ + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_MASK, + sampling_rate); + + /* set sampling rate of IV */ + if (max98388->interleave_mode && + sampling_rate > MAX98388_PCM_SR_16000) + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + (sampling_rate - 3) << MAX98388_PCM_SR_IV_SHIFT); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2042_PCM_SR_SETUP, + MAX98388_PCM_SR_IV_MASK, + sampling_rate << MAX98388_PCM_SR_IV_SHIFT); + + ret = max98388_set_clock(component, params); + + if (status) { + regmap_write(max98388->regmap, + MAX98388_R210F_GLOBAL_EN, 1); + usleep_range(30000, 31000); + } + + return ret; + +err: + return -EINVAL; +} + +#define MAX_NUM_SLOTS 16 +#define MAX_NUM_CH 2 + +static int max98388_dai_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct max98388_priv *max98388 = snd_soc_component_get_drvdata(component); + int bsel = 0; + unsigned int chan_sz = 0; + unsigned int mask; + int cnt, slot_found; + int addr, bits; + + if (!tx_mask && !rx_mask && !slots && !slot_width) + max98388->tdm_mode = false; + else + max98388->tdm_mode = true; + + /* BCLK configuration */ + bsel = max98388_get_bclk_sel(slots * slot_width); + if (bsel == 0) { + dev_err(component->dev, "BCLK %d not supported\n", + slots * slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2041_PCM_CLK_SETUP, + MAX98388_PCM_CLK_SETUP_BSEL_MASK, + bsel); + + /* Channel size configuration */ + switch (slot_width) { + case 16: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_16; + break; + case 24: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_24; + break; + case 32: + chan_sz = MAX98388_PCM_MODE_CFG_CHANSZ_32; + break; + default: + dev_err(component->dev, "format unsupported %d\n", + slot_width); + return -EINVAL; + } + + regmap_update_bits(max98388->regmap, + MAX98388_R2040_PCM_MODE_CFG, + MAX98388_PCM_MODE_CFG_CHANSZ_MASK, chan_sz); + + /* Rx slot configuration */ + slot_found = 0; + mask = rx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + if (slot_found == 0) + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH0_SHIFT, + cnt); + else + regmap_update_bits(max98388->regmap, + MAX98388_R2059_PCM_RX_SRC2, + MAX98388_RX_SRC_CH1_SHIFT, + cnt); + slot_found++; + if (slot_found >= MAX_NUM_CH) + break; + } + } + + /* speaker feedback slot configuration */ + slot_found = 0; + mask = tx_mask; + for (cnt = 0 ; cnt < MAX_NUM_SLOTS ; cnt++, mask >>= 1) { + if (mask & 0x1) { + addr = MAX98388_R2044_PCM_TX_CTRL1 + (cnt / 8); + bits = cnt % 8; + regmap_update_bits(max98388->regmap, addr, bits, bits); + if (slot_found >= MAX_NUM_CH) + break; + } + } + + return 0; +} + +#define MAX98388_RATES SNDRV_PCM_RATE_8000_96000 + +#define MAX98388_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops max98388_dai_ops = { + .set_fmt = max98388_dai_set_fmt, + .hw_params = max98388_dai_hw_params, + .set_tdm_slot = max98388_dai_tdm_slot, +}; + +static bool max98388_readable_register(struct device *dev, + unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2002_INT_RAW2: + case MAX98388_R2004_INT_STATE1... MAX98388_R2005_INT_STATE2: + case MAX98388_R2020_THERM_WARN_THRESH: + case MAX98388_R2031_SPK_MON_THRESH + ... MAX98388_R2033_SPK_MON_DURATION: + case MAX98388_R2037_ERR_MON_CTRL: + case MAX98388_R2040_PCM_MODE_CFG + ... MAX98388_R2042_PCM_SR_SETUP: + case MAX98388_R2044_PCM_TX_CTRL1 + ... MAX98388_R2045_PCM_TX_CTRL2: + case MAX98388_R2050_PCM_TX_HIZ_CTRL1 + ... MAX98388_R2059_PCM_RX_SRC2: + case MAX98388_R205C_PCM_TX_DRIVE_STRENGTH + ... MAX98388_R205F_PCM_TX_EN: + case MAX98388_R2090_SPK_CH_VOL_CTRL + ... MAX98388_R2094_SPK_AMP_ER_CTRL: + case MAX98388_R209E_SPK_CH_PINK_NOISE_EN + ... MAX98388_R209F_SPK_CH_AMP_EN: + case MAX98388_R20A0_IV_DATA_DSP_CTRL: + case MAX98388_R20A7_IV_DATA_EN: + case MAX98388_R20E0_BP_ALC_THRESH ... MAX98388_R20E4_BP_ALC_MUTE: + case MAX98388_R20EE_BP_INF_HOLD_REL ... MAX98388_R20EF_BP_ALC_EN: + case MAX98388_R210E_AUTO_RESTART: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +}; + +static bool max98388_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case MAX98388_R2001_INT_RAW1 ... MAX98388_R2005_INT_STATE2: + case MAX98388_R210F_GLOBAL_EN: + case MAX98388_R22FF_REV_ID: + return true; + default: + return false; + } +} + +static struct snd_soc_dai_driver max98388_dai[] = { + { + .name = "max98388-aif1", + .playback = { + .stream_name = "HiFi Playback", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .capture = { + .stream_name = "HiFi Capture", + .channels_min = 1, + .channels_max = 2, + .rates = MAX98388_RATES, + .formats = MAX98388_FORMATS, + }, + .ops = &max98388_dai_ops, + } +}; + +static int max98388_suspend(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, true); + regcache_mark_dirty(max98388->regmap); + + return 0; +} + +static int max98388_resume(struct device *dev) +{ + struct max98388_priv *max98388 = dev_get_drvdata(dev); + + regcache_cache_only(max98388->regmap, false); + max98388_reset(max98388, dev); + regcache_sync(max98388->regmap); + + return 0; +} + +static const struct dev_pm_ops max98388_pm = { + SYSTEM_SLEEP_PM_OPS(max98388_suspend, max98388_resume) +}; + +static const struct regmap_config max98388_regmap = { + .reg_bits = 16, + .val_bits = 8, + .max_register = MAX98388_R22FF_REV_ID, + .reg_defaults = max98388_reg, + .num_reg_defaults = ARRAY_SIZE(max98388_reg), + .readable_reg = max98388_readable_register, + .volatile_reg = max98388_volatile_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct snd_soc_component_driver soc_codec_dev_max98388 = { + .probe = max98388_probe, + .controls = max98388_snd_controls, + .num_controls = ARRAY_SIZE(max98388_snd_controls), + .dapm_widgets = max98388_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(max98388_dapm_widgets), + .dapm_routes = max98388_audio_map, + .num_dapm_routes = ARRAY_SIZE(max98388_audio_map), + .use_pmdown_time = 1, + .endianness = 1, +}; + +static void max98388_read_deveice_property(struct device *dev, + struct max98388_priv *max98388) +{ + int value; + + if (!device_property_read_u32(dev, "adi,vmon-slot-no", &value)) + max98388->v_slot = value & 0xF; + else + max98388->v_slot = 0; + + if (!device_property_read_u32(dev, "adi,imon-slot-no", &value)) + max98388->i_slot = value & 0xF; + else + max98388->i_slot = 1; + + if (device_property_read_bool(dev, "adi,interleave-mode")) + max98388->interleave_mode = true; + else + max98388->interleave_mode = false; +} + +static int max98388_i2c_probe(struct i2c_client *i2c) +{ + int ret = 0; + int reg = 0; + + struct max98388_priv *max98388 = NULL; + + max98388 = devm_kzalloc(&i2c->dev, sizeof(*max98388), GFP_KERNEL); + if (!max98388) + return -ENOMEM; + + i2c_set_clientdata(i2c, max98388); + + /* regmap initialization */ + max98388->regmap = devm_regmap_init_i2c(i2c, &max98388_regmap); + if (IS_ERR(max98388->regmap)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->regmap), + "Failed to allocate register map.\n"); + + /* voltage/current slot & gpio configuration */ + max98388_read_deveice_property(&i2c->dev, max98388); + + /* Device Reset */ + max98388->reset_gpio = devm_gpiod_get_optional(&i2c->dev, + "reset", GPIOD_OUT_HIGH); + if (IS_ERR(max98388->reset_gpio)) + return dev_err_probe(&i2c->dev, PTR_ERR(max98388->reset_gpio), + "Unable to request GPIO\n"); + + if (max98388->reset_gpio) { + usleep_range(5000, 6000); + gpiod_set_value_cansleep(max98388->reset_gpio, 0); + /* Wait for the hw reset done */ + usleep_range(5000, 6000); + } + + /* Read Revision ID */ + ret = regmap_read(max98388->regmap, + MAX98388_R22FF_REV_ID, ®); + if (ret < 0) + return dev_err_probe(&i2c->dev, ret, + "Failed to read the revision ID\n"); + + dev_info(&i2c->dev, "MAX98388 revisionID: 0x%02X\n", reg); + + /* codec registration */ + ret = devm_snd_soc_register_component(&i2c->dev, + &soc_codec_dev_max98388, + max98388_dai, + ARRAY_SIZE(max98388_dai)); + if (ret < 0) + dev_err(&i2c->dev, "Failed to register codec: %d\n", ret); + + return ret; +} + +static const struct i2c_device_id max98388_i2c_id[] = { + { "max98388", 0}, + { }, +}; + +MODULE_DEVICE_TABLE(i2c, max98388_i2c_id); + +static const struct of_device_id max98388_of_match[] = { + { .compatible = "adi,max98388", }, + { } +}; +MODULE_DEVICE_TABLE(of, max98388_of_match); + +static const struct acpi_device_id max98388_acpi_match[] = { + { "ADS8388", 0 }, + {}, +}; +MODULE_DEVICE_TABLE(acpi, max98388_acpi_match); + +static struct i2c_driver max98388_i2c_driver = { + .driver = { + .name = "max98388", + .of_match_table = max98388_of_match, + .acpi_match_table = max98388_acpi_match, + .pm = pm_sleep_ptr(&max98388_pm), + }, + .probe = max98388_i2c_probe, + .id_table = max98388_i2c_id, +}; + +module_i2c_driver(max98388_i2c_driver) + +MODULE_DESCRIPTION("ALSA SoC MAX98388 driver"); +MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/max98388.h b/sound/soc/codecs/max98388.h new file mode 100644 index 000000000000..77833d181913 --- /dev/null +++ b/sound/soc/codecs/max98388.h @@ -0,0 +1,234 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * max98388.h -- MAX98388 ALSA SoC audio driver header + * + * Copyright(c) 2022, Analog Devices Inc. + */ + +#ifndef _MAX98388_H +#define _MAX98388_H + +/* Device Status Registers */ +#define MAX98388_R2000_SW_RESET 0x2000 +#define MAX98388_R2001_INT_RAW1 0x2001 +#define MAX98388_R2002_INT_RAW2 0x2002 +#define MAX98388_R2004_INT_STATE1 0x2004 +#define MAX98388_R2005_INT_STATE2 0x2005 +/* Thermal Protection Registers */ +#define MAX98388_R2020_THERM_WARN_THRESH 0x2020 +/* Error Monitor */ +#define MAX98388_R2031_SPK_MON_THRESH 0x2031 +#define MAX98388_R2032_SPK_MON_LD_SEL 0x2032 +#define MAX98388_R2033_SPK_MON_DURATION 0x2033 +#define MAX98388_R2037_ERR_MON_CTRL 0x2037 +/* PCM Registers */ +#define MAX98388_R2040_PCM_MODE_CFG 0x2040 +#define MAX98388_R2041_PCM_CLK_SETUP 0x2041 +#define MAX98388_R2042_PCM_SR_SETUP 0x2042 +#define MAX98388_R2044_PCM_TX_CTRL1 0x2044 +#define MAX98388_R2045_PCM_TX_CTRL2 0x2045 +#define MAX98388_R2050_PCM_TX_HIZ_CTRL1 0x2050 +#define MAX98388_R2051_PCM_TX_HIZ_CTRL2 0x2051 +#define MAX98388_R2052_PCM_TX_HIZ_CTRL3 0x2052 +#define MAX98388_R2053_PCM_TX_HIZ_CTRL4 0x2053 +#define MAX98388_R2054_PCM_TX_HIZ_CTRL5 0x2054 +#define MAX98388_R2055_PCM_TX_HIZ_CTRL6 0x2055 +#define MAX98388_R2056_PCM_TX_HIZ_CTRL7 0x2056 +#define MAX98388_R2057_PCM_TX_HIZ_CTRL8 0x2057 +#define MAX98388_R2058_PCM_RX_SRC1 0x2058 +#define MAX98388_R2059_PCM_RX_SRC2 0x2059 +#define MAX98388_R205C_PCM_TX_DRIVE_STRENGTH 0x205C +#define MAX98388_R205D_PCM_TX_SRC_EN 0x205D +#define MAX98388_R205E_PCM_RX_EN 0x205E +#define MAX98388_R205F_PCM_TX_EN 0x205F +/* Speaker Channel Control */ +#define MAX98388_R2090_SPK_CH_VOL_CTRL 0x2090 +#define MAX98388_R2091_SPK_CH_CFG 0x2091 +#define MAX98388_R2092_SPK_AMP_OUT_CFG 0x2092 +#define MAX98388_R2093_SPK_AMP_SSM_CFG 0x2093 +#define MAX98388_R2094_SPK_AMP_ER_CTRL 0x2094 +#define MAX98388_R209E_SPK_CH_PINK_NOISE_EN 0x209E +#define MAX98388_R209F_SPK_CH_AMP_EN 0x209F +#define MAX98388_R20A0_IV_DATA_DSP_CTRL 0x20A0 +#define MAX98388_R20A7_IV_DATA_EN 0x20A7 +#define MAX98388_R20E0_BP_ALC_THRESH 0x20E0 +#define MAX98388_R20E1_BP_ALC_RATES 0x20E1 +#define MAX98388_R20E2_BP_ALC_ATTEN 0x20E2 +#define MAX98388_R20E3_BP_ALC_REL 0x20E3 +#define MAX98388_R20E4_BP_ALC_MUTE 0x20E4 +#define MAX98388_R20EE_BP_INF_HOLD_REL 0x20EE +#define MAX98388_R20EF_BP_ALC_EN 0x20EF +#define MAX98388_R210E_AUTO_RESTART 0x210E +#define MAX98388_R210F_GLOBAL_EN 0x210F +#define MAX98388_R22FF_REV_ID 0x22FF + +/* MAX98388_R2000_SW_RESET */ +#define MAX98388_SOFT_RESET (0x1 << 0) + +/* MAX98388_R2020_THERM_WARN_THRESH */ +#define MAX98388_THERM_SHDN_THRESH_SHIFT (0) +#define MAX98388_THERM_WARN_THRESH_SHIFT (2) + +/* MAX98388_R2022_PCM_TX_SRC_1 */ +#define MAX98388_PCM_TX_CH_SRC_A_V_SHIFT (0) +#define MAX98388_PCM_TX_CH_SRC_A_I_SHIFT (4) + +/* MAX98388_R2024_PCM_DATA_FMT_CFG */ +#define MAX98388_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3) +#define MAX98388_PCM_MODE_CFG_FORMAT_SHIFT (3) +#define MAX98388_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 2) +#define MAX98388_PCM_FORMAT_I2S (0x0 << 0) +#define MAX98388_PCM_FORMAT_LJ (0x1 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE0 (0x3 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE1 (0x4 << 0) +#define MAX98388_PCM_FORMAT_TDM_MODE2 (0x5 << 0) +#define MAX98388_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6) +#define MAX98388_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6) + +/* MAX98388_R2031_SPK_MON_THRESH */ +#define MAX98388_SPKMON_THRESH_SHIFT (0) + +/* MAX98388_R2032_SPK_MON_LD_SEL */ +#define MAX98388_SPKMON_LOAD_SHIFT (0) + +/* MAX98388_R2033_SPK_MON_DURATION */ +#define MAX98388_SPKMON_DURATION_SHIFT (0) + +/* MAX98388_R2037_ERR_MON_CTRL */ +#define MAX98388_CLOCK_MON_SHIFT (0) +#define MAX98388_SPK_MON_SHIFT (1) + +/* MAX98388_R203E_AMP_PATH_GAIN */ +#define MAX98388_SPK_DIGI_GAIN_MASK (0xF << 4) +#define MAX98388_SPK_DIGI_GAIN_SHIFT (4) +#define MAX98388_FS_GAIN_MAX_MASK (0xF << 0) +#define MAX98388_FS_GAIN_MAX_SHIFT (0) + +/* MAX98388_R2041_PCM_CLK_SETUP */ +#define MAX98388_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 4) +#define MAX98388_PCM_CLK_SETUP_BSEL_MASK (0xF << 0) + +/* MAX98388_R2042_PCM_SR_SETUP */ +#define MAX98388_PCM_SR_MASK (0xF << 0) +#define MAX98388_PCM_SR_IV_MASK (0xF << 4) +#define MAX98388_PCM_SR_IV_SHIFT (4) +#define MAX98388_PCM_SR_8000 (0x0 << 0) +#define MAX98388_PCM_SR_11025 (0x1 << 0) +#define MAX98388_PCM_SR_12000 (0x2 << 0) +#define MAX98388_PCM_SR_16000 (0x3 << 0) +#define MAX98388_PCM_SR_22050 (0x4 << 0) +#define MAX98388_PCM_SR_24000 (0x5 << 0) +#define MAX98388_PCM_SR_32000 (0x6 << 0) +#define MAX98388_PCM_SR_44100 (0x7 << 0) +#define MAX98388_PCM_SR_48000 (0x8 << 0) +#define MAX98388_PCM_SR_88200 (0x9 << 0) +#define MAX98388_PCM_SR_96000 (0xA << 0) + +/* MAX98388_R2043_AMP_EN */ +#define MAX98388_SPK_EN_MASK (0x1 << 0) +#define MAX98388_SPKFB_EN_MASK (0x1 << 1) +#define MAX98388_SPKFB_EN_SHIFT (1) + +/* MAX98388_R2052_MEAS_ADC_PVDD_FLT_CFG */ +#define MAX98388_FLT_EN_SHIFT (4) + +/* MAX98388_R2058_PCM_RX_SRC1 */ +#define MAX98388_PCM_TO_SPK_MONOMIX_CFG_SHIFT (0) + +/* MAX98388_R2059_PCM_RX_SRC2 */ +#define MAX98388_RX_SRC_CH0_SHIFT (0) +#define MAX98388_RX_SRC_CH1_SHIFT (4) + +/* MAX98388_R2091_SPK_CH_CFG */ +#define MAX98388_SPK_CFG_DCBLK_SHIFT (0) +#define MAX98388_SPK_CFG_DITH_EN_SHIFT (1) +#define MAX98388_SPK_CFG_INV_SHIFT (2) +#define MAX98388_SPK_CFG_VOL_RMPUP_SHIFT (3) +#define MAX98388_SPK_CFG_VOL_RMPDN_SHIFT (4) + +/* MAX98388_R2092_SPK_AMP_OUT_CFG */ +#define MAX98388_SPK_AMP_OUT_GAIN_SHIFT (0) +#define MAX98388_SPK_AMP_OUT_MODE_SHIFT (3) + +/* MAX98388_R2093_SPK_AMP_SSM_CFG */ +#define MAX98388_SPK_AMP_SSM_EN_SHIFT (0) +#define MAX98388_SPK_AMP_SSM_MOD_SHIFT (1) + +/* MAX98388_R2094_SPK_AMP_ER_CTRL */ +#define MAX98388_EDGE_RATE_RISE_SHIFT (0) +#define MAX98388_EDGE_RATE_FALL_SHIFT (2) + +/* MAX98388_R209E_SPK_CH_PINK_NOISE_EN */ +#define MAX98388_PINK_NOISE_GEN_SHIFT (0) + +/* MAX98388_R20A0_IV_DATA_DSP_CTRL */ +#define MAX98388_AMP_DSP_CTRL_VOL_DCBLK_SHIFT (0) +#define MAX98388_AMP_DSP_CTRL_CUR_DCBLK_SHIFT (1) +#define MAX98388_AMP_DSP_CTRL_VOL_INV_SHIFT (2) +#define MAX98388_AMP_DSP_CTRL_CUR_INV_SHIFT (3) +#define MAX98388_AMP_DSP_CTRL_DITH_SHIFT (4) + +/* MAX98388_R20B2_BDE_L4_CFG_2 */ +#define MAX98388_LVL4_HOLD_EN_SHIFT (6) +#define MAX98388_LVL4_MUTE_EN_SHIFT (7) + +/* MAX98388_R20B5_BDE_EN */ +#define MAX98388_BDE_EN_SHIFT (0) + +/* MAX98388_R20D1_DHT_CFG */ +#define MAX98388_DHT_ROT_PNT_SHIFT (0) +#define MAX98388_DHT_SPK_GAIN_MIN_SHIFT (4) + +/* MAX98388_R20D2_DHT_ATTACK_CFG */ +#define MAX98388_DHT_ATTACK_RATE_SHIFT (0) +#define MAX98388_DHT_ATTACK_STEP_SHIFT (3) + +/* MAX98388_R20D3_DHT_RELEASE_CFG */ +#define MAX98388_DHT_RELEASE_RATE_SHIFT (0) +#define MAX98388_DHT_RELEASE_STEP_SHIFT (3) + +/* MAX98388_R20D4_DHT_EN */ +#define MAX98388_DHT_EN_SHIFT (0) + +/* MAX98388_R20E0_BP_ALC_THRESH */ +#define MAX98388_ALC_THRESH_SHIFT (0) + +/* MAX98388_R20E1_BP_ALC_RATES */ +#define MAX98388_ALC_RELEASE_RATE_SHIFT (0) +#define MAX98388_ALC_ATTACK_RATE_SHIFT (4) + +/* MAX98388_R20E2_BP_ALC_ATTEN */ +#define MAX98388_ALC_MAX_ATTEN_SHIFT (0) + +/* MAX98388_R20E3_BP_ALC_REL */ +#define MAX98388_ALC_DEBOUNCE_TIME_SHIFT (0) + +/* MAX98388_R20E4_BP_ALC_MUTE */ +#define MAX98388_ALC_MUTE_EN_SHIFT (0) +#define MAX98388_ALC_MUTE_DELAY_SHIFT (1) +#define MAX98388_ALC_MUTE_RAMP_EN_SHIFT (4) +#define MAX98388_ALC_UNMUTE_RAMP_EN_SHIFT (5) + +/* MAX98388_R210E_AUTO_RESTART */ +#define MAX98388_PVDD_UVLO_AUTORESTART_SHIFT (0) +#define MAX98388_THERM_AUTORESTART_SHIFT (1) +#define MAX98388_OVC_AUTORESTART_SHIFT (2) +#define MAX98388_CMON_AUTORESTART_SHIFT (3) + +/* MAX98388_R210F_GLOBAL_EN */ +#define MAX98388_GLOBAL_EN_MASK (0x1 << 0) + +struct max98388_priv { + struct regmap *regmap; + struct gpio_desc *reset_gpio; + unsigned int v_slot; + unsigned int i_slot; + unsigned int spkfb_slot; + bool interleave_mode; + unsigned int ch_size; + bool tdm_mode; +}; + +#endif diff --git a/sound/soc/codecs/max98390.c b/sound/soc/codecs/max98390.c index 7a5260ff8d6b..5b8e78e51630 100644 --- a/sound/soc/codecs/max98390.c +++ b/sound/soc/codecs/max98390.c @@ -1133,7 +1133,7 @@ static struct i2c_driver max98390_i2c_driver = { .acpi_match_table = ACPI_PTR(max98390_acpi_match), .pm = &max98390_pm, }, - .probe_new = max98390_i2c_probe, + .probe = max98390_i2c_probe, .id_table = max98390_i2c_id, }; diff --git a/sound/soc/codecs/max98396.c b/sound/soc/codecs/max98396.c index db3de41f10e0..3a1d8c211f3c 100644 --- a/sound/soc/codecs/max98396.c +++ b/sound/soc/codecs/max98396.c @@ -1907,7 +1907,7 @@ static struct i2c_driver max98396_i2c_driver = { .acpi_match_table = ACPI_PTR(max98396_acpi_match), .pm = &max98396_pm, }, - .probe_new = max98396_i2c_probe, + .probe = max98396_i2c_probe, .id_table = max98396_i2c_id, }; diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c index a6733396b0ca..8b012a85360a 100644 --- a/sound/soc/codecs/max9850.c +++ b/sound/soc/codecs/max9850.c @@ -329,7 +329,7 @@ static struct i2c_driver max9850_i2c_driver = { .driver = { .name = "max9850", }, - .probe_new = max9850_i2c_probe, + .probe = max9850_i2c_probe, .id_table = max9850_i2c_id, }; diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c index 0daa2a3dd621..93412b966b33 100644 --- a/sound/soc/codecs/max98504.c +++ b/sound/soc/codecs/max98504.c @@ -371,7 +371,7 @@ static struct i2c_driver max98504_i2c_driver = { .name = "max98504", .of_match_table = of_match_ptr(max98504_of_match), }, - .probe_new = max98504_i2c_probe, + .probe = max98504_i2c_probe, .id_table = max98504_i2c_id, }; module_i2c_driver(max98504_i2c_driver); diff --git a/sound/soc/codecs/max98520.c b/sound/soc/codecs/max98520.c index 5edd6f90f8a7..8637fff307ad 100644 --- a/sound/soc/codecs/max98520.c +++ b/sound/soc/codecs/max98520.c @@ -756,7 +756,7 @@ static struct i2c_driver max98520_i2c_driver = { .of_match_table = of_match_ptr(max98520_of_match), .pm = &max98520_pm, }, - .probe_new = max98520_i2c_probe, + .probe = max98520_i2c_probe, .id_table = max98520_i2c_id, }; diff --git a/sound/soc/codecs/max9860.c b/sound/soc/codecs/max9860.c index 9611ab1e79e5..4015ed2c47ec 100644 --- a/sound/soc/codecs/max9860.c +++ b/sound/soc/codecs/max9860.c @@ -723,7 +723,7 @@ static const struct of_device_id max9860_of_match[] = { MODULE_DEVICE_TABLE(of, max9860_of_match); static struct i2c_driver max9860_i2c_driver = { - .probe_new = max9860_probe, + .probe = max9860_probe, .remove = max9860_remove, .id_table = max9860_i2c_id, .driver = { diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c index ae552d72beec..b616ad39858c 100644 --- a/sound/soc/codecs/max9867.c +++ b/sound/soc/codecs/max9867.c @@ -702,7 +702,7 @@ static struct i2c_driver max9867_i2c_driver = { .name = "max9867", .of_match_table = of_match_ptr(max9867_of_match), }, - .probe_new = max9867_i2c_probe, + .probe = max9867_i2c_probe, .id_table = max9867_i2c_id, }; diff --git a/sound/soc/codecs/max9877.c b/sound/soc/codecs/max9877.c index 3bf86328d5cd..2ae64fcf29c7 100644 --- a/sound/soc/codecs/max9877.c +++ b/sound/soc/codecs/max9877.c @@ -160,7 +160,7 @@ static struct i2c_driver max9877_i2c_driver = { .driver = { .name = "max9877", }, - .probe_new = max9877_i2c_probe, + .probe = max9877_i2c_probe, .id_table = max9877_i2c_id, }; diff --git a/sound/soc/codecs/max98925.c b/sound/soc/codecs/max98925.c index c24d9f2c8874..a9c1d85cd0d5 100644 --- a/sound/soc/codecs/max98925.c +++ b/sound/soc/codecs/max98925.c @@ -635,7 +635,7 @@ static struct i2c_driver max98925_i2c_driver = { .name = "max98925", .of_match_table = of_match_ptr(max98925_of_match), }, - .probe_new = max98925_i2c_probe, + .probe = max98925_i2c_probe, .id_table = max98925_i2c_id, }; diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c index bffd56e240e9..bdc508e23e59 100644 --- a/sound/soc/codecs/max98926.c +++ b/sound/soc/codecs/max98926.c @@ -582,7 +582,7 @@ static struct i2c_driver max98926_i2c_driver = { .name = "max98926", .of_match_table = of_match_ptr(max98926_of_match), }, - .probe_new = max98926_i2c_probe, + .probe = max98926_i2c_probe, .id_table = max98926_i2c_id, }; diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c index 331d3e1d735c..0aaf2e6ae78d 100644 --- a/sound/soc/codecs/max98927.c +++ b/sound/soc/codecs/max98927.c @@ -973,7 +973,7 @@ static struct i2c_driver max98927_i2c_driver = { .acpi_match_table = ACPI_PTR(max98927_acpi_match), .pm = &max98927_pm, }, - .probe_new = max98927_i2c_probe, + .probe = max98927_i2c_probe, .remove = max98927_i2c_remove, .id_table = max98927_i2c_id, }; diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c index 3c6ac77379cb..a45ef9d65703 100644 --- a/sound/soc/codecs/ml26124.c +++ b/sound/soc/codecs/ml26124.c @@ -581,7 +581,7 @@ static struct i2c_driver ml26124_i2c_driver = { .driver = { .name = "ml26124", }, - .probe_new = ml26124_i2c_probe, + .probe = ml26124_i2c_probe, .id_table = ml26124_i2c_id, }; diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c index cb487e63615c..30690479ec17 100644 --- a/sound/soc/codecs/mt6359.c +++ b/sound/soc/codecs/mt6359.c @@ -18,6 +18,20 @@ #include "mt6359.h" +static void mt6359_set_gpio_smt(struct mt6359_priv *priv) +{ + /* set gpio SMT mode */ + regmap_update_bits(priv->regmap, MT6359_SMT_CON1, 0x3ff0, 0x3ff0); +} + +static void mt6359_set_gpio_driving(struct mt6359_priv *priv) +{ + /* 8:4mA(default), a:8mA, c:12mA, e:16mA */ + regmap_update_bits(priv->regmap, MT6359_DRV_CON2, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON3, 0xffff, 0x8888); + regmap_update_bits(priv->regmap, MT6359_DRV_CON4, 0x00ff, 0x88); +} + static void mt6359_set_playback_gpio(struct mt6359_priv *priv) { /* set gpio mosi mode, clk / data mosi */ @@ -360,8 +374,34 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (struct soc_mixer_control *)kcontrol->private_value; unsigned int reg = 0; int index = ucontrol->value.integer.value[0]; + int orig_gain[2], new_gain[2]; int ret; + switch (mc->reg) { + case MT6359_ZCD_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + orig_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + case MT6359_AUDENC_ANA_CON0: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; + break; + case MT6359_AUDENC_ANA_CON1: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; + break; + case MT6359_AUDENC_ANA_CON2: + orig_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; + break; + default: + return -EINVAL; + } + ret = snd_soc_put_volsw(kcontrol, ucontrol); if (ret < 0) return ret; @@ -373,6 +413,8 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (reg >> RG_AUDHPLGAIN_SFT) & RG_AUDHPLGAIN_MASK; priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR] = (reg >> RG_AUDHPRGAIN_SFT) & RG_AUDHPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; break; case MT6359_ZCD_CON1: regmap_read(priv->regmap, MT6359_ZCD_CON1, ®); @@ -380,35 +422,82 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol, (reg >> RG_AUDLOLGAIN_SFT) & RG_AUDLOLGAIN_MASK; priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR] = (reg >> RG_AUDLORGAIN_SFT) & RG_AUDLORGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + new_gain[1] = priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; break; case MT6359_ZCD_CON3: regmap_read(priv->regmap, MT6359_ZCD_CON3, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL] = (reg >> RG_AUDHSGAIN_SFT) & RG_AUDHSGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; break; case MT6359_AUDENC_ANA_CON0: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON0, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1] = (reg >> RG_AUDPREAMPLGAIN_SFT) & RG_AUDPREAMPLGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP1]; break; case MT6359_AUDENC_ANA_CON1: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON1, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2] = (reg >> RG_AUDPREAMPRGAIN_SFT) & RG_AUDPREAMPRGAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP2]; break; case MT6359_AUDENC_ANA_CON2: regmap_read(priv->regmap, MT6359_AUDENC_ANA_CON2, ®); priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3] = (reg >> RG_AUDPREAMP3GAIN_SFT) & RG_AUDPREAMP3GAIN_MASK; + new_gain[0] = priv->ana_gain[AUDIO_ANALOG_VOLUME_MICAMP3]; break; } + ret = 0; + if (orig_gain[0] != new_gain[0]) { + ret = 1; + } else if (snd_soc_volsw_is_stereo(mc)) { + if (orig_gain[1] != new_gain[1]) + ret = 1; + } + dev_dbg(priv->dev, "%s(), name %s, reg(0x%x) = 0x%x, set index = %x\n", __func__, kcontrol->id.name, mc->reg, reg, index); return ret; } +static int mt6359_get_playback_volsw(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct mt6359_priv *priv = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + switch (mc->reg) { + case MT6359_ZCD_CON2: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HPOUTR]; + break; + case MT6359_ZCD_CON1: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTL]; + ucontrol->value.integer.value[1] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_LINEOUTR]; + break; + case MT6359_ZCD_CON3: + ucontrol->value.integer.value[0] = + priv->ana_gain[AUDIO_ANALOG_VOLUME_HSOUTL]; + break; + default: + return -EINVAL; + } + + return 0; +} + /* MUX */ /* LOL MUX */ @@ -1070,9 +1159,10 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, { struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm); struct mt6359_priv *priv = snd_soc_component_get_drvdata(cmpnt); + unsigned int mux = dapm_kcontrol_get_value(w->kcontrols[0]); dev_dbg(priv->dev, "%s(), event 0x%x, mux %u\n", - __func__, event, dapm_kcontrol_get_value(w->kcontrols[0])); + __func__, event, mux); switch (event) { case SND_SOC_DAPM_PRE_PMU: @@ -1110,14 +1200,29 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, /* Enable AUD_CLK */ mt6359_set_decoder_clk(priv, true); - /* Enable Audio DAC (3rd DAC) */ - regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); - /* Enable low-noise mode of DAC */ - if (priv->dev_counter[DEVICE_HP] == 0) - regmap_write(priv->regmap, - MT6359_AUDDEC_ANA_CON9, 0x0001); - /* Switch LOL MUX to audio 3rd DAC */ - regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + /* Switch LOL MUX to audio DAC */ + if (mux == LO_MUX_L_DAC) { + if (priv->dev_counter[DEVICE_HP] > 0) { + dev_info(priv->dev, "%s(), can not enable DAC, hp count %d\n", + __func__, priv->dev_counter[DEVICE_HP]); + break; + } + /* Enable DACL and switch HP MUX to open*/ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x3009); + /* Disable low-noise mode of DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0xf200); + usleep_range(100, 120); + /* Switch LOL MUX to DACL */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x0117); + } else if (mux == LO_MUX_3RD_DAC) { + /* Enable Audio DAC (3rd DAC) */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x3113); + /* Enable low-noise mode of DAC */ + if (priv->dev_counter[DEVICE_HP] == 0) + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON9, 0x0001); + /* Switch LOL MUX to audio 3rd DAC */ + regmap_write(priv->regmap, MT6359_AUDDEC_ANA_CON7, 0x311b); + } break; case SND_SOC_DAPM_PRE_PMD: /* Switch LOL MUX to open */ @@ -1129,6 +1234,15 @@ static int mt_lo_event(struct snd_soc_dapm_widget *w, regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, 0x000f, 0x0000); + if (mux == LO_MUX_L_DAC) { + /* Disable HP driver core circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 4, 0x0); + /* Disable HP driver bias circuits */ + regmap_update_bits(priv->regmap, MT6359_AUDDEC_ANA_CON0, + 0x3 << 6, 0x0); + } + /* Disable AUD_CLK */ mt6359_set_decoder_clk(priv, false); @@ -2358,6 +2472,10 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { {"MISO2_MUX", "UL2_CH1", "UL2_SRC_MUX"}, {"MISO2_MUX", "UL2_CH2", "UL2_SRC_MUX"}, + {"MISO0_MUX", NULL, "UL_SRC"}, + {"MISO1_MUX", NULL, "UL_SRC"}, + {"MISO2_MUX", NULL, "UL_SRC_34"}, + {"UL_SRC_MUX", "AMIC", "ADC_L"}, {"UL_SRC_MUX", "AMIC", "ADC_R"}, {"UL_SRC_MUX", "DMIC", "DMIC0_MUX"}, @@ -2497,6 +2615,7 @@ static const struct snd_soc_dapm_route mt6359_dapm_routes[] = { /* Lineout Path */ {"LOL Mux", "Playback", "DAC_3RD"}, + {"LOL Mux", "Playback_L_DAC", "DACL"}, {"LINEOUT L", NULL, "LOL Mux"}, /* Headphone Path */ @@ -2666,6 +2785,8 @@ static int mt6359_codec_init_reg(struct snd_soc_component *cmpnt) 0x1 << RG_AUDLOLSCDISABLE_VAUDP32_SFT); /* set gpio */ + mt6359_set_gpio_smt(priv); + mt6359_set_gpio_driving(priv); mt6359_reset_playback_gpio(priv); mt6359_reset_capture_gpio(priv); @@ -2697,22 +2818,23 @@ static void mt6359_codec_remove(struct snd_soc_component *cmpnt) cmpnt->regmap = NULL; } -static const DECLARE_TLV_DB_SCALE(hp_playback_tlv, -2200, 100, 0); static const DECLARE_TLV_DB_SCALE(playback_tlv, -1000, 100, 0); static const DECLARE_TLV_DB_SCALE(capture_tlv, 0, 600, 0); static const struct snd_kcontrol_new mt6359_snd_controls[] = { /* dl pga gain */ SOC_DOUBLE_EXT_TLV("Headset Volume", - MT6359_ZCD_CON2, 0, 7, 0x1E, 0, - snd_soc_get_volsw, mt6359_put_volsw, - hp_playback_tlv), + MT6359_ZCD_CON2, 0, 7, 0x12, 0, + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), SOC_DOUBLE_EXT_TLV("Lineout Volume", MT6359_ZCD_CON1, 0, 7, 0x12, 0, - snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), SOC_SINGLE_EXT_TLV("Handset Volume", MT6359_ZCD_CON3, 0, 0x12, 0, - snd_soc_get_volsw, mt6359_put_volsw, playback_tlv), + mt6359_get_playback_volsw, mt6359_put_volsw, + playback_tlv), /* ul pga gain */ SOC_SINGLE_EXT_TLV("PGA1 Volume", diff --git a/sound/soc/codecs/mt6660.c b/sound/soc/codecs/mt6660.c index cc2df5f7ea19..5c50c7de26cd 100644 --- a/sound/soc/codecs/mt6660.c +++ b/sound/soc/codecs/mt6660.c @@ -570,7 +570,7 @@ static struct i2c_driver mt6660_i2c_driver = { .of_match_table = of_match_ptr(mt6660_of_id), .pm = &mt6660_dev_pm_ops, }, - .probe_new = mt6660_i2c_probe, + .probe = mt6660_i2c_probe, .remove = mt6660_i2c_remove, .id_table = mt6660_i2c_id, }; diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c index 0626d5694c22..2174a89772fc 100644 --- a/sound/soc/codecs/nau8540.c +++ b/sound/soc/codecs/nau8540.c @@ -890,7 +890,7 @@ static struct i2c_driver nau8540_i2c_driver = { .name = "nau8540", .of_match_table = of_match_ptr(nau8540_of_ids), }, - .probe_new = nau8540_i2c_probe, + .probe = nau8540_i2c_probe, .id_table = nau8540_i2c_ids, }; module_i2c_driver(nau8540_i2c_driver); diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c index ccb512c21d74..47f000cd4d99 100644 --- a/sound/soc/codecs/nau8810.c +++ b/sound/soc/codecs/nau8810.c @@ -914,7 +914,7 @@ static struct i2c_driver nau8810_i2c_driver = { .name = "nau8810", .of_match_table = of_match_ptr(nau8810_of_match), }, - .probe_new = nau8810_i2c_probe, + .probe = nau8810_i2c_probe, .id_table = nau8810_i2c_id, }; diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c index fee970427a24..96d75882b33a 100644 --- a/sound/soc/codecs/nau8821.c +++ b/sound/soc/codecs/nau8821.c @@ -1859,7 +1859,7 @@ static struct i2c_driver nau8821_driver = { .of_match_table = of_match_ptr(nau8821_of_ids), .acpi_match_table = ACPI_PTR(nau8821_acpi_match), }, - .probe_new = nau8821_i2c_probe, + .probe = nau8821_i2c_probe, .id_table = nau8821_i2c_ids, }; module_i2c_driver(nau8821_driver); diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c index d5006d8de639..ff3024899f45 100644 --- a/sound/soc/codecs/nau8822.c +++ b/sound/soc/codecs/nau8822.c @@ -1166,7 +1166,7 @@ static struct i2c_driver nau8822_i2c_driver = { .name = "nau8822", .of_match_table = of_match_ptr(nau8822_of_match), }, - .probe_new = nau8822_i2c_probe, + .probe = nau8822_i2c_probe, .id_table = nau8822_i2c_id, }; module_i2c_driver(nau8822_i2c_driver); diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c index 5a4db8944d06..704af1cf8cbf 100644 --- a/sound/soc/codecs/nau8824.c +++ b/sound/soc/codecs/nau8824.c @@ -2030,7 +2030,7 @@ static struct i2c_driver nau8824_i2c_driver = { .of_match_table = of_match_ptr(nau8824_of_ids), .acpi_match_table = ACPI_PTR(nau8824_acpi_match), }, - .probe_new = nau8824_i2c_probe, + .probe = nau8824_i2c_probe, .id_table = nau8824_i2c_ids, }; module_i2c_driver(nau8824_i2c_driver); diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c index f4eb999761a4..9e0e4ddf128e 100644 --- a/sound/soc/codecs/nau8825.c +++ b/sound/soc/codecs/nau8825.c @@ -53,6 +53,7 @@ struct nau8825_fll { int mclk_src; int ratio; int fll_frac; + int fll_frac_num; int fll_int; int clk_ref_div; }; @@ -178,6 +179,8 @@ static const struct reg_default nau8825_reg_defaults[] = { { NAU8825_REG_CLASSG_CTRL, 0x0 }, { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 }, { NAU8825_REG_MISC_CTRL, 0x0 }, + { NAU8825_REG_FLL2_LOWER, 0x0 }, + { NAU8825_REG_FLL2_UPPER, 0x0 }, { NAU8825_REG_BIAS_ADJ, 0x0 }, { NAU8825_REG_TRIM_SETTINGS, 0x0 }, { NAU8825_REG_ANALOG_CONTROL_1, 0x0 }, @@ -200,6 +203,23 @@ static struct reg_default nau8825_xtalk_baktab[] = { { NAU8825_REG_DACR_CTRL, 0x02cf }, }; +/* The regmap patch for Rev C */ +static const struct reg_sequence nau8825_regmap_patch[] = { + { NAU8825_REG_FLL2, 0x0000 }, + { NAU8825_REG_FLL4, 0x8010 }, + { NAU8825_REG_FLL_VCO_RSV, 0x0bc0 }, + { NAU8825_REG_INTERRUPT_MASK, 0x0800 }, + { NAU8825_REG_DACL_CTRL, 0x00cf }, + { NAU8825_REG_DACR_CTRL, 0x02cf }, + { NAU8825_REG_OPT_EFUSE_CTRL, 0x0400 }, + { NAU8825_REG_FLL2_LOWER, 0x26e9 }, + { NAU8825_REG_FLL2_UPPER, 0x0031 }, + { NAU8825_REG_ANALOG_CONTROL_2, 0x0020 }, + { NAU8825_REG_ANALOG_ADC_2, 0x0220 }, + { NAU8825_REG_MIC_BIAS, 0x0046 }, +}; + + static const unsigned short logtable[256] = { 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7, 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508, @@ -608,8 +628,13 @@ static void nau8825_xtalk_prepare(struct nau8825 *nau8825) regmap_update_bits(nau8825->regmap, NAU8825_REG_INTERRUPT_MASK, NAU8825_IRQ_RMS_EN, 0); /* Power up left and right DAC */ - regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); } static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825) @@ -622,9 +647,14 @@ static void nau8825_xtalk_clean_dac(struct nau8825 *nau8825) NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L, NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L); /* Power down left and right DAC */ - regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + /* Enable the TESTDAC and disable L/R HP impedance */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_HPR_IMP | NAU8825_BIAS_HPL_IMP | @@ -855,7 +885,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg) case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R: case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: case NAU8825_REG_MISC_CTRL: - case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS: + case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_FLL2_UPPER: case NAU8825_REG_BIAS_ADJ: case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: @@ -881,6 +911,7 @@ static bool nau8825_writeable_reg(struct device *dev, unsigned int reg) case NAU8825_REG_IMM_MODE_CTRL: case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL: case NAU8825_REG_MISC_CTRL: + case NAU8825_REG_FLL2_LOWER ... NAU8825_REG_FLL2_UPPER: case NAU8825_REG_BIAS_ADJ: case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2: case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS: @@ -911,6 +942,32 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg) } } +static int nau8825_fepga_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct nau8825 *nau8825 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, + NAU8825_ACDC_VREF_MICP | NAU8825_ACDC_VREF_MICN); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, NAU8825_DISCHRG_EN); + msleep(40); + regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST, + NAU8825_DISCHRG_EN, 0); + regmap_update_bits(nau8825->regmap, NAU8825_REG_FEPGA, + NAU8825_ACDC_CTRL_MASK, 0); + break; + default: + break; + } + + return 0; +} + static int nau8825_adc_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -970,10 +1027,25 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w, /* Disables the TESTDAC to let DAC signal pass through. */ regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, 0); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); break; case SND_SOC_DAPM_POST_PMD: regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ, NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + else + regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0); + break; default: return -EINVAL; @@ -1127,8 +1199,8 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { SND_SOC_DAPM_INPUT("MIC"), SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0), - SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, - NULL, 0), + SND_SOC_DAPM_PGA_E("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0, + NULL, 0, nau8825_fepga_event, SND_SOC_DAPM_POST_PMU), SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, nau8825_adc_event, SND_SOC_DAPM_POST_PMU | @@ -1181,12 +1253,13 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = { NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0), SND_SOC_DAPM_PGA_S("Output DACL", 7, - NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_PGA_S("Output DACR", 7, - NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event, + SND_SOC_NOPM, 0, 0, nau8825_output_dac_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), + /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */ SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8, NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0), @@ -1678,6 +1751,10 @@ static void nau8825_setup_auto_irq(struct nau8825 *nau8825) { struct regmap *regmap = nau8825->regmap; + /* Enable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, + NAU8825_HSD_AUTO_MODE, NAU8825_HSD_AUTO_MODE); + /* Enable headset jack type detection complete interruption and * jack ejection interruption. */ @@ -1929,6 +2006,9 @@ static int nau8825_jack_insert(struct nau8825 *nau8825) regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER, NAU8825_CLK_MCLK_SRC_MASK, 0xf); + /* Disable HSD function */ + regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, NAU8825_HSD_AUTO_MODE, 0); + /* Leaving HPOL/R grounded after jack insert by default. They will be * ungrounded as part of the widget power up sequence at the beginning * of playback to reduce pop. @@ -2173,9 +2253,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825) regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_64); /* Disable DACR/L power */ - regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, - NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, + NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL); /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input * signal to avoid any glitches due to power up transients in both * the analog and digital DAC circuit. @@ -2307,9 +2388,12 @@ static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs, /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional * input based on FDCO, FREF and FLL ratio. */ - fvco = div_u64(fvco_max << 16, fref * fll_param->ratio); - fll_param->fll_int = (fvco >> 16) & 0x3FF; - fll_param->fll_frac = fvco & 0xFFFF; + fvco = div_u64(fvco_max << fll_param->fll_frac_num, fref * fll_param->ratio); + fll_param->fll_int = (fvco >> fll_param->fll_frac_num) & 0x3FF; + if (fll_param->fll_frac_num == 16) + fll_param->fll_frac = fvco & 0xFFFF; + else + fll_param->fll_frac = fvco & 0xFFFFFF; return 0; } @@ -2323,8 +2407,16 @@ static void nau8825_fll_apply(struct nau8825 *nau8825, regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1, NAU8825_FLL_RATIO_MASK | NAU8825_ICTRL_LATCH_MASK, fll_param->ratio | (0x6 << NAU8825_ICTRL_LATCH_SFT)); - /* FLL 16-bit fractional input */ - regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac); + /* FLL 16/24 bit fractional input */ + if (fll_param->fll_frac_num == 16) + regmap_write(nau8825->regmap, NAU8825_REG_FLL2, + fll_param->fll_frac); + else { + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_LOWER, + fll_param->fll_frac & 0xffff); + regmap_write(nau8825->regmap, NAU8825_REG_FLL2_UPPER, + (fll_param->fll_frac >> 16) & 0xff); + } /* FLL 10-bit integer input */ regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3, NAU8825_FLL_INTEGER_MASK, fll_param->fll_int); @@ -2366,6 +2458,11 @@ static int nau8825_set_pll(struct snd_soc_component *component, int pll_id, int struct nau8825_fll fll_param; int ret, fs; + if (nau8825->sw_id == NAU8825_SOFTWARE_ID_NAU8825) + fll_param.fll_frac_num = 16; + else + fll_param.fll_frac_num = 24; + fs = freq_out / 256; ret = nau8825_calc_fll_param(freq_in, fs, &fll_param); if (ret < 0) { @@ -2897,8 +2994,19 @@ static int nau8825_i2c_probe(struct i2c_client *i2c) ret); return ret; } - if ((value & NAU8825_SOFTWARE_ID_MASK) != - NAU8825_SOFTWARE_ID_NAU8825) { + nau8825->sw_id = value & NAU8825_SOFTWARE_ID_MASK; + switch (nau8825->sw_id) { + case NAU8825_SOFTWARE_ID_NAU8825: + break; + case NAU8825_SOFTWARE_ID_NAU8825C: + ret = regmap_register_patch(nau8825->regmap, nau8825_regmap_patch, + ARRAY_SIZE(nau8825_regmap_patch)); + if (ret) { + dev_err(dev, "Failed to register Rev C patch: %d\n", ret); + return ret; + } + break; + default: dev_err(dev, "Not a NAU8825 chip\n"); return -ENODEV; } @@ -2944,7 +3052,7 @@ static struct i2c_driver nau8825_driver = { .of_match_table = of_match_ptr(nau8825_of_ids), .acpi_match_table = ACPI_PTR(nau8825_acpi_match), }, - .probe_new = nau8825_i2c_probe, + .probe = nau8825_i2c_probe, .remove = nau8825_i2c_remove, .id_table = nau8825_i2c_ids, }; diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h index 44b62bc3880f..2abfbb5184da 100644 --- a/sound/soc/codecs/nau8825.h +++ b/sound/soc/codecs/nau8825.h @@ -75,6 +75,8 @@ #define NAU8825_REG_MISC_CTRL 0x55 #define NAU8825_REG_I2C_DEVICE_ID 0x58 #define NAU8825_REG_SARDOUT_RAM_STATUS 0x59 +#define NAU8825_REG_FLL2_LOWER 0x5a +#define NAU8825_REG_FLL2_UPPER 0x5b #define NAU8825_REG_BIAS_ADJ 0x66 #define NAU8825_REG_TRIM_SETTINGS 0x68 #define NAU8825_REG_ANALOG_CONTROL_1 0x69 @@ -386,6 +388,7 @@ #define NAU8825_GPIO2JD1 (1 << 7) #define NAU8825_SOFTWARE_ID_MASK 0x3 #define NAU8825_SOFTWARE_ID_NAU8825 0x0 +#define NAU8825_SOFTWARE_ID_NAU8825C 0x1 /* BIAS_ADJ (0x66) */ #define NAU8825_BIAS_HPR_IMP (1 << 15) @@ -442,10 +445,17 @@ /* BOOST (0x76) */ #define NAU8825_PRECHARGE_DIS (1 << 13) #define NAU8825_GLOBAL_BIAS_EN (1 << 12) +#define NAU8825_DISCHRG_EN (1 << 11) #define NAU8825_HP_BOOST_DIS (1 << 9) #define NAU8825_HP_BOOST_G_DIS (1 << 8) #define NAU8825_SHORT_SHUTDOWN_EN (1 << 6) +/* FEPGA (0x77) */ +#define NAU8825_ACDC_CTRL_SFT 14 +#define NAU8825_ACDC_CTRL_MASK (0x3 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICP (0x1 << NAU8825_ACDC_CTRL_SFT) +#define NAU8825_ACDC_VREF_MICN (0x2 << NAU8825_ACDC_CTRL_SFT) + /* POWER_UP_CONTROL (0x7f) */ #define NAU8825_POWERUP_INTEGR_R (1 << 5) #define NAU8825_POWERUP_INTEGR_L (1 << 4) @@ -490,6 +500,7 @@ struct nau8825 { struct clk *mclk; struct work_struct xtalk_work; struct semaphore xtalk_sem; + int sw_id; int irq; int mclk_freq; /* 0 - mclk is disabled */ int button_pressed; diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c index 3591f6f53901..735e1942b530 100644 --- a/sound/soc/codecs/pcm1681.c +++ b/sound/soc/codecs/pcm1681.c @@ -327,7 +327,7 @@ static struct i2c_driver pcm1681_i2c_driver = { .of_match_table = of_match_ptr(pcm1681_dt_ids), }, .id_table = pcm1681_i2c_id, - .probe_new = pcm1681_i2c_probe, + .probe = pcm1681_i2c_probe, }; module_i2c_driver(pcm1681_i2c_driver); diff --git a/sound/soc/codecs/pcm1789-i2c.c b/sound/soc/codecs/pcm1789-i2c.c index fafe0dcbe4ea..f2d0b4d21e41 100644 --- a/sound/soc/codecs/pcm1789-i2c.c +++ b/sound/soc/codecs/pcm1789-i2c.c @@ -52,7 +52,7 @@ static struct i2c_driver pcm1789_i2c_driver = { .of_match_table = of_match_ptr(pcm1789_of_match), }, .id_table = pcm1789_i2c_ids, - .probe_new = pcm1789_i2c_probe, + .probe = pcm1789_i2c_probe, .remove = pcm1789_i2c_remove, }; diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c index e20fe3a85af8..10579681f44b 100644 --- a/sound/soc/codecs/pcm179x-i2c.c +++ b/sound/soc/codecs/pcm179x-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver pcm179x_i2c_driver = { .of_match_table = of_match_ptr(pcm179x_of_match), }, .id_table = pcm179x_i2c_ids, - .probe_new = pcm179x_i2c_probe, + .probe = pcm179x_i2c_probe, }; module_i2c_driver(pcm179x_i2c_driver); diff --git a/sound/soc/codecs/pcm186x-i2c.c b/sound/soc/codecs/pcm186x-i2c.c index 932c8d41c3ea..a514ebd1b68a 100644 --- a/sound/soc/codecs/pcm186x-i2c.c +++ b/sound/soc/codecs/pcm186x-i2c.c @@ -46,7 +46,7 @@ static int pcm186x_i2c_probe(struct i2c_client *i2c) } static struct i2c_driver pcm186x_i2c_driver = { - .probe_new = pcm186x_i2c_probe, + .probe = pcm186x_i2c_probe, .id_table = pcm186x_i2c_id, .driver = { .name = "pcm186x", diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c index dd21803ba13c..451a8fd8fac5 100644 --- a/sound/soc/codecs/pcm186x.c +++ b/sound/soc/codecs/pcm186x.c @@ -12,7 +12,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> #include <linux/slab.h> diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c index 2388098eeca3..5330cf46b127 100644 --- a/sound/soc/codecs/pcm3060-i2c.c +++ b/sound/soc/codecs/pcm3060-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver pcm3060_i2c_driver = { #endif /* CONFIG_OF */ }, .id_table = pcm3060_i2c_id, - .probe_new = pcm3060_i2c_probe, + .probe = pcm3060_i2c_probe, }; module_i2c_driver(pcm3060_i2c_driver); diff --git a/sound/soc/codecs/pcm3168a-i2c.c b/sound/soc/codecs/pcm3168a-i2c.c index a0eec82e9872..7052cc0c97d1 100644 --- a/sound/soc/codecs/pcm3168a-i2c.c +++ b/sound/soc/codecs/pcm3168a-i2c.c @@ -44,7 +44,7 @@ static const struct of_device_id pcm3168a_of_match[] = { MODULE_DEVICE_TABLE(of, pcm3168a_of_match); static struct i2c_driver pcm3168a_i2c_driver = { - .probe_new = pcm3168a_i2c_probe, + .probe = pcm3168a_i2c_probe, .remove = pcm3168a_i2c_remove, .id_table = pcm3168a_i2c_id, .driver = { diff --git a/sound/soc/codecs/pcm512x-i2c.c b/sound/soc/codecs/pcm512x-i2c.c index 9dfbbe8f4a0b..5cd2b64b9337 100644 --- a/sound/soc/codecs/pcm512x-i2c.c +++ b/sound/soc/codecs/pcm512x-i2c.c @@ -66,7 +66,7 @@ MODULE_DEVICE_TABLE(acpi, pcm512x_acpi_match); #endif static struct i2c_driver pcm512x_i2c_driver = { - .probe_new = pcm512x_i2c_probe, + .probe = pcm512x_i2c_probe, .remove = pcm512x_i2c_remove, .id_table = pcm512x_i2c_id, .driver = { diff --git a/sound/soc/codecs/rk3328_codec.c b/sound/soc/codecs/rk3328_codec.c index 1d523bfd9d84..9697aefc6e03 100644 --- a/sound/soc/codecs/rk3328_codec.c +++ b/sound/soc/codecs/rk3328_codec.c @@ -11,7 +11,6 @@ #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/mfd/syscon.h> #include <sound/dmaengine_pcm.h> diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c index c1568216126e..42bac8150f62 100644 --- a/sound/soc/codecs/rt1011.c +++ b/sound/soc/codecs/rt1011.c @@ -2184,7 +2184,7 @@ static const struct regmap_config rt1011_regmap = { .max_register = RT1011_MAX_REG + 1, .volatile_reg = rt1011_volatile_register, .readable_reg = rt1011_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1011_reg, .num_reg_defaults = ARRAY_SIZE(rt1011_reg), .use_single_read = true, @@ -2483,7 +2483,7 @@ static struct i2c_driver rt1011_i2c_driver = { .of_match_table = of_match_ptr(rt1011_of_match), .acpi_match_table = ACPI_PTR(rt1011_acpi_match) }, - .probe_new = rt1011_i2c_probe, + .probe = rt1011_i2c_probe, .shutdown = rt1011_i2c_shutdown, .id_table = rt1011_i2c_id, }; diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c index 57d0f1c69e46..38d9f69b08d6 100644 --- a/sound/soc/codecs/rt1015.c +++ b/sound/soc/codecs/rt1015.c @@ -1170,7 +1170,7 @@ static struct i2c_driver rt1015_i2c_driver = { .of_match_table = of_match_ptr(rt1015_of_match), .acpi_match_table = ACPI_PTR(rt1015_acpi_match), }, - .probe_new = rt1015_i2c_probe, + .probe = rt1015_i2c_probe, .shutdown = rt1015_i2c_shutdown, .id_table = rt1015_i2c_id, }; diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c index 37eeec650f03..b1e69fa290b2 100644 --- a/sound/soc/codecs/rt1016.c +++ b/sound/soc/codecs/rt1016.c @@ -683,7 +683,7 @@ static struct i2c_driver rt1016_i2c_driver = { .of_match_table = of_match_ptr(rt1016_of_match), .acpi_match_table = ACPI_PTR(rt1016_acpi_match), }, - .probe_new = rt1016_i2c_probe, + .probe = rt1016_i2c_probe, .shutdown = rt1016_i2c_shutdown, .id_table = rt1016_i2c_id, }; diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c index dff2596c81eb..fd55049920c1 100644 --- a/sound/soc/codecs/rt1019.c +++ b/sound/soc/codecs/rt1019.c @@ -535,7 +535,7 @@ static const struct regmap_config rt1019_regmap = { .max_register = RT1019_BEEP_2, .volatile_reg = rt1019_volatile_register, .readable_reg = rt1019_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1019_reg, .num_reg_defaults = ARRAY_SIZE(rt1019_reg), }; @@ -600,7 +600,7 @@ static struct i2c_driver rt1019_i2c_driver = { .of_match_table = of_match_ptr(rt1019_of_match), .acpi_match_table = ACPI_PTR(rt1019_acpi_match), }, - .probe_new = rt1019_i2c_probe, + .probe = rt1019_i2c_probe, .id_table = rt1019_i2c_id, }; module_i2c_driver(rt1019_i2c_driver); diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c index 5b39a440b6dc..59895131e6e0 100644 --- a/sound/soc/codecs/rt1305.c +++ b/sound/soc/codecs/rt1305.c @@ -955,7 +955,7 @@ static const struct regmap_config rt1305_regmap = { RT1305_PR_SPACING), .volatile_reg = rt1305_volatile_register, .readable_reg = rt1305_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1305_reg, .num_reg_defaults = ARRAY_SIZE(rt1305_reg), .ranges = rt1305_ranges, @@ -1170,7 +1170,7 @@ static struct i2c_driver rt1305_i2c_driver = { .acpi_match_table = ACPI_PTR(rt1305_acpi_match) #endif }, - .probe_new = rt1305_i2c_probe, + .probe = rt1305_i2c_probe, .shutdown = rt1305_i2c_shutdown, .id_table = rt1305_i2c_id, }; diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index 1797af824f60..f43520ca3187 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -68,7 +68,7 @@ static const struct regmap_config rt1308_sdw_regmap = { .max_register = 0xcfff, .reg_defaults = rt1308_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1308_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -304,9 +304,6 @@ static int rt1308_update_status(struct sdw_slave *slave, { struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1308->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1308->hw_init = false; @@ -314,7 +311,7 @@ static int rt1308_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1308->hw_init || rt1308->status != SDW_SLAVE_ATTACHED) + if (rt1308->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h index 04ff18fa18e2..f816c73e247e 100644 --- a/sound/soc/codecs/rt1308-sdw.h +++ b/sound/soc/codecs/rt1308-sdw.h @@ -159,7 +159,6 @@ struct rt1308_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c index d2a8e9fe3e23..9d07756cda40 100644 --- a/sound/soc/codecs/rt1308.c +++ b/sound/soc/codecs/rt1308.c @@ -773,7 +773,7 @@ static const struct regmap_config rt1308_regmap = { .max_register = RT1308_MAX_REG, .volatile_reg = rt1308_volatile_register, .readable_reg = rt1308_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt1308_reg, .num_reg_defaults = ARRAY_SIZE(rt1308_reg), .use_single_read = true, @@ -862,7 +862,7 @@ static struct i2c_driver rt1308_i2c_driver = { .of_match_table = of_match_ptr(rt1308_of_match), .acpi_match_table = ACPI_PTR(rt1308_acpi_match), }, - .probe_new = rt1308_i2c_probe, + .probe = rt1308_i2c_probe, .shutdown = rt1308_i2c_shutdown, .id_table = rt1308_i2c_id, }; diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 2ee5e763e345..721821d9e9af 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -188,7 +188,7 @@ static const struct regmap_config rt1316_sdw_regmap = { .max_register = 0x4108ffff, .reg_defaults = rt1316_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1316_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -323,9 +323,6 @@ static int rt1316_update_status(struct sdw_slave *slave, { struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1316->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1316->hw_init = false; @@ -333,7 +330,7 @@ static int rt1316_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1316->hw_init || rt1316->status != SDW_SLAVE_ATTACHED) + if (rt1316->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h index e37121655bc1..dc1bfe40edd3 100644 --- a/sound/soc/codecs/rt1316-sdw.h +++ b/sound/soc/codecs/rt1316-sdw.h @@ -42,7 +42,6 @@ struct rt1316_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 795accedc22c..16d750102c8c 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -337,7 +337,7 @@ static const struct regmap_config rt1318_sdw_regmap = { .max_register = 0x41081488, .reg_defaults = rt1318_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt1318_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -456,9 +456,6 @@ static int rt1318_update_status(struct sdw_slave *slave, { struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt1318->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt1318->hw_init = false; @@ -466,7 +463,7 @@ static int rt1318_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt1318->hw_init || rt1318->status != SDW_SLAVE_ATTACHED) + if (rt1318->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h index 85918c184f16..86e83d63a017 100644 --- a/sound/soc/codecs/rt1318-sdw.h +++ b/sound/soc/codecs/rt1318-sdw.h @@ -88,7 +88,6 @@ struct rt1318_sdw_priv { struct snd_soc_component *component; struct regmap *regmap; struct sdw_slave *sdw_slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c index 4667bf7561b1..9a33e3776b55 100644 --- a/sound/soc/codecs/rt274.c +++ b/sound/soc/codecs/rt274.c @@ -1221,7 +1221,7 @@ static struct i2c_driver rt274_i2c_driver = { .of_match_table = of_match_ptr(rt274_of_match), #endif }, - .probe_new = rt274_i2c_probe, + .probe = rt274_i2c_probe, .remove = rt274_i2c_remove, .id_table = rt274_i2c_id, }; diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c index ceb56647e369..981155b046fd 100644 --- a/sound/soc/codecs/rt286.c +++ b/sound/soc/codecs/rt286.c @@ -1263,7 +1263,7 @@ static struct i2c_driver rt286_i2c_driver = { .name = "rt286", .acpi_match_table = ACPI_PTR(rt286_acpi_match), }, - .probe_new = rt286_i2c_probe, + .probe = rt286_i2c_probe, .remove = rt286_i2c_remove, .id_table = rt286_i2c_id, }; diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c index cea26f3a02b6..8fbd25ad9b47 100644 --- a/sound/soc/codecs/rt298.c +++ b/sound/soc/codecs/rt298.c @@ -1311,7 +1311,7 @@ static struct i2c_driver rt298_i2c_driver = { .name = "rt298", .acpi_match_table = ACPI_PTR(rt298_acpi_match), }, - .probe_new = rt298_i2c_probe, + .probe = rt298_i2c_probe, .remove = rt298_i2c_remove, .id_table = rt298_i2c_id, }; diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c index b9bcf04d4dc9..b3aac2373357 100644 --- a/sound/soc/codecs/rt5514.c +++ b/sound/soc/codecs/rt5514.c @@ -1195,7 +1195,7 @@ static const struct regmap_config rt5514_regmap = { .reg_read = rt5514_i2c_read, .reg_write = rt5514_i2c_write, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5514_reg, .num_reg_defaults = ARRAY_SIZE(rt5514_reg), .use_single_read = true, @@ -1328,7 +1328,7 @@ static struct i2c_driver rt5514_i2c_driver = { .of_match_table = of_match_ptr(rt5514_of_match), .pm = &rt5514_i2_pm_ops, }, - .probe_new = rt5514_i2c_probe, + .probe = rt5514_i2c_probe, .id_table = rt5514_i2c_id, }; module_i2c_driver(rt5514_i2c_driver); diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c index 948abde10463..c13108b51eaf 100644 --- a/sound/soc/codecs/rt5616.c +++ b/sound/soc/codecs/rt5616.c @@ -1315,7 +1315,7 @@ static const struct regmap_config rt5616_regmap = { RT5616_PR_SPACING), .volatile_reg = rt5616_volatile_register, .readable_reg = rt5616_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5616_reg, .num_reg_defaults = ARRAY_SIZE(rt5616_reg), .ranges = rt5616_ranges, @@ -1404,7 +1404,7 @@ static struct i2c_driver rt5616_i2c_driver = { .name = "rt5616", .of_match_table = of_match_ptr(rt5616_of_match), }, - .probe_new = rt5616_i2c_probe, + .probe = rt5616_i2c_probe, .remove = rt5616_i2c_remove, .shutdown = rt5616_i2c_shutdown, .id_table = rt5616_i2c_id, diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c index 55c232413e2b..a64e66c2d3c4 100644 --- a/sound/soc/codecs/rt5631.c +++ b/sound/soc/codecs/rt5631.c @@ -1693,7 +1693,7 @@ static const struct regmap_config rt5631_regmap_config = { .max_register = RT5631_VENDOR_ID2, .reg_defaults = rt5631_reg, .num_reg_defaults = ARRAY_SIZE(rt5631_reg), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -1728,7 +1728,7 @@ static struct i2c_driver rt5631_i2c_driver = { .name = "rt5631", .of_match_table = of_match_ptr(rt5631_i2c_dt_ids), }, - .probe_new = rt5631_i2c_probe, + .probe = rt5631_i2c_probe, .remove = rt5631_i2c_remove, .id_table = rt5631_i2c_id, }; diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c index 139257055507..0ed4fa261abf 100644 --- a/sound/soc/codecs/rt5640.c +++ b/sound/soc/codecs/rt5640.c @@ -2949,7 +2949,7 @@ static const struct regmap_config rt5640_regmap = { .volatile_reg = rt5640_volatile_register, .readable_reg = rt5640_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5640_reg, .num_reg_defaults = ARRAY_SIZE(rt5640_reg), .ranges = rt5640_ranges, @@ -3079,7 +3079,7 @@ static struct i2c_driver rt5640_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5640_acpi_match), .of_match_table = of_match_ptr(rt5640_of_match), }, - .probe_new = rt5640_i2c_probe, + .probe = rt5640_i2c_probe, .id_table = rt5640_i2c_id, }; module_i2c_driver(rt5640_i2c_driver); diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c index 7c7cbb6362ea..acc7fb1581b2 100644 --- a/sound/soc/codecs/rt5645.c +++ b/sound/soc/codecs/rt5645.c @@ -3546,7 +3546,7 @@ static const struct regmap_config rt5645_regmap = { .volatile_reg = rt5645_volatile_register, .readable_reg = rt5645_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5645_reg, .num_reg_defaults = ARRAY_SIZE(rt5645_reg), .ranges = rt5645_ranges, @@ -3563,7 +3563,7 @@ static const struct regmap_config rt5650_regmap = { .volatile_reg = rt5645_volatile_register, .readable_reg = rt5645_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5650_reg, .num_reg_defaults = ARRAY_SIZE(rt5650_reg), .ranges = rt5645_ranges, @@ -4184,7 +4184,7 @@ static struct i2c_driver rt5645_i2c_driver = { .of_match_table = of_match_ptr(rt5645_of_match), .acpi_match_table = ACPI_PTR(rt5645_acpi_match), }, - .probe_new = rt5645_i2c_probe, + .probe = rt5645_i2c_probe, .remove = rt5645_i2c_remove, .shutdown = rt5645_i2c_shutdown, .id_table = rt5645_i2c_id, diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c index df90af906563..0cee4fd1c84b 100644 --- a/sound/soc/codecs/rt5651.c +++ b/sound/soc/codecs/rt5651.c @@ -2172,7 +2172,7 @@ static const struct regmap_config rt5651_regmap = { .volatile_reg = rt5651_volatile_register, .readable_reg = rt5651_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5651_reg, .num_reg_defaults = ARRAY_SIZE(rt5651_reg), .ranges = rt5651_ranges, @@ -2279,7 +2279,7 @@ static struct i2c_driver rt5651_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5651_acpi_match), .of_match_table = of_match_ptr(rt5651_of_match), }, - .probe_new = rt5651_i2c_probe, + .probe = rt5651_i2c_probe, .id_table = rt5651_i2c_id, }; module_i2c_driver(rt5651_i2c_driver); diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c index 5e21e3c37ab5..df6f0d769bbd 100644 --- a/sound/soc/codecs/rt5659.c +++ b/sound/soc/codecs/rt5659.c @@ -4141,13 +4141,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c) regmap_write(rt5659->regmap, RT5659_RESET, 0); /* Check if MCLK provided */ - rt5659->mclk = devm_clk_get(&i2c->dev, "mclk"); - if (IS_ERR(rt5659->mclk)) { - if (PTR_ERR(rt5659->mclk) != -ENOENT) - return PTR_ERR(rt5659->mclk); - /* Otherwise mark the mclk pointer to NULL */ - rt5659->mclk = NULL; - } + rt5659->mclk = devm_clk_get_optional(&i2c->dev, "mclk"); + if (IS_ERR(rt5659->mclk)) + return PTR_ERR(rt5659->mclk); rt5659_calibrate(rt5659); @@ -4340,7 +4336,7 @@ static struct i2c_driver rt5659_i2c_driver = { .of_match_table = of_match_ptr(rt5659_of_match), .acpi_match_table = ACPI_PTR(rt5659_acpi_match), }, - .probe_new = rt5659_i2c_probe, + .probe = rt5659_i2c_probe, .shutdown = rt5659_i2c_shutdown, .id_table = rt5659_i2c_id, }; diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c index 341baa29fdb1..eade087b2d8b 100644 --- a/sound/soc/codecs/rt5660.c +++ b/sound/soc/codecs/rt5660.c @@ -1221,7 +1221,7 @@ static const struct regmap_config rt5660_regmap = { .volatile_reg = rt5660_volatile_register, .readable_reg = rt5660_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5660_reg, .num_reg_defaults = ARRAY_SIZE(rt5660_reg), .ranges = rt5660_ranges, @@ -1341,7 +1341,7 @@ static struct i2c_driver rt5660_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5660_acpi_match), .of_match_table = of_match_ptr(rt5660_of_match), }, - .probe_new = rt5660_i2c_probe, + .probe = rt5660_i2c_probe, .id_table = rt5660_i2c_id, }; module_i2c_driver(rt5660_i2c_driver); diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c index f73751dbde30..77246f84de29 100644 --- a/sound/soc/codecs/rt5663.c +++ b/sound/soc/codecs/rt5663.c @@ -3268,7 +3268,7 @@ static const struct regmap_config rt5663_v2_regmap = { .max_register = 0x07fa, .volatile_reg = rt5663_v2_volatile_register, .readable_reg = rt5663_v2_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5663_v2_reg, .num_reg_defaults = ARRAY_SIZE(rt5663_v2_reg), }; @@ -3281,7 +3281,7 @@ static const struct regmap_config rt5663_regmap = { .max_register = 0x03f3, .volatile_reg = rt5663_volatile_register, .readable_reg = rt5663_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5663_reg, .num_reg_defaults = ARRAY_SIZE(rt5663_reg), }; @@ -3733,7 +3733,7 @@ static struct i2c_driver rt5663_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5663_acpi_match), .of_match_table = of_match_ptr(rt5663_of_match), }, - .probe_new = rt5663_i2c_probe, + .probe = rt5663_i2c_probe, .remove = rt5663_i2c_remove, .shutdown = rt5663_i2c_shutdown, .id_table = rt5663_i2c_id, diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c index 17afaef85c77..83c367af91da 100644 --- a/sound/soc/codecs/rt5665.c +++ b/sound/soc/codecs/rt5665.c @@ -4626,7 +4626,7 @@ static const struct regmap_config rt5665_regmap = { .max_register = 0x0400, .volatile_reg = rt5665_volatile_register, .readable_reg = rt5665_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5665_reg, .num_reg_defaults = ARRAY_SIZE(rt5665_reg), .use_single_read = true, @@ -4968,7 +4968,7 @@ static struct i2c_driver rt5665_i2c_driver = { .of_match_table = of_match_ptr(rt5665_of_match), .acpi_match_table = ACPI_PTR(rt5665_acpi_match), }, - .probe_new = rt5665_i2c_probe, + .probe = rt5665_i2c_probe, .shutdown = rt5665_i2c_shutdown, .id_table = rt5665_i2c_id, }; diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c index ecf3b0527dbe..f04c810fd710 100644 --- a/sound/soc/codecs/rt5668.c +++ b/sound/soc/codecs/rt5668.c @@ -2370,7 +2370,7 @@ static const struct regmap_config rt5668_regmap = { .max_register = RT5668_I2C_MODE, .volatile_reg = rt5668_volatile_register, .readable_reg = rt5668_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5668_reg, .num_reg_defaults = ARRAY_SIZE(rt5668_reg), .use_single_read = true, @@ -2618,7 +2618,7 @@ static struct i2c_driver rt5668_i2c_driver = { .of_match_table = of_match_ptr(rt5668_of_match), .acpi_match_table = ACPI_PTR(rt5668_acpi_match), }, - .probe_new = rt5668_i2c_probe, + .probe = rt5668_i2c_probe, .shutdown = rt5668_i2c_shutdown, .id_table = rt5668_i2c_id, }; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c index a230f441559a..0e34293f3395 100644 --- a/sound/soc/codecs/rt5670.c +++ b/sound/soc/codecs/rt5670.c @@ -2863,7 +2863,7 @@ static const struct regmap_config rt5670_regmap = { RT5670_PR_SPACING), .volatile_reg = rt5670_volatile_register, .readable_reg = rt5670_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5670_reg, .num_reg_defaults = ARRAY_SIZE(rt5670_reg), .ranges = rt5670_ranges, @@ -3328,7 +3328,7 @@ static struct i2c_driver rt5670_i2c_driver = { .name = "rt5670", .acpi_match_table = ACPI_PTR(rt5670_acpi_match), }, - .probe_new = rt5670_i2c_probe, + .probe = rt5670_i2c_probe, .remove = rt5670_i2c_remove, .id_table = rt5670_i2c_id, }; diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c index 3bf019b3f700..ad14d18860fc 100644 --- a/sound/soc/codecs/rt5677.c +++ b/sound/soc/codecs/rt5677.c @@ -5704,7 +5704,7 @@ static struct i2c_driver rt5677_i2c_driver = { .of_match_table = rt5677_of_match, .acpi_match_table = ACPI_PTR(rt5677_acpi_match), }, - .probe_new = rt5677_i2c_probe, + .probe = rt5677_i2c_probe, .remove = rt5677_i2c_remove, }; module_i2c_driver(rt5677_i2c_driver); @@ -5712,3 +5712,5 @@ module_i2c_driver(rt5677_i2c_driver); MODULE_DESCRIPTION("ASoC RT5677 driver"); MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>"); MODULE_LICENSE("GPL v2"); + +MODULE_FIRMWARE("rt5677_elf_vad"); diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c index 5bc46b041786..fb8ffb5b2ff6 100644 --- a/sound/soc/codecs/rt5682-i2c.c +++ b/sound/soc/codecs/rt5682-i2c.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> @@ -47,7 +46,7 @@ static const struct regmap_config rt5682_regmap = { .max_register = RT5682_I2C_MODE, .volatile_reg = rt5682_volatile_register, .readable_reg = rt5682_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682_reg, .num_reg_defaults = RT5682_REG_NUM, .use_single_read = true, @@ -334,7 +333,7 @@ static struct i2c_driver rt5682_i2c_driver = { .acpi_match_table = rt5682_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe_new = rt5682_i2c_probe, + .probe = rt5682_i2c_probe, .remove = rt5682_i2c_remove, .shutdown = rt5682_i2c_shutdown, .id_table = rt5682_i2c_id, diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 23f17f70d7e9..67404f45389f 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -79,7 +79,7 @@ static const struct regmap_config rt5682_sdw_indirect_regmap = { .max_register = RT5682_I2C_MODE, .volatile_reg = rt5682_volatile_register, .readable_reg = rt5682_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682_reg, .num_reg_defaults = RT5682_REG_NUM, .use_single_read = true, @@ -500,9 +500,6 @@ static int rt5682_update_status(struct sdw_slave *slave, { struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt5682->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt5682->hw_init = false; @@ -510,7 +507,7 @@ static int rt5682_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt5682->hw_init || rt5682->status != SDW_SLAVE_ATTACHED) + if (rt5682->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h index e8efd8a84a6c..1a43d595f341 100644 --- a/sound/soc/codecs/rt5682.h +++ b/sound/soc/codecs/rt5682.h @@ -1440,7 +1440,6 @@ struct rt5682_priv { bool disable_irq; struct mutex calibrate_mutex; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c index 9c34dca58f54..c77c675bd5f5 100644 --- a/sound/soc/codecs/rt5682s.c +++ b/sound/soc/codecs/rt5682s.c @@ -11,7 +11,6 @@ #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/i2c.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> @@ -2848,14 +2847,9 @@ static int rt5682s_dai_probe_clks(struct snd_soc_component *component) int ret; /* Check if MCLK provided */ - rt5682s->mclk = devm_clk_get(component->dev, "mclk"); - if (IS_ERR(rt5682s->mclk)) { - if (PTR_ERR(rt5682s->mclk) != -ENOENT) { - ret = PTR_ERR(rt5682s->mclk); - return ret; - } - rt5682s->mclk = NULL; - } + rt5682s->mclk = devm_clk_get_optional(component->dev, "mclk"); + if (IS_ERR(rt5682s->mclk)) + return PTR_ERR(rt5682s->mclk); /* Register CCF DAI clock control */ ret = rt5682s_register_dai_clks(component); @@ -3046,7 +3040,7 @@ static const struct regmap_config rt5682s_regmap = { .max_register = RT5682S_MAX_REG, .volatile_reg = rt5682s_volatile_register, .readable_reg = rt5682s_readable_register, - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .reg_defaults = rt5682s_reg, .num_reg_defaults = ARRAY_SIZE(rt5682s_reg), .use_single_read = true, @@ -3316,7 +3310,7 @@ static struct i2c_driver rt5682s_i2c_driver = { .acpi_match_table = rt5682s_acpi_match, .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, - .probe_new = rt5682s_i2c_probe, + .probe = rt5682s_i2c_probe, .remove = rt5682s_i2c_remove, .shutdown = rt5682s_i2c_shutdown, .id_table = rt5682s_i2c_id, diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 96fc5f36d0d0..8b28e47775cc 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -292,7 +292,7 @@ static const struct regmap_config rt700_regmap = { .max_register = 0x755800, .reg_defaults = rt700_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt700_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt700_sdw_read, @@ -315,9 +315,6 @@ static int rt700_update_status(struct sdw_slave *slave, { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt700->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt700->hw_init = false; @@ -325,7 +322,7 @@ static int rt700_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt700->hw_init || rt700->status != SDW_SLAVE_ATTACHED) + if (rt700->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h index 93c44005d38c..491774d207de 100644 --- a/sound/soc/codecs/rt700.h +++ b/sound/soc/codecs/rt700.h @@ -15,7 +15,6 @@ struct rt700_priv { struct regmap *regmap; struct regmap *sdw_regmap; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 51f3335343e0..119e1f9605d7 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -119,7 +119,7 @@ static const struct regmap_config rt711_sdca_regmap = { .max_register = 0x44ffffff, .reg_defaults = rt711_sdca_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_sdca_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -133,7 +133,7 @@ static const struct regmap_config rt711_sdca_mbq_regmap = { .max_register = 0x40800f12, .reg_defaults = rt711_sdca_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_sdca_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -143,9 +143,6 @@ static int rt711_sdca_update_status(struct sdw_slave *slave, { struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt711->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt711->hw_init = false; @@ -168,7 +165,7 @@ static int rt711_sdca_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h index 22076f268577..11d421e8ab2b 100644 --- a/sound/soc/codecs/rt711-sdca.h +++ b/sound/soc/codecs/rt711-sdca.h @@ -19,7 +19,6 @@ struct rt711_sdca_priv { struct regmap *regmap, *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 4fe68bcf2a7c..87dafcb4545d 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -296,7 +296,7 @@ static const struct regmap_config rt711_regmap = { .max_register = 0x755800, .reg_defaults = rt711_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt711_sdw_read, @@ -319,9 +319,6 @@ static int rt711_update_status(struct sdw_slave *slave, { struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt711->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt711->hw_init = false; @@ -329,7 +326,7 @@ static int rt711_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt711->hw_init || rt711->status != SDW_SLAVE_ATTACHED) + if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h index b31351f11df9..491e357191f9 100644 --- a/sound/soc/codecs/rt711.h +++ b/sound/soc/codecs/rt711.h @@ -15,7 +15,6 @@ struct rt711_priv { struct regmap *sdw_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 09807b6d6353..869cc7bfd178 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -110,7 +110,7 @@ static const struct regmap_config rt712_sdca_dmic_regmap = { .max_register = 0x40981300, .reg_defaults = rt712_sdca_dmic_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -124,7 +124,7 @@ static const struct regmap_config rt712_sdca_dmic_mbq_regmap = { .max_register = 0x40800f14, .reg_defaults = rt712_sdca_dmic_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -803,9 +803,6 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave, { struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt712->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt712->hw_init = false; @@ -813,7 +810,7 @@ static int rt712_sdca_dmic_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED) + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h index 74c29677c251..110154e74efe 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.h +++ b/sound/soc/codecs/rt712-sdca-dmic.h @@ -16,7 +16,6 @@ struct rt712_sdca_dmic_priv { struct regmap *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index 3f319459dfec..ad06267b0ea0 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -116,7 +116,7 @@ static const struct regmap_config rt712_sdca_regmap = { .max_register = 0x44ffffff, .reg_defaults = rt712_sdca_reg_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -130,7 +130,7 @@ static const struct regmap_config rt712_sdca_mbq_regmap = { .max_register = 0x41000312, .reg_defaults = rt712_sdca_mbq_defaults, .num_reg_defaults = ARRAY_SIZE(rt712_sdca_mbq_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -140,9 +140,6 @@ static int rt712_sdca_update_status(struct sdw_slave *slave, { struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt712->status = status; - if (status == SDW_SLAVE_UNATTACHED) rt712->hw_init = false; @@ -165,7 +162,7 @@ static int rt712_sdca_update_status(struct sdw_slave *slave, * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED) + if (rt712->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h index c6a94a23f46e..ff79e03118ce 100644 --- a/sound/soc/codecs/rt712-sdca.h +++ b/sound/soc/codecs/rt712-sdca.h @@ -20,7 +20,6 @@ struct rt712_sdca_priv { struct regmap *mbq_regmap; struct snd_soc_component *component; struct sdw_slave *slave; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index 38a82e4e2f95..df10916bab46 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -97,7 +97,7 @@ static const struct regmap_config rt715_sdca_regmap = { .max_register = 0x43ffffff, .reg_defaults = rt715_reg_defaults_sdca, .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -111,7 +111,7 @@ static const struct regmap_config rt715_sdca_mbq_regmap = { .max_register = 0x43ffffff, .reg_defaults = rt715_mbq_reg_defaults_sdca, .num_reg_defaults = ARRAY_SIZE(rt715_mbq_reg_defaults_sdca), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, }; @@ -121,14 +121,11 @@ static int rt715_sdca_update_status(struct sdw_slave *slave, { struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt715->status = status; - /* * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h index 7577f3151934..e5d6928ecaba 100644 --- a/sound/soc/codecs/rt715-sdca.h +++ b/sound/soc/codecs/rt715-sdca.h @@ -24,7 +24,6 @@ struct rt715_sdca_priv { int dbg_nid; int dbg_vid; int dbg_payload; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index 4e61e16470ed..6db87442b783 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -354,7 +354,7 @@ static const struct regmap_config rt715_regmap = { .max_register = 0x752039, /* Maximum number of register */ .reg_defaults = rt715_reg_defaults, /* Defaults */ .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), - .cache_type = REGCACHE_RBTREE, + .cache_type = REGCACHE_MAPLE, .use_single_read = true, .use_single_write = true, .reg_read = rt715_sdw_read, @@ -417,13 +417,11 @@ static int rt715_update_status(struct sdw_slave *slave, { struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev); - /* Update the status */ - rt715->status = status; /* * Perform initialization only if slave status is present and * hw_init flag is false */ - if (rt715->hw_init || rt715->status != SDW_SLAVE_ATTACHED) + if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) return 0; /* perform I/O transfers required for Slave initialization */ diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h index 17a8d041c1c3..12a0ae656d09 100644 --- a/sound/soc/codecs/rt715.h +++ b/sound/soc/codecs/rt715.h @@ -18,7 +18,6 @@ struct rt715_priv { int dbg_nid; int dbg_vid; int dbg_payload; - enum sdw_slave_status status; struct sdw_bus_params params; bool hw_init; bool first_hw_init; diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c new file mode 100644 index 000000000000..cc57e4e27805 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -0,0 +1,507 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca-sdw.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/pm_runtime.h> +#include <linux/soundwire/sdw_registers.h> + +#include "rt722-sdca.h" +#include "rt722-sdca-sdw.h" + +static bool rt722_sdca_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01 ... 0x2f0a: + case 0x2f35 ... 0x2f36: + case 0x2f50: + case 0x2f54: + case 0x2f58 ... 0x2f5d: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_SELECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2f01: + case 0x2f54: + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, RT722_SDCA_CTL_DETECTED_MODE, + 0): + case SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, + 0) ... SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH, 0): + case RT722_BUF_ADDR_HID1 ... RT722_BUF_ADDR_HID2: + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_readable_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000 ... 0x2000024: + case 0x2000029 ... 0x200004a: + case 0x2000051 ... 0x2000052: + case 0x200005a ... 0x200005b: + case 0x2000061 ... 0x2000069: + case 0x200006b: + case 0x2000070: + case 0x200007f: + case 0x2000082 ... 0x200008e: + case 0x2000090 ... 0x2000094: + case 0x5300000 ... 0x5300002: + case 0x5400002: + case 0x5600000 ... 0x5600007: + case 0x5700000 ... 0x5700004: + case 0x5800000 ... 0x5800004: + case 0x5b00003: + case 0x5c00011: + case 0x5d00006: + case 0x5f00000 ... 0x5f0000d: + case 0x5f00030: + case 0x6100000 ... 0x6100051: + case 0x6100055 ... 0x6100057: + case 0x6100062: + case 0x6100064 ... 0x6100065: + case 0x6100067: + case 0x6100070 ... 0x610007c: + case 0x6100080: + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03): + case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L): + case SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_R): + return true; + default: + return false; + } +} + +static bool rt722_sdca_mbq_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case 0x2000000: + case 0x200000d: + case 0x2000019: + case 0x2000020: + case 0x2000030: + case 0x2000046: + case 0x2000067: + case 0x2000084: + case 0x2000086: + return true; + default: + return false; + } +} + +static const struct regmap_config rt722_sdca_regmap = { + .reg_bits = 32, + .val_bits = 8, + .readable_reg = rt722_sdca_readable_register, + .volatile_reg = rt722_sdca_volatile_register, + .max_register = 0x44ffffff, + .reg_defaults = rt722_sdca_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_reg_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static const struct regmap_config rt722_sdca_mbq_regmap = { + .name = "sdw-mbq", + .reg_bits = 32, + .val_bits = 16, + .readable_reg = rt722_sdca_mbq_readable_register, + .volatile_reg = rt722_sdca_mbq_volatile_register, + .max_register = 0x41000312, + .reg_defaults = rt722_sdca_mbq_defaults, + .num_reg_defaults = ARRAY_SIZE(rt722_sdca_mbq_defaults), + .cache_type = REGCACHE_MAPLE, + .use_single_read = true, + .use_single_write = true, +}; + +static int rt722_sdca_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (status == SDW_SLAVE_UNATTACHED) + rt722->hw_init = false; + + if (status == SDW_SLAVE_ATTACHED) { + if (rt722->hs_jack) { + /* + * Due to the SCP_SDCA_INTMASK will be cleared by any reset, and then + * if the device attached again, we will need to set the setting back. + * It could avoid losing the jack detection interrupt. + * This also could sync with the cache value as the rt722_sdca_jack_init set. + */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + } + } + + /* + * Perform initialization only if slave status is present and + * hw_init flag is false + */ + if (rt722->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + /* perform I/O transfers required for Slave initialization */ + return rt722_sdca_io_init(&slave->dev, slave); +} + +static int rt722_sdca_read_prop(struct sdw_slave *slave) +{ + struct sdw_slave_prop *prop = &slave->prop; + int nval; + int i, j; + u32 bit; + unsigned long addr; + struct sdw_dpn_prop *dpn; + + prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; + + prop->paging_support = true; + + /* + * port = 1 for headphone playback + * port = 2 for headset-mic capture + * port = 3 for speaker playback + * port = 6 for digital-mic capture + */ + prop->source_ports = BIT(6) | BIT(2); /* BITMAP: 01000100 */ + prop->sink_ports = BIT(3) | BIT(1); /* BITMAP: 00001010 */ + + nval = hweight32(prop->source_ports); + prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->src_dpn_prop), GFP_KERNEL); + if (!prop->src_dpn_prop) + return -ENOMEM; + + i = 0; + dpn = prop->src_dpn_prop; + addr = prop->source_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[i].num = bit; + dpn[i].type = SDW_DPN_FULL; + dpn[i].simple_ch_prep_sm = true; + dpn[i].ch_prep_timeout = 10; + i++; + } + + /* do this again for sink now */ + nval = hweight32(prop->sink_ports); + prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval, + sizeof(*prop->sink_dpn_prop), GFP_KERNEL); + if (!prop->sink_dpn_prop) + return -ENOMEM; + + j = 0; + dpn = prop->sink_dpn_prop; + addr = prop->sink_ports; + for_each_set_bit(bit, &addr, 32) { + dpn[j].num = bit; + dpn[j].type = SDW_DPN_FULL; + dpn[j].simple_ch_prep_sm = true; + dpn[j].ch_prep_timeout = 10; + j++; + } + + /* set the timeout values */ + prop->clk_stop_timeout = 200; + + /* wake-up event */ + prop->wake_capable = 1; + + return 0; +} + +static int rt722_sdca_interrupt_callback(struct sdw_slave *slave, + struct sdw_slave_intr_status *status) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + int ret, stat; + int count = 0, retry = 3; + unsigned int sdca_cascade, scp_sdca_stat1, scp_sdca_stat2 = 0; + + if (cancel_delayed_work_sync(&rt722->jack_detect_work)) { + dev_warn(&slave->dev, "%s the pending delayed_work was cancelled", __func__); + /* avoid the HID owner doesn't change to device */ + if (rt722->scp_sdca_stat2) + scp_sdca_stat2 = rt722->scp_sdca_stat2; + } + + /* + * The critical section below intentionally protects a rather large piece of code. + * We don't want to allow the system suspend to disable an interrupt while we are + * processing it, which could be problematic given the quirky SoundWire interrupt + * scheme. We do want however to prevent new workqueues from being scheduled if + * the disable_irq flag was set during system suspend. + */ + mutex_lock(&rt722->disable_irq_lock); + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat1 = ret; + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + rt722->scp_sdca_stat2 = ret; + if (scp_sdca_stat2) + rt722->scp_sdca_stat2 |= scp_sdca_stat2; + do { + /* clear flag */ + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_0) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_0, SDW_SCP_SDCA_INT_SDCA_0); + if (ret < 0) + goto io_error; + } else if (ret & SDW_SCP_SDCA_INTMASK_SDCA_6) { + ret = sdw_update_no_pm(rt722->slave, SDW_SCP_SDCA_INT1, + SDW_SCP_SDCA_INT_SDCA_6, SDW_SCP_SDCA_INT_SDCA_6); + if (ret < 0) + goto io_error; + } + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + if (ret & SDW_SCP_SDCA_INTMASK_SDCA_8) { + ret = sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INT2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + if (ret < 0) + goto io_error; + } + + /* check if flag clear or not */ + ret = sdw_read_no_pm(rt722->slave, SDW_DP0_INT); + if (ret < 0) + goto io_error; + sdca_cascade = ret & SDW_DP0_SDCA_CASCADE; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT1); + if (ret < 0) + goto io_error; + scp_sdca_stat1 = ret & SDW_SCP_SDCA_INTMASK_SDCA_0; + + ret = sdw_read_no_pm(rt722->slave, SDW_SCP_SDCA_INT2); + if (ret < 0) + goto io_error; + scp_sdca_stat2 = ret & SDW_SCP_SDCA_INTMASK_SDCA_8; + + stat = scp_sdca_stat1 || scp_sdca_stat2 || sdca_cascade; + + count++; + } while (stat != 0 && count < retry); + + if (stat) + dev_warn(&slave->dev, + "%s scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + if (status->sdca_cascade && !rt722->disable_irq) + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_detect_work, msecs_to_jiffies(30)); + + mutex_unlock(&rt722->disable_irq_lock); + + return 0; + +io_error: + mutex_unlock(&rt722->disable_irq_lock); + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static struct sdw_slave_ops rt722_sdca_slave_ops = { + .read_prop = rt722_sdca_read_prop, + .interrupt_callback = rt722_sdca_interrupt_callback, + .update_status = rt722_sdca_update_status, +}; + +static int rt722_sdca_sdw_probe(struct sdw_slave *slave, + const struct sdw_device_id *id) +{ + struct regmap *regmap, *mbq_regmap; + + /* Regmap Initialization */ + mbq_regmap = devm_regmap_init_sdw_mbq(slave, &rt722_sdca_mbq_regmap); + if (IS_ERR(mbq_regmap)) + return PTR_ERR(mbq_regmap); + + regmap = devm_regmap_init_sdw(slave, &rt722_sdca_regmap); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + return rt722_sdca_init(&slave->dev, regmap, mbq_regmap, slave); +} + +static int rt722_sdca_sdw_remove(struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); + + if (rt722->hw_init) { + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + } + + if (rt722->first_hw_init) + pm_runtime_disable(&slave->dev); + + mutex_destroy(&rt722->calibrate_mutex); + mutex_destroy(&rt722->disable_irq_lock); + + return 0; +} + +static const struct sdw_device_id rt722_sdca_id[] = { + SDW_SLAVE_ENTRY_EXT(0x025d, 0x722, 0x3, 0x1, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, rt722_sdca_id); + +static int __maybe_unused rt722_sdca_dev_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + if (!rt722->hw_init) + return 0; + + cancel_delayed_work_sync(&rt722->jack_detect_work); + cancel_delayed_work_sync(&rt722->jack_btn_check_work); + + regcache_cache_only(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, true); + + return 0; +} + +static int __maybe_unused rt722_sdca_dev_system_suspend(struct device *dev) +{ + struct rt722_sdca_priv *rt722_sdca = dev_get_drvdata(dev); + struct sdw_slave *slave = dev_to_sdw_dev(dev); + int ret1, ret2; + + if (!rt722_sdca->hw_init) + return 0; + + /* + * prevent new interrupts from being handled after the + * deferred work completes and before the parent disables + * interrupts on the link + */ + mutex_lock(&rt722_sdca->disable_irq_lock); + rt722_sdca->disable_irq = true; + ret1 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6, 0); + ret2 = sdw_update_no_pm(slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8, 0); + mutex_unlock(&rt722_sdca->disable_irq_lock); + + if (ret1 < 0 || ret2 < 0) { + /* log but don't prevent suspend from happening */ + dev_dbg(&slave->dev, "%s: could not disable SDCA interrupts\n:", __func__); + } + + return rt722_sdca_dev_suspend(dev); +} + +#define RT722_PROBE_TIMEOUT 5000 + +static int __maybe_unused rt722_sdca_dev_resume(struct device *dev) +{ + struct sdw_slave *slave = dev_to_sdw_dev(dev); + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + unsigned long time; + + if (!rt722->first_hw_init) + return 0; + + if (!slave->unattach_request) + goto regmap_sync; + + time = wait_for_completion_timeout(&slave->initialization_complete, + msecs_to_jiffies(RT722_PROBE_TIMEOUT)); + if (!time) { + dev_err(&slave->dev, "Initialization not complete, timed out\n"); + sdw_show_ping_status(slave->bus, true); + + return -ETIMEDOUT; + } + +regmap_sync: + slave->unattach_request = 0; + regcache_cache_only(rt722->regmap, false); + regcache_sync(rt722->regmap); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_sync(rt722->mbq_regmap); + return 0; +} + +static const struct dev_pm_ops rt722_sdca_pm = { + SET_SYSTEM_SLEEP_PM_OPS(rt722_sdca_dev_system_suspend, rt722_sdca_dev_resume) + SET_RUNTIME_PM_OPS(rt722_sdca_dev_suspend, rt722_sdca_dev_resume, NULL) +}; + +static struct sdw_driver rt722_sdca_sdw_driver = { + .driver = { + .name = "rt722-sdca", + .owner = THIS_MODULE, + .pm = &rt722_sdca_pm, + }, + .probe = rt722_sdca_sdw_probe, + .remove = rt722_sdca_sdw_remove, + .ops = &rt722_sdca_slave_ops, + .id_table = rt722_sdca_id, +}; +module_sdw_driver(rt722_sdca_sdw_driver); + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca-sdw.h b/sound/soc/codecs/rt722-sdca-sdw.h new file mode 100644 index 000000000000..5b43e86f75d1 --- /dev/null +++ b/sound/soc/codecs/rt722-sdca-sdw.h @@ -0,0 +1,124 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca-sdw.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_SDW_H__ +#define __RT722_SDW_H__ + +#include <linux/regmap.h> +#include <linux/soundwire/sdw_registers.h> + +static const struct reg_default rt722_sdca_reg_defaults[] = { + { 0x202d, 0x00 }, + { 0x2f01, 0x00 }, + { 0x2f02, 0x09 }, + { 0x2f03, 0x00 }, + { 0x2f04, 0x00 }, + { 0x2f05, 0x0b }, + { 0x2f06, 0x01 }, + { 0x2f08, 0x00 }, + { 0x2f09, 0x00 }, + { 0x2f0a, 0x00 }, + { 0x2f35, 0x00 }, + { 0x2f36, 0x00 }, + { 0x2f50, 0xf0 }, + { 0x2f58, 0x07 }, + { 0x2f59, 0x07 }, + { 0x2f5a, 0x07 }, + { 0x2f5b, 0x07 }, + { 0x2f5c, 0x27 }, + { 0x2f5d, 0x07 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, RT722_SDCA_CTL_REQ_POWER_STATE, + 0), 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, + 0), 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_01), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_02), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_03), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_MUTE, CH_04), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, RT722_SDCA_CTL_VENDOR_DEF, 0), + 0x00 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), + 0x09 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_L), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_MUTE, CH_R), + 0x01 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, RT722_SDCA_CTL_REQ_POWER_STATE, 0), + 0x03 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, RT722_SDCA_CTL_VENDOR_DEF, 0), 0x00 }, +}; + +static const struct reg_default rt722_sdca_mbq_defaults[] = { + { 0x200003c, 0xc214 }, + { 0x2000046, 0x8004 }, + { 0x6100006, 0x0005 }, + { 0x6100010, 0x2630 }, + { 0x6100011, 0x152f }, + { 0x6100013, 0x0102 }, + { 0x6100015, 0x2200 }, + { 0x6100017, 0x0102 }, + { 0x6100025, 0x2a29 }, + { 0x6100026, 0x2a00 }, + { 0x6100028, 0x2a2a }, + { 0x6100029, 0x4141 }, + { 0x6100055, 0x0000 }, + { 0x5810000, 0x702d }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, RT722_SDCA_CTL_FU_VOLUME, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_L), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, RT722_SDCA_CTL_FU_CH_GAIN, + CH_R), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_01), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_02), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_03), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, RT722_SDCA_CTL_FU_VOLUME, + CH_04), 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_01), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_02), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_03), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, RT722_SDCA_CTL_FU_CH_GAIN, CH_04), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_L), + 0x0000 }, + { SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, RT722_SDCA_CTL_FU_VOLUME, CH_R), + 0x0000 }, +}; + +#endif /* __RT722_SDW_H__ */ diff --git a/sound/soc/codecs/rt722-sdca.c b/sound/soc/codecs/rt722-sdca.c new file mode 100644 index 000000000000..9c0d34366c9e --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.c @@ -0,0 +1,1555 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// rt722-sdca.c -- rt722 SDCA ALSA SoC audio driver +// +// Copyright(c) 2023 Realtek Semiconductor Corp. +// +// + +#include <linux/bitops.h> +#include <sound/core.h> +#include <linux/delay.h> +#include <linux/init.h> +#include <sound/initval.h> +#include <sound/jack.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <sound/pcm.h> +#include <linux/pm_runtime.h> +#include <sound/pcm_params.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/slab.h> +#include <sound/soc-dapm.h> +#include <sound/tlv.h> + +#include "rt722-sdca.h" + +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value) +{ + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + int ret; + + ret = regmap_write(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to set private value: %06x <= %04x ret=%d\n", + addr, value, ret); + + return ret; +} + +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value) +{ + int ret; + struct regmap *regmap = rt722->mbq_regmap; + unsigned int addr = (nid << 20) | reg; + + ret = regmap_read(regmap, addr, value); + if (ret < 0) + dev_err(&rt722->slave->dev, + "Failed to get private value: %06x => %04x ret=%d\n", + addr, *value, ret); + + return ret; +} + +static int rt722_sdca_index_update_bits(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val) +{ + unsigned int tmp; + int ret; + + ret = rt722_sdca_index_read(rt722, nid, reg, &tmp); + if (ret < 0) + return ret; + + set_mask_bits(&tmp, mask, val); + return rt722_sdca_index_write(rt722, nid, reg, tmp); +} + +static int rt722_sdca_btn_type(unsigned char *buffer) +{ + if ((*buffer & 0xf0) == 0x10 || (*buffer & 0x0f) == 0x01 || (*(buffer + 1) == 0x01) || + (*(buffer + 1) == 0x10)) + return SND_JACK_BTN_2; + else if ((*buffer & 0xf0) == 0x20 || (*buffer & 0x0f) == 0x02 || (*(buffer + 1) == 0x02) || + (*(buffer + 1) == 0x20)) + return SND_JACK_BTN_3; + else if ((*buffer & 0xf0) == 0x40 || (*buffer & 0x0f) == 0x04 || (*(buffer + 1) == 0x04) || + (*(buffer + 1) == 0x40)) + return SND_JACK_BTN_0; + else if ((*buffer & 0xf0) == 0x80 || (*buffer & 0x0f) == 0x08 || (*(buffer + 1) == 0x08) || + (*(buffer + 1) == 0x80)) + return SND_JACK_BTN_1; + + return 0; +} + +static unsigned int rt722_sdca_button_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int btn_type = 0, offset, idx, val, owner; + int ret; + unsigned char buf[3]; + + /* get current UMP message owner */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), &owner); + if (ret < 0) + return 0; + + /* if owner is device then there is no button event from device */ + if (owner == 1) + return 0; + + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto _end_btn_det_; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto _end_btn_det_; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + +_end_btn_det_: + /* Host is owner, so set back to device */ + if (owner == 0) + /* set owner to device */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_CURRENT_OWNER, 0), 0x01); + + return btn_type; +} + +static int rt722_sdca_headset_detect(struct rt722_sdca_priv *rt722) +{ + unsigned int det_mode; + int ret; + + /* get detected_mode */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + switch (det_mode) { + case 0x00: + rt722->jack_type = 0; + break; + case 0x03: + rt722->jack_type = SND_JACK_HEADPHONE; + break; + case 0x05: + rt722->jack_type = SND_JACK_HEADSET; + break; + } + + /* write selected_mode */ + if (det_mode) { + ret = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_SELECTED_MODE, 0), det_mode); + if (ret < 0) + goto io_error; + } + + dev_dbg(&rt722->slave->dev, + "%s, detected_mode=0x%x\n", __func__, det_mode); + + return 0; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); + return ret; +} + +static void rt722_sdca_jack_detect_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_detect_work.work); + int btn_type = 0, ret; + + if (!rt722->hs_jack) + return; + + if (!rt722->component->card || !rt722->component->card->instantiated) + return; + + /* SDW_SCP_SDCA_INT_SDCA_6 is used for jack detection */ + if (rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_6 || + rt722->scp_sdca_stat1 & SDW_SCP_SDCA_INT_SDCA_0) { + ret = rt722_sdca_headset_detect(rt722); + if (ret < 0) + return; + } + + /* SDW_SCP_SDCA_INT_SDCA_8 is used for button detection */ + if (rt722->scp_sdca_stat2 & SDW_SCP_SDCA_INT_SDCA_8) + btn_type = rt722_sdca_button_detect(rt722); + + if (rt722->jack_type == 0) + btn_type = 0; + + dev_dbg(&rt722->slave->dev, + "in %s, jack_type=%d\n", __func__, rt722->jack_type); + dev_dbg(&rt722->slave->dev, + "in %s, btn_type=0x%x\n", __func__, btn_type); + dev_dbg(&rt722->slave->dev, + "in %s, scp_sdca_stat1=0x%x, scp_sdca_stat2=0x%x\n", __func__, + rt722->scp_sdca_stat1, rt722->scp_sdca_stat2); + + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } +} + +static void rt722_sdca_btn_check_handler(struct work_struct *work) +{ + struct rt722_sdca_priv *rt722 = + container_of(work, struct rt722_sdca_priv, jack_btn_check_work.work); + int btn_type = 0, ret, idx; + unsigned int det_mode, offset, val; + unsigned char buf[3]; + + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_GE49, + RT722_SDCA_CTL_DETECTED_MODE, 0), &det_mode); + if (ret < 0) + goto io_error; + + /* pin attached */ + if (det_mode) { + /* read UMP message offset */ + ret = regmap_read(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_HID, RT722_SDCA_ENT_HID01, + RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET, 0), &offset); + if (ret < 0) + goto io_error; + + for (idx = 0; idx < sizeof(buf); idx++) { + ret = regmap_read(rt722->regmap, + RT722_BUF_ADDR_HID1 + offset + idx, &val); + if (ret < 0) + goto io_error; + buf[idx] = val & 0xff; + } + + if (buf[0] == 0x11) + btn_type = rt722_sdca_btn_type(&buf[1]); + } else + rt722->jack_type = 0; + + dev_dbg(&rt722->slave->dev, "%s, btn_type=0x%x\n", __func__, btn_type); + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type | btn_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + if (btn_type) { + /* button released */ + snd_soc_jack_report(rt722->hs_jack, rt722->jack_type, + SND_JACK_HEADSET | + SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3); + + mod_delayed_work(system_power_efficient_wq, + &rt722->jack_btn_check_work, msecs_to_jiffies(200)); + } + + return; + +io_error: + pr_err_ratelimited("IO error in %s, ret %d\n", __func__, ret); +} + +static void rt722_sdca_jack_init(struct rt722_sdca_priv *rt722) +{ + mutex_lock(&rt722->calibrate_mutex); + if (rt722->hs_jack) { + /* set SCP_SDCA_IntMask1[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK1, + SDW_SCP_SDCA_INTMASK_SDCA_0 | SDW_SCP_SDCA_INTMASK_SDCA_6); + /* set SCP_SDCA_IntMask2[0]=1 */ + sdw_write_no_pm(rt722->slave, SDW_SCP_SDCA_INTMASK2, + SDW_SCP_SDCA_INTMASK_SDCA_8); + dev_dbg(&rt722->slave->dev, "in %s enable\n", __func__); + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_UNSOL_CTL, 0x016E); + /* set XU(et03h) & XU(et0Dh) to Not bypassed */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU03, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_XU0D, + RT722_SDCA_CTL_SELECTED_MODE, 0), 0); + /* trigger GE interrupt */ + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_GE_RELATED_CTL2, 0x4000, 0x4000); + } + mutex_unlock(&rt722->calibrate_mutex); +} + +static int rt722_sdca_set_jack_detect(struct snd_soc_component *component, + struct snd_soc_jack *hs_jack, void *data) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722->hs_jack = hs_jack; + + ret = pm_runtime_resume_and_get(component->dev); + if (ret < 0) { + if (ret != -EACCES) { + dev_err(component->dev, "%s: failed to resume %d\n", __func__, ret); + return ret; + } + /* pm_runtime not enabled yet */ + dev_dbg(component->dev, "%s: skipping jack init for now\n", __func__); + return 0; + } + + rt722_sdca_jack_init(rt722); + + pm_runtime_mark_last_busy(component->dev); + pm_runtime_put_autosuspend(component->dev); + + return 0; +} + +/* For SDCA control DAC/ADC Gain */ +static int rt722_sdca_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int read_l, read_r, gain_l_val, gain_r_val; + unsigned int adc_vol_flag = 0, changed = 0; + unsigned int lvalue, rvalue; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &lvalue); + regmap_read(rt722->mbq_regmap, mc->rreg, &rvalue); + + /* L Channel */ + gain_l_val = ucontrol->value.integer.value[0]; + if (gain_l_val > mc->max) + gain_l_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_l_val = gain_l_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_l_val = 0x1e00 - ((mc->max - gain_l_val) * interval_offset); + else + gain_l_val = 0 - ((mc->max - gain_l_val) * interval_offset); + gain_l_val &= 0xffff; + } + + /* R Channel */ + gain_r_val = ucontrol->value.integer.value[1]; + if (gain_r_val > mc->max) + gain_r_val = mc->max; + + if (mc->shift == 8) /* boost gain */ + gain_r_val = gain_r_val * tendB; + else { + /* ADC/DAC gain */ + if (adc_vol_flag) + gain_r_val = 0x1e00 - ((mc->max - gain_r_val) * interval_offset); + else + gain_r_val = 0 - ((mc->max - gain_r_val) * interval_offset); + gain_r_val &= 0xffff; + } + + if (lvalue != gain_l_val || rvalue != gain_r_val) + changed = 1; + else + return 0; + + /* Lch*/ + regmap_write(rt722->mbq_regmap, mc->reg, gain_l_val); + + /* Rch */ + regmap_write(rt722->mbq_regmap, mc->rreg, gain_r_val); + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + if (read_r == gain_r_val && read_l == gain_l_val) + return changed; + + return -EIO; +} + +static int rt722_sdca_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + unsigned int read_l, read_r, ctl_l = 0, ctl_r = 0; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + const unsigned int tendB = 0xa00; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume") || + strstr(ucontrol->id.name, "FU0F Capture Volume")) + adc_vol_flag = 1; + + regmap_read(rt722->mbq_regmap, mc->reg, &read_l); + regmap_read(rt722->mbq_regmap, mc->rreg, &read_r); + + if (mc->shift == 8) /* boost gain */ + ctl_l = read_l / tendB; + else { + if (adc_vol_flag) + ctl_l = mc->max - (((0x1e00 - read_l) & 0xffff) / interval_offset); + else + ctl_l = mc->max - (((0 - read_l) & 0xffff) / interval_offset); + } + + if (read_l != read_r) { + if (mc->shift == 8) /* boost gain */ + ctl_r = read_r / tendB; + else { /* ADC/DAC gain */ + if (adc_vol_flag) + ctl_r = mc->max - (((0x1e00 - read_r) & 0xffff) / interval_offset); + else + ctl_r = mc->max - (((0 - read_r) & 0xffff) / interval_offset); + } + } else { + ctl_r = ctl_l; + } + + ucontrol->value.integer.value[0] = ctl_l; + ucontrol->value.integer.value[1] = ctl_r; + + return 0; +} + +static int rt722_sdca_set_fu1e_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err, i; + unsigned int ch_mute; + + for (i = 0; i < ARRAY_SIZE(rt722->fu1e_mixer_mute); i++) { + ch_mute = rt722->fu1e_dapm_mute || rt722->fu1e_mixer_mute[i]; + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute); + if (err < 0) + return err; + } + + return 0; +} + +static int rt722_sdca_fu1e_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int i; + + for (i = 0; i < p->count; i++) + ucontrol->value.integer.value[i] = !rt722->fu1e_mixer_mute[i]; + + return 0; +} + +static int rt722_sdca_fu1e_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + int err, changed = 0, i; + + for (i = 0; i < p->count; i++) { + if (rt722->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i]) + changed = 1; + rt722->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i]; + } + + err = rt722_sdca_set_fu1e_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_set_fu0f_capture_ctl(struct rt722_sdca_priv *rt722) +{ + int err; + unsigned int ch_l, ch_r; + + ch_l = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_l_mute) ? 0x01 : 0x00; + ch_r = (rt722->fu0f_dapm_mute || rt722->fu0f_mixer_r_mute) ? 0x01 : 0x00; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_L), ch_l); + if (err < 0) + return err; + + err = regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_MUTE, CH_R), ch_r); + if (err < 0) + return err; + + return 0; +} + +static int rt722_sdca_fu0f_capture_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = !rt722->fu0f_mixer_l_mute; + ucontrol->value.integer.value[1] = !rt722->fu0f_mixer_r_mute; + return 0; +} + +static int rt722_sdca_fu0f_capture_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int err, changed = 0; + + if (rt722->fu0f_mixer_l_mute != !ucontrol->value.integer.value[0] || + rt722->fu0f_mixer_r_mute != !ucontrol->value.integer.value[1]) + changed = 1; + + rt722->fu0f_mixer_l_mute = !ucontrol->value.integer.value[0]; + rt722->fu0f_mixer_r_mute = !ucontrol->value.integer.value[1]; + err = rt722_sdca_set_fu0f_capture_ctl(rt722); + if (err < 0) + return err; + + return changed; +} + +static int rt722_sdca_fu_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + + if (p->max == 1) + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + else + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = p->count; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = p->max; + return 0; +} + +static int rt722_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int regvalue, ctl, i; + unsigned int adc_vol_flag = 0; + const unsigned int interval_offset = 0xc0; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value); + + if (!adc_vol_flag) /* boost gain */ + ctl = regvalue / boost_step; + else { /* ADC gain */ + if (adc_vol_flag) + ctl = p->max - (((vol_max - regvalue) & 0xffff) / interval_offset); + else + ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset); + } + + ucontrol->value.integer.value[i] = ctl; + } + + return 0; +} + +static int rt722_sdca_dmic_set_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_kcontrol_chip(kcontrol); + struct rt722_sdca_dmic_kctrl_priv *p = + (struct rt722_sdca_dmic_kctrl_priv *)kcontrol->private_value; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int boost_step = 0x0a00; + unsigned int vol_max = 0x1e00; + unsigned int gain_val[4]; + unsigned int i, adc_vol_flag = 0, changed = 0; + unsigned int regvalue[4]; + const unsigned int interval_offset = 0xc0; + int err; + + if (strstr(ucontrol->id.name, "FU1E Capture Volume")) + adc_vol_flag = 1; + + /* check all channels */ + for (i = 0; i < p->count; i++) { + regmap_read(rt722->mbq_regmap, p->reg_base + i, ®value[i]); + + gain_val[i] = ucontrol->value.integer.value[i]; + if (gain_val[i] > p->max) + gain_val[i] = p->max; + + if (!adc_vol_flag) /* boost gain */ + gain_val[i] = gain_val[i] * boost_step; + else { /* ADC gain */ + gain_val[i] = vol_max - ((p->max - gain_val[i]) * interval_offset); + gain_val[i] &= 0xffff; + } + + if (regvalue[i] != gain_val[i]) + changed = 1; + } + + if (!changed) + return 0; + + for (i = 0; i < p->count; i++) { + err = regmap_write(rt722->mbq_regmap, p->reg_base + i, gain_val[i]); + if (err < 0) + dev_err(&rt722->slave->dev, "%#08x can't be set\n", p->reg_base + i); + } + + return changed; +} + +#define RT722_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \ + ((unsigned long)&(struct rt722_sdca_dmic_kctrl_priv) \ + {.reg_base = xreg_base, .count = xcount, .max = xmax, \ + .invert = xinvert}) + +#define RT722_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .info = rt722_sdca_fu_info, \ + .get = rt722_sdca_fu1e_capture_get, \ + .put = rt722_sdca_fu1e_capture_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)} + +#define RT722_SDCA_EXT_TLV(xname, reg_base, xhandler_get,\ + xhandler_put, xcount, xmax, tlv_array) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \ + .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \ + SNDRV_CTL_ELEM_ACCESS_READWRITE, \ + .tlv.p = (tlv_array), \ + .info = rt722_sdca_fu_info, \ + .get = xhandler_get, .put = xhandler_put, \ + .private_value = RT722_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) } + +static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6525, 75, 0); +static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, -1725, 75, 0); +static const DECLARE_TLV_DB_SCALE(boost_vol_tlv, 0, 1000, 0); + +static const struct snd_kcontrol_new rt722_sdca_controls[] = { + /* Headphone playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU05 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* Headset mic capture settings */ + SOC_DOUBLE_EXT("FU0F Capture Switch", SND_SOC_NOPM, 0, 1, 1, 0, + rt722_sdca_fu0f_capture_get, rt722_sdca_fu0f_capture_put), + SOC_DOUBLE_R_EXT_TLV("FU0F Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU0F, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x3f, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, mic_vol_tlv), + SOC_DOUBLE_R_EXT_TLV("FU33 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_L), + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PLATFORM_FU44, + RT722_SDCA_CTL_FU_CH_GAIN, CH_R), 8, 3, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, boost_vol_tlv), + /* AMP playback settings */ + SOC_DOUBLE_R_EXT_TLV("FU06 Playback Volume", + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_L), + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_VOLUME, CH_R), 0, 0x57, 0, + rt722_sdca_set_gain_get, rt722_sdca_set_gain_put, out_vol_tlv), + /* DMIC capture settings */ + RT722_SDCA_FU_CTRL("FU1E Capture Switch", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_MUTE, CH_01), 1, 1, 4), + RT722_SDCA_EXT_TLV("FU1E Capture Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_USER_FU1E, + RT722_SDCA_CTL_FU_VOLUME, CH_01), + rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, + 4, 0x3f, mic_vol_tlv), + RT722_SDCA_EXT_TLV("FU15 Boost Volume", + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_FU15, + RT722_SDCA_CTL_FU_CH_GAIN, CH_01), + rt722_sdca_dmic_set_gain_get, rt722_sdca_dmic_set_gain_put, + 4, 3, boost_vol_tlv), +}; + +static int rt722_sdca_adc_mux_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned int val = 0, mask_sft; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val); + + ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7; + + return 0; +} + +static int rt722_sdca_adc_mux_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_dapm_kcontrol_component(kcontrol); + struct snd_soc_dapm_context *dapm = + snd_soc_dapm_kcontrol_dapm(kcontrol); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; + unsigned int *item = ucontrol->value.enumerated.item; + unsigned int val, val2 = 0, change, mask_sft; + + if (item[0] >= e->items) + return -EINVAL; + + if (strstr(ucontrol->id.name, "ADC 22 Mux")) + mask_sft = 12; + else if (strstr(ucontrol->id.name, "ADC 24 Mux")) + mask_sft = 4; + else if (strstr(ucontrol->id.name, "ADC 25 Mux")) + mask_sft = 0; + else + return -EINVAL; + + val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l; + + rt722_sdca_index_read(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, &val2); + val2 = (0x7 << mask_sft) & val2; + + if (val == val2) + change = 0; + else + change = 1; + + if (change) + rt722_sdca_index_update_bits(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_MUX_CTL0, 0x7 << mask_sft, + val << mask_sft); + + snd_soc_dapm_mux_update_power(dapm, kcontrol, + item[0], e, NULL); + + return change; +} + +static const char * const adc22_mux_text[] = { + "MIC2", + "LINE1", + "LINE2", +}; + +static const char * const adc07_10_mux_text[] = { + "DMIC1", + "DMIC2", +}; + +static SOC_ENUM_SINGLE_DECL( + rt722_adc22_enum, SND_SOC_NOPM, 0, adc22_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc24_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static SOC_ENUM_SINGLE_DECL( + rt722_adc25_enum, SND_SOC_NOPM, 0, adc07_10_mux_text); + +static const struct snd_kcontrol_new rt722_sdca_adc22_mux = + SOC_DAPM_ENUM_EXT("ADC 22 Mux", rt722_adc22_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc24_mux = + SOC_DAPM_ENUM_EXT("ADC 24 Mux", rt722_adc24_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static const struct snd_kcontrol_new rt722_sdca_adc25_mux = + SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt722_adc25_enum, + rt722_sdca_adc_mux_get, rt722_sdca_adc_mux_put); + +static int rt722_sdca_fu42_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_USER_FU05, + RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt722_sdca_fu21_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char unmute = 0x0, mute = 0x1; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L), unmute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R), unmute); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_L), mute); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_USER_FU06, + RT722_SDCA_CTL_FU_MUTE, CH_R), mute); + break; + } + return 0; +} + +static int rt722_sdca_fu113_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu1e_dapm_mute = false; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu1e_dapm_mute = true; + rt722_sdca_set_fu1e_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_fu36_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + rt722->fu0f_dapm_mute = false; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + case SND_SOC_DAPM_PRE_PMD: + rt722->fu0f_dapm_mute = true; + rt722_sdca_set_fu0f_capture_ctl(rt722); + break; + } + return 0; +} + +static int rt722_sdca_pde47_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE40, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde23_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_PDE23, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde11_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_PDE2A, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static int rt722_sdca_pde12_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + unsigned char ps0 = 0x0, ps3 = 0x3; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps0); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_PDE12, + RT722_SDCA_CTL_REQ_POWER_STATE, 0), ps3); + break; + } + return 0; +} + +static const struct snd_soc_dapm_widget rt722_sdca_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("HP"), + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_INPUT("MIC2"), + SND_SOC_DAPM_INPUT("LINE1"), + SND_SOC_DAPM_INPUT("LINE2"), + SND_SOC_DAPM_INPUT("DMIC1_2"), + SND_SOC_DAPM_INPUT("DMIC3_4"), + + SND_SOC_DAPM_SUPPLY("PDE 23", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde23_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 47", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde47_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde11_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SUPPLY("PDE 12", SND_SOC_NOPM, 0, 0, + rt722_sdca_pde12_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_DAC_E("FU 21", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu21_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_DAC_E("FU 42", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu42_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 36", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu36_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_ADC_E("FU 113", NULL, SND_SOC_NOPM, 0, 0, + rt722_sdca_fu113_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_MUX("ADC 22 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc22_mux), + SND_SOC_DAPM_MUX("ADC 24 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc24_mux), + SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0, + &rt722_sdca_adc25_mux), + + SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Headphone Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Headset Capture", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("DP3RX", "DP3 Speaker Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("DP6TX", "DP6 DMic Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route rt722_sdca_audio_map[] = { + {"FU 42", NULL, "DP1RX"}, + {"FU 21", NULL, "DP3RX"}, + + {"ADC 22 Mux", "MIC2", "MIC2"}, + {"ADC 22 Mux", "LINE1", "LINE1"}, + {"ADC 22 Mux", "LINE2", "LINE2"}, + {"ADC 24 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 24 Mux", "DMIC2", "DMIC3_4"}, + {"ADC 25 Mux", "DMIC1", "DMIC1_2"}, + {"ADC 25 Mux", "DMIC2", "DMIC3_4"}, + {"FU 36", NULL, "PDE 12"}, + {"FU 36", NULL, "ADC 22 Mux"}, + {"FU 113", NULL, "PDE 11"}, + {"FU 113", NULL, "ADC 24 Mux"}, + {"FU 113", NULL, "ADC 25 Mux"}, + {"DP2TX", NULL, "FU 36"}, + {"DP6TX", NULL, "FU 113"}, + + {"HP", NULL, "PDE 47"}, + {"HP", NULL, "FU 42"}, + {"SPK", NULL, "PDE 23"}, + {"SPK", NULL, "FU 21"}, +}; + +static int rt722_sdca_parse_dt(struct rt722_sdca_priv *rt722, struct device *dev) +{ + device_property_read_u32(dev, "realtek,jd-src", &rt722->jd_src); + + return 0; +} + +static int rt722_sdca_probe(struct snd_soc_component *component) +{ + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + int ret; + + rt722_sdca_parse_dt(rt722, &rt722->slave->dev); + rt722->component = component; + + ret = pm_runtime_resume(component->dev); + if (ret < 0 && ret != -EACCES) + return ret; + + return 0; +} + +static const struct snd_soc_component_driver soc_sdca_dev_rt722 = { + .probe = rt722_sdca_probe, + .controls = rt722_sdca_controls, + .num_controls = ARRAY_SIZE(rt722_sdca_controls), + .dapm_widgets = rt722_sdca_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(rt722_sdca_dapm_widgets), + .dapm_routes = rt722_sdca_audio_map, + .num_dapm_routes = ARRAY_SIZE(rt722_sdca_audio_map), + .set_jack = rt722_sdca_set_jack_detect, + .endianness = 1, +}; + +static int rt722_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream, + int direction) +{ + snd_soc_dai_dma_data_set(dai, direction, sdw_stream); + + return 0; +} + +static void rt722_sdca_shutdown(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_soc_dai_set_dma_data(dai, substream, NULL); +} + +static int rt722_sdca_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_config stream_config; + struct sdw_port_config port_config; + enum sdw_data_direction direction; + struct sdw_stream_runtime *sdw_stream; + int retval, port, num_channels; + unsigned int sampling_rate; + + dev_dbg(dai->dev, "%s %s", __func__, dai->name); + sdw_stream = snd_soc_dai_get_dma_data(dai, substream); + + if (!sdw_stream) + return -EINVAL; + + if (!rt722->slave) + return -EINVAL; + + /* + * RT722_AIF1 with port = 1 for headphone playback + * RT722_AIF1 with port = 2 for headset-mic capture + * RT722_AIF2 with port = 3 for speaker playback + * RT722_AIF3 with port = 6 for digital-mic capture + */ + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + direction = SDW_DATA_DIR_RX; + if (dai->id == RT722_AIF1) + port = 1; + else if (dai->id == RT722_AIF2) + port = 3; + else + return -EINVAL; + } else { + direction = SDW_DATA_DIR_TX; + if (dai->id == RT722_AIF1) + port = 2; + else if (dai->id == RT722_AIF3) + port = 6; + else + return -EINVAL; + } + stream_config.frame_rate = params_rate(params); + stream_config.ch_count = params_channels(params); + stream_config.bps = snd_pcm_format_width(params_format(params)); + stream_config.direction = direction; + + num_channels = params_channels(params); + port_config.ch_mask = GENMASK(num_channels - 1, 0); + port_config.num = port; + + retval = sdw_stream_add_slave(rt722->slave, &stream_config, + &port_config, 1, sdw_stream); + if (retval) { + dev_err(dai->dev, "Unable to configure port\n"); + return retval; + } + + if (params_channels(params) > 16) { + dev_err(component->dev, "Unsupported channels %d\n", + params_channels(params)); + return -EINVAL; + } + + /* sampling rate configuration */ + switch (params_rate(params)) { + case 44100: + sampling_rate = RT722_SDCA_RATE_44100HZ; + break; + case 48000: + sampling_rate = RT722_SDCA_RATE_48000HZ; + break; + case 96000: + sampling_rate = RT722_SDCA_RATE_96000HZ; + break; + case 192000: + sampling_rate = RT722_SDCA_RATE_192000HZ; + break; + default: + dev_err(component->dev, "Rate %d is not supported\n", + params_rate(params)); + return -EINVAL; + } + + /* set sampling frequency */ + if (dai->id == RT722_AIF1) { + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS01, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_JACK_CODEC, RT722_SDCA_ENT_CS11, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + } + + if (dai->id == RT722_AIF2) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_CS31, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + if (dai->id == RT722_AIF3) + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_CS1F, + RT722_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), sampling_rate); + + return 0; +} + +static int rt722_sdca_pcm_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct rt722_sdca_priv *rt722 = snd_soc_component_get_drvdata(component); + struct sdw_stream_runtime *sdw_stream = + snd_soc_dai_get_dma_data(dai, substream); + + if (!rt722->slave) + return -EINVAL; + + sdw_stream_remove_slave(rt722->slave, sdw_stream); + return 0; +} + +#define RT722_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000) +#define RT722_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE) + +static const struct snd_soc_dai_ops rt722_sdca_ops = { + .hw_params = rt722_sdca_pcm_hw_params, + .hw_free = rt722_sdca_pcm_hw_free, + .set_stream = rt722_sdca_set_sdw_stream, + .shutdown = rt722_sdca_shutdown, +}; + +static struct snd_soc_dai_driver rt722_sdca_dai[] = { + { + .name = "rt722-sdca-aif1", + .id = RT722_AIF1, + .playback = { + .stream_name = "DP1 Headphone Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .capture = { + .stream_name = "DP2 Headset Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif2", + .id = RT722_AIF2, + .playback = { + .stream_name = "DP3 Speaker Playback", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + }, + { + .name = "rt722-sdca-aif3", + .id = RT722_AIF3, + .capture = { + .stream_name = "DP6 DMic Capture", + .channels_min = 1, + .channels_max = 2, + .rates = RT722_STEREO_RATES, + .formats = RT722_FORMATS, + }, + .ops = &rt722_sdca_ops, + } +}; + +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722; + + rt722 = devm_kzalloc(dev, sizeof(*rt722), GFP_KERNEL); + if (!rt722) + return -ENOMEM; + + dev_set_drvdata(dev, rt722); + rt722->slave = slave; + rt722->regmap = regmap; + rt722->mbq_regmap = mbq_regmap; + + mutex_init(&rt722->calibrate_mutex); + mutex_init(&rt722->disable_irq_lock); + + INIT_DELAYED_WORK(&rt722->jack_detect_work, rt722_sdca_jack_detect_handler); + INIT_DELAYED_WORK(&rt722->jack_btn_check_work, rt722_sdca_btn_check_handler); + + /* + * Mark hw_init to false + * HW init will be performed when device reports present + */ + rt722->hw_init = false; + rt722->first_hw_init = false; + rt722->fu1e_dapm_mute = true; + rt722->fu0f_dapm_mute = true; + rt722->fu0f_mixer_l_mute = rt722->fu0f_mixer_r_mute = true; + rt722->fu1e_mixer_mute[0] = rt722->fu1e_mixer_mute[1] = + rt722->fu1e_mixer_mute[2] = rt722->fu1e_mixer_mute[3] = true; + + return devm_snd_soc_register_component(dev, + &soc_sdca_dev_rt722, rt722_sdca_dai, ARRAY_SIZE(rt722_sdca_dai)); +} + +static void rt722_sdca_dmic_preset(struct rt722_sdca_priv *rt722) +{ + /* Set AD07 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC0A_08_PDE_FLOAT_CTL, 0x2a29); + /* Set AD10 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC10_PDE_FLOAT_CTL, 0x2a00); + /* Set DMIC1/DMIC2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC1_2_PDE_FLOAT_CTL, 0x2a2a); + /* Set DMIC2 IT entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_ENT_FLOAT_CTL, 0x2626); + /* Set AD10 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_ENT_FLOAT_CTL, 0x1e00); + /* Set DMIC2 FU entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515); + /* Set AD10 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_ADC_VOL_CH_FLOAT_CTL, 0x0304); + /* Set DMIC2 FU channel floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304); + /* vf71f_r12_07_06 and vf71f_r13_07_06 = 2’b00 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, + RT722_HDA_LEGACY_CONFIG_CTL0, 0x0000); + /* Enable vf707_r12_05/vf707_r13_05 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT722_SDCA_ENT_IT26, + RT722_SDCA_CTL_VENDOR_DEF, 0), 0x01); + /* Fine tune PDE2A latency */ + regmap_write(rt722->regmap, 0x2f5c, 0x25); +} + +static void rt722_sdca_amp_preset(struct rt722_sdca_priv *rt722) +{ + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* Reset dc_cal_top */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0x702c); + /* W1C Trigger Calibration */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DC_CALIB_CTRL, + 0xf02d); + /* Set DAC02/ClassD power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_AMP_PDE_FLOAT_CTL, + 0x2323); + /* Set EAPD high */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_EAPD_CTL, + 0x0002); + /* Enable vf707_r14 */ + regmap_write(rt722->regmap, + SDW_SDCA_CTL(FUNC_NUM_AMP, RT722_SDCA_ENT_OT23, + RT722_SDCA_CTL_VENDOR_DEF, CH_08), 0x04); +} + +static void rt722_sdca_jack_preset(struct rt722_sdca_priv *rt722) +{ + int loop_check, chk_cnt = 100, ret; + unsigned int calib_status = 0; + + /* Read eFuse */ + rt722_sdca_index_write(rt722, RT722_VENDOR_SPK_EFUSE, RT722_DC_CALIB_CTRL, + 0x4808); + /* Button A, B, C, D bypass mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL4, + 0xcf00); + /* HID1 slot enable */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL5, + 0x000f); + /* Report ID for HID1 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL0, + 0x1100); + /* OSC/OOC for slot 2, 3 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_UMP_HID_CTL7, + 0x0c12); + /* Set JD de-bounce clock control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_JD_CTRL1, + 0x7002); + /* Set DVQ=01 */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_CLSD_CTRL6, + 0xc215); + /* FSM switch to calibration manual mode */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_FSM_CTL, + 0x4100); + /* W1C Trigger DC calibration (HP) */ + rt722_sdca_index_write(rt722, RT722_VENDOR_CALI, RT722_DAC_DC_CALI_CTL3, + 0x008d); + /* check HP calibration FSM status */ + for (loop_check = 0; loop_check < chk_cnt; loop_check++) { + ret = rt722_sdca_index_read(rt722, RT722_VENDOR_CALI, + RT722_DAC_DC_CALI_CTL3, &calib_status); + if (ret < 0 || loop_check == chk_cnt) + dev_dbg(&rt722->slave->dev, "calibration failed!, ret=%d\n", ret); + if ((calib_status & 0x0040) == 0x0) + break; + } + /* Release HP-JD, EN_CBJ_TIE_GL/R open, en_osw gating auto done bit */ + rt722_sdca_index_write(rt722, RT722_VENDOR_REG, RT722_DIGITAL_MISC_CTRL4, + 0x0010); + /* Set ADC09 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ADC0A_08_PDE_FLOAT_CTL, + 0x2a12); + /* Set MIC2 and LINE1 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_MIC2_LINE2_PDE_FLOAT_CTL, + 0x3429); + /* Set ET41h and LINE2 power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_ET41_LINE2_PDE_FLOAT_CTL, + 0x4112); + /* Set DAC03 and HP power entity floating control */ + rt722_sdca_index_write(rt722, RT722_VENDOR_HDA_CTL, RT722_DAC03_HP_PDE_FLOAT_CTL, + 0x4040); + /* Fine tune PDE40 latency */ + regmap_write(rt722->regmap, 0x2f58, 0x07); +} + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave) +{ + struct rt722_sdca_priv *rt722 = dev_get_drvdata(dev); + + rt722->disable_irq = false; + + if (rt722->hw_init) + return 0; + + if (rt722->first_hw_init) { + regcache_cache_only(rt722->regmap, false); + regcache_cache_bypass(rt722->regmap, true); + regcache_cache_only(rt722->mbq_regmap, false); + regcache_cache_bypass(rt722->mbq_regmap, true); + } else { + /* + * PM runtime is only enabled when a Slave reports as Attached + */ + + /* set autosuspend parameters */ + pm_runtime_set_autosuspend_delay(&slave->dev, 3000); + pm_runtime_use_autosuspend(&slave->dev); + + /* update count of parent 'active' children */ + pm_runtime_set_active(&slave->dev); + + /* make sure the device does not suspend immediately */ + pm_runtime_mark_last_busy(&slave->dev); + + pm_runtime_enable(&slave->dev); + } + + pm_runtime_get_noresume(&slave->dev); + + rt722_sdca_dmic_preset(rt722); + rt722_sdca_amp_preset(rt722); + rt722_sdca_jack_preset(rt722); + + if (rt722->first_hw_init) { + regcache_cache_bypass(rt722->regmap, false); + regcache_mark_dirty(rt722->regmap); + regcache_cache_bypass(rt722->mbq_regmap, false); + regcache_mark_dirty(rt722->mbq_regmap); + } else + rt722->first_hw_init = true; + + /* Mark Slave initialization complete */ + rt722->hw_init = true; + + pm_runtime_mark_last_busy(&slave->dev); + pm_runtime_put_autosuspend(&slave->dev); + + dev_dbg(&slave->dev, "%s hw_init complete\n", __func__); + return 0; +} + +MODULE_DESCRIPTION("ASoC RT722 SDCA SDW driver"); +MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/rt722-sdca.h b/sound/soc/codecs/rt722-sdca.h new file mode 100644 index 000000000000..44af8901352e --- /dev/null +++ b/sound/soc/codecs/rt722-sdca.h @@ -0,0 +1,237 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * rt722-sdca.h -- RT722 SDCA ALSA SoC audio driver header + * + * Copyright(c) 2023 Realtek Semiconductor Corp. + */ + +#ifndef __RT722_H__ +#define __RT722_H__ + +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/soc.h> +#include <linux/workqueue.h> + +struct rt722_sdca_priv { + struct regmap *regmap; + struct regmap *mbq_regmap; + struct snd_soc_component *component; + struct sdw_slave *slave; + struct sdw_bus_params params; + bool hw_init; + bool first_hw_init; + struct mutex calibrate_mutex; + struct mutex disable_irq_lock; + bool disable_irq; + /* For Headset jack & Headphone */ + unsigned int scp_sdca_stat1; + unsigned int scp_sdca_stat2; + struct snd_soc_jack *hs_jack; + struct delayed_work jack_detect_work; + struct delayed_work jack_btn_check_work; + int jack_type; + int jd_src; + bool fu0f_dapm_mute; + bool fu0f_mixer_l_mute; + bool fu0f_mixer_r_mute; + /* For DMIC */ + bool fu1e_dapm_mute; + bool fu1e_mixer_mute[4]; +}; + +struct rt722_sdca_dmic_kctrl_priv { + unsigned int reg_base; + unsigned int count; + unsigned int max; + unsigned int invert; +}; + +/* NID */ +#define RT722_VENDOR_REG 0x20 +#define RT722_VENDOR_CALI 0x58 +#define RT722_VENDOR_SPK_EFUSE 0x5c +#define RT722_VENDOR_IMS_DRE 0x5b +#define RT722_VENDOR_ANALOG_CTL 0x5f +#define RT722_VENDOR_HDA_CTL 0x61 + +/* Index (NID:20h) */ +#define RT722_JD_PRODUCT_NUM 0x00 +#define RT722_ANALOG_BIAS_CTL3 0x04 +#define RT722_JD_CTRL1 0x09 +#define RT722_LDO2_3_CTL1 0x0e +#define RT722_LDO1_CTL 0x1a +#define RT722_HP_JD_CTRL 0x24 +#define RT722_CLSD_CTRL6 0x3c +#define RT722_COMBO_JACK_AUTO_CTL1 0x45 +#define RT722_COMBO_JACK_AUTO_CTL2 0x46 +#define RT722_COMBO_JACK_AUTO_CTL3 0x47 +#define RT722_DIGITAL_MISC_CTRL4 0x4a +#define RT722_FSM_CTL 0x67 +#define RT722_SDCA_INTR_REC 0x82 +#define RT722_SW_CONFIG1 0x8a +#define RT722_SW_CONFIG2 0x8b + +/* Index (NID:58h) */ +#define RT722_DAC_DC_CALI_CTL0 0x00 +#define RT722_DAC_DC_CALI_CTL1 0x01 +#define RT722_DAC_DC_CALI_CTL2 0x02 +#define RT722_DAC_DC_CALI_CTL3 0x03 + +/* Index (NID:59h) */ +#define RT722_ULTRA_SOUND_DETECTOR6 0x1e + +/* Index (NID:5bh) */ +#define RT722_IMS_DIGITAL_CTL1 0x00 +#define RT722_IMS_DIGITAL_CTL5 0x05 +#define RT722_HP_DETECT_RLDET_CTL1 0x29 +#define RT722_HP_DETECT_RLDET_CTL2 0x2a + +/* Index (NID:5fh) */ +#define RT722_MISC_POWER_CTL0 0x00 +#define RT722_MISC_POWER_CTL7 0x08 + +/* Index (NID:61h) */ +#define RT722_HDA_LEGACY_MUX_CTL0 0x00 +#define RT722_HDA_LEGACY_UNSOL_CTL 0x03 +#define RT722_HDA_LEGACY_CONFIG_CTL0 0x06 +#define RT722_HDA_LEGACY_RESET_CTL 0x08 +#define RT722_HDA_LEGACY_GPIO_WAKE_EN_CTL 0x0e +#define RT722_DMIC_ENT_FLOAT_CTL 0x10 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL0 0x11 +#define RT722_DMIC_GAIN_ENT_FLOAT_CTL2 0x13 +#define RT722_ADC_ENT_FLOAT_CTL 0x15 +#define RT722_ADC_VOL_CH_FLOAT_CTL 0x17 +#define RT722_ADC_SAMPLE_RATE_FLOAT 0x18 +#define RT722_DAC03_HP_PDE_FLOAT_CTL 0x22 +#define RT722_MIC2_LINE2_PDE_FLOAT_CTL 0x23 +#define RT722_ET41_LINE2_PDE_FLOAT_CTL 0x24 +#define RT722_ADC0A_08_PDE_FLOAT_CTL 0x25 +#define RT722_ADC10_PDE_FLOAT_CTL 0x26 +#define RT722_DMIC1_2_PDE_FLOAT_CTL 0x28 +#define RT722_AMP_PDE_FLOAT_CTL 0x29 +#define RT722_I2S_IN_OUT_PDE_FLOAT_CTL 0x2f +#define RT722_GE_RELATED_CTL1 0x45 +#define RT722_GE_RELATED_CTL2 0x46 +#define RT722_MIXER_CTL0 0x52 +#define RT722_MIXER_CTL1 0x53 +#define RT722_EAPD_CTL 0x55 +#define RT722_UMP_HID_CTL0 0x60 +#define RT722_UMP_HID_CTL1 0x61 +#define RT722_UMP_HID_CTL2 0x62 +#define RT722_UMP_HID_CTL3 0x63 +#define RT722_UMP_HID_CTL4 0x64 +#define RT722_UMP_HID_CTL5 0x65 +#define RT722_UMP_HID_CTL6 0x66 +#define RT722_UMP_HID_CTL7 0x67 +#define RT722_UMP_HID_CTL8 0x68 + +/* Parameter & Verb control 01 (0x1a)(NID:20h) */ +#define RT722_HIDDEN_REG_SW_RESET (0x1 << 14) + +/* combo jack auto switch control 2 (0x46)(NID:20h) */ +#define RT722_COMBOJACK_AUTO_DET_STATUS (0x1 << 11) +#define RT722_COMBOJACK_AUTO_DET_TRS (0x1 << 10) +#define RT722_COMBOJACK_AUTO_DET_CTIA (0x1 << 9) +#define RT722_COMBOJACK_AUTO_DET_OMTP (0x1 << 8) + +/* DAC calibration control (0x00)(NID:58h) */ +#define RT722_DC_CALIB_CTRL (0x1 << 16) +/* DAC DC offset calibration control-1 (0x01)(NID:58h) */ +#define RT722_PDM_DC_CALIB_STATUS (0x1 << 15) + +#define RT722_EAPD_HIGH 0x2 +#define RT722_EAPD_LOW 0x0 + +/* Buffer address for HID */ +#define RT722_BUF_ADDR_HID1 0x44030000 +#define RT722_BUF_ADDR_HID2 0x44030020 + +/* RT722 SDCA Control - function number */ +#define FUNC_NUM_JACK_CODEC 0x01 +#define FUNC_NUM_MIC_ARRAY 0x02 +#define FUNC_NUM_HID 0x03 +#define FUNC_NUM_AMP 0x04 + +/* RT722 SDCA entity */ +#define RT722_SDCA_ENT_HID01 0x01 +#define RT722_SDCA_ENT_GE49 0x49 +#define RT722_SDCA_ENT_USER_FU05 0x05 +#define RT722_SDCA_ENT_USER_FU06 0x06 +#define RT722_SDCA_ENT_USER_FU0F 0x0f +#define RT722_SDCA_ENT_USER_FU10 0x19 +#define RT722_SDCA_ENT_USER_FU1E 0x1e +#define RT722_SDCA_ENT_FU15 0x15 +#define RT722_SDCA_ENT_PDE23 0x23 +#define RT722_SDCA_ENT_PDE40 0x40 +#define RT722_SDCA_ENT_PDE11 0x11 +#define RT722_SDCA_ENT_PDE12 0x12 +#define RT722_SDCA_ENT_PDE2A 0x2a +#define RT722_SDCA_ENT_CS01 0x01 +#define RT722_SDCA_ENT_CS11 0x11 +#define RT722_SDCA_ENT_CS1F 0x1f +#define RT722_SDCA_ENT_CS1C 0x1c +#define RT722_SDCA_ENT_CS31 0x31 +#define RT722_SDCA_ENT_OT23 0x42 +#define RT722_SDCA_ENT_IT26 0x26 +#define RT722_SDCA_ENT_IT09 0x09 +#define RT722_SDCA_ENT_PLATFORM_FU15 0x15 +#define RT722_SDCA_ENT_PLATFORM_FU44 0x44 +#define RT722_SDCA_ENT_XU03 0x03 +#define RT722_SDCA_ENT_XU0D 0x0d + +/* RT722 SDCA control */ +#define RT722_SDCA_CTL_SAMPLE_FREQ_INDEX 0x10 +#define RT722_SDCA_CTL_FU_MUTE 0x01 +#define RT722_SDCA_CTL_FU_VOLUME 0x02 +#define RT722_SDCA_CTL_HIDTX_CURRENT_OWNER 0x10 +#define RT722_SDCA_CTL_HIDTX_SET_OWNER_TO_DEVICE 0x11 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_OFFSET 0x12 +#define RT722_SDCA_CTL_HIDTX_MESSAGE_LENGTH 0x13 +#define RT722_SDCA_CTL_SELECTED_MODE 0x01 +#define RT722_SDCA_CTL_DETECTED_MODE 0x02 +#define RT722_SDCA_CTL_REQ_POWER_STATE 0x01 +#define RT722_SDCA_CTL_VENDOR_DEF 0x30 +#define RT722_SDCA_CTL_FU_CH_GAIN 0x0b + +/* RT722 SDCA channel */ +#define CH_L 0x01 +#define CH_R 0x02 +#define CH_01 0x01 +#define CH_02 0x02 +#define CH_03 0x03 +#define CH_04 0x04 +#define CH_08 0x08 + +/* sample frequency index */ +#define RT722_SDCA_RATE_16000HZ 0x04 +#define RT722_SDCA_RATE_32000HZ 0x07 +#define RT722_SDCA_RATE_44100HZ 0x08 +#define RT722_SDCA_RATE_48000HZ 0x09 +#define RT722_SDCA_RATE_96000HZ 0x0b +#define RT722_SDCA_RATE_192000HZ 0x0d + +enum { + RT722_AIF1, /* For headset mic and headphone */ + RT722_AIF2, /* For speaker */ + RT722_AIF3, /* For dmic */ + RT722_AIFS, +}; + +enum rt722_sdca_jd_src { + RT722_JD_NULL, + RT722_JD1, +}; + +int rt722_sdca_io_init(struct device *dev, struct sdw_slave *slave); +int rt722_sdca_init(struct device *dev, struct regmap *regmap, + struct regmap *mbq_regmap, struct sdw_slave *slave); +int rt722_sdca_index_write(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int value); +int rt722_sdca_index_read(struct rt722_sdca_priv *rt722, + unsigned int nid, unsigned int reg, unsigned int *value); + +int rt722_sdca_jack_detect(struct rt722_sdca_priv *rt722, bool *hp, bool *mic); +#endif /* __RT722_H__ */ diff --git a/sound/soc/codecs/rt9120.c b/sound/soc/codecs/rt9120.c index fcf4fbaed3c7..733a7d130a95 100644 --- a/sound/soc/codecs/rt9120.c +++ b/sound/soc/codecs/rt9120.c @@ -633,7 +633,7 @@ static struct i2c_driver rt9120_driver = { .of_match_table = rt9120_device_table, .pm = &rt9120_pm_ops, }, - .probe_new = rt9120_probe, + .probe = rt9120_probe, .remove = rt9120_remove, }; module_i2c_driver(rt9120_driver); diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c index a916f4619ea3..b22ba95bd0c0 100644 --- a/sound/soc/codecs/sgtl5000.c +++ b/sound/soc/codecs/sgtl5000.c @@ -1826,7 +1826,7 @@ static struct i2c_driver sgtl5000_i2c_driver = { .name = "sgtl5000", .of_match_table = sgtl5000_dt_ids, }, - .probe_new = sgtl5000_i2c_probe, + .probe = sgtl5000_i2c_probe, .remove = sgtl5000_i2c_remove, .shutdown = sgtl5000_i2c_shutdown, .id_table = sgtl5000_id, diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c index b6c132edf3bd..7b9abbc1bd94 100644 --- a/sound/soc/codecs/sma1303.c +++ b/sound/soc/codecs/sma1303.c @@ -1807,7 +1807,7 @@ static struct i2c_driver sma1303_i2c_driver = { .name = "sma1303", .of_match_table = sma1303_of_match, }, - .probe_new = sma1303_i2c_probe, + .probe = sma1303_i2c_probe, .remove = sma1303_i2c_remove, .id_table = sma1303_i2c_id, }; diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c index a40fd20df984..93af8e209b05 100644 --- a/sound/soc/codecs/src4xxx-i2c.c +++ b/sound/soc/codecs/src4xxx-i2c.c @@ -36,7 +36,7 @@ static struct i2c_driver src4xxx_i2c_driver = { .name = "src4xxx", .of_match_table = of_match_ptr(src4xxx_of_match), }, - .probe_new = src4xxx_i2c_probe, + .probe = src4xxx_i2c_probe, .id_table = src4xxx_i2c_ids, }; module_i2c_driver(src4xxx_i2c_driver); diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c index 22cb3b7c8283..d20d897407eb 100644 --- a/sound/soc/codecs/ssm2518.c +++ b/sound/soc/codecs/ssm2518.c @@ -803,7 +803,7 @@ static struct i2c_driver ssm2518_driver = { .name = "ssm2518", .of_match_table = of_match_ptr(ssm2518_dt_ids), }, - .probe_new = ssm2518_i2c_probe, + .probe = ssm2518_i2c_probe, .id_table = ssm2518_i2c_ids, }; module_i2c_driver(ssm2518_driver); diff --git a/sound/soc/codecs/ssm2602-i2c.c b/sound/soc/codecs/ssm2602-i2c.c index 3c85772901f5..596096466cd4 100644 --- a/sound/soc/codecs/ssm2602-i2c.c +++ b/sound/soc/codecs/ssm2602-i2c.c @@ -49,7 +49,7 @@ static struct i2c_driver ssm2602_i2c_driver = { .name = "ssm2602", .of_match_table = ssm2602_of_match, }, - .probe_new = ssm2602_i2c_probe, + .probe = ssm2602_i2c_probe, .id_table = ssm2602_i2c_id, }; module_i2c_driver(ssm2602_i2c_driver); diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c new file mode 100644 index 000000000000..008cb3eb5758 --- /dev/null +++ b/sound/soc/codecs/ssm3515.c @@ -0,0 +1,448 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +// +// Analog Devices' SSM3515 audio amp driver +// +// Copyright (C) The Asahi Linux Contributors + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + + +#define SSM3515_PWR 0x00 +#define SSM3515_PWR_APWDN_EN BIT(7) +#define SSM3515_PWR_BSNS_PWDN BIT(6) +#define SSM3515_PWR_S_RST BIT(1) +#define SSM3515_PWR_SPWDN BIT(0) + +#define SSM3515_GEC 0x01 +#define SSM3515_GEC_EDGE BIT(4) +#define SSM3515_GEC_EDGE_SHIFT 4 +#define SSM3515_GEC_ANA_GAIN GENMASK(1, 0) + +#define SSM3515_DAC 0x02 +#define SSM3515_DAC_HV BIT(7) +#define SSM3515_DAC_MUTE BIT(6) +#define SSM3515_DAC_HPF BIT(5) +#define SSM3515_DAC_LPM BIT(4) +#define SSM3515_DAC_FS GENMASK(2, 0) + +#define SSM3515_DAC_VOL 0x03 + +#define SSM3515_SAI1 0x04 +#define SSM3515_SAI1_DAC_POL BIT(7) +#define SSM3515_SAI1_BCLK_POL BIT(6) +#define SSM3515_SAI1_TDM_BCLKS GENMASK(5, 3) +#define SSM3515_SAI1_FSYNC_MODE BIT(2) +#define SSM3515_SAI1_SDATA_FMT BIT(1) +#define SSM3515_SAI1_SAI_MODE BIT(0) + +#define SSM3515_SAI2 0x05 +#define SSM3515_SAI2_DATA_WIDTH BIT(7) +#define SSM3515_SAI2_AUTO_SLOT BIT(4) +#define SSM3515_SAI2_TDM_SLOT GENMASK(3, 0) + +#define SSM3515_VBAT_OUT 0x06 + +#define SSM3515_STATUS 0x0a +#define SSM3515_STATUS_UVLO_REG BIT(6) +#define SSM3515_STATUS_LIM_EG BIT(5) +#define SSM3515_STATUS_CLIP BIT(4) +#define SSM3515_STATUS_AMP_OC BIT(3) +#define SSM3515_STATUS_OTF BIT(2) +#define SSM3515_STATUS_OTW BIT(1) +#define SSM3515_STATUS_BAT_WARN BIT(0) + +static bool ssm3515_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case SSM3515_STATUS: + case SSM3515_VBAT_OUT: + return true; + + default: + return false; + } +} + +static const struct reg_default ssm3515_reg_defaults[] = { + { SSM3515_PWR, 0x81 }, + { SSM3515_GEC, 0x01 }, + { SSM3515_DAC, 0x32 }, + { SSM3515_DAC_VOL, 0x40 }, + { SSM3515_SAI1, 0x11 }, + { SSM3515_SAI2, 0x00 }, +}; + +static const struct regmap_config ssm3515_i2c_regmap = { + .reg_bits = 8, + .val_bits = 8, + .volatile_reg = ssm3515_volatile_reg, + .max_register = 0xb, + .reg_defaults = ssm3515_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(ssm3515_reg_defaults), + .cache_type = REGCACHE_FLAT, +}; + +struct ssm3515_data { + struct device *dev; + struct regmap *regmap; +}; + +// The specced range is -71.25...24.00 dB with step size of 0.375 dB, +// and a mute item below that. This is represented by -71.62...24.00 dB +// with the mute item mapped onto the low end. +static DECLARE_TLV_DB_MINMAX_MUTE(ssm3515_dac_volume, -7162, 2400); + +static const char * const ssm3515_ana_gain_text[] = { + "8.4 V Span", "12.6 V Span", "14 V Span", "15 V Span", +}; + +static SOC_ENUM_SINGLE_DECL(ssm3515_ana_gain_enum, SSM3515_GEC, + __bf_shf(SSM3515_GEC_ANA_GAIN), + ssm3515_ana_gain_text); + +static const struct snd_kcontrol_new ssm3515_snd_controls[] = { + SOC_SINGLE_TLV("DAC Playback Volume", SSM3515_DAC_VOL, + 0, 255, 1, ssm3515_dac_volume), + SOC_SINGLE("Low EMI Mode Switch", SSM3515_GEC, + __bf_shf(SSM3515_GEC_EDGE), 1, 0), + SOC_SINGLE("Soft Volume Ramping Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HV), 1, 1), + SOC_SINGLE("HPF Switch", SSM3515_DAC, + __bf_shf(SSM3515_DAC_HPF), 1, 0), + SOC_SINGLE("DAC Invert Switch", SSM3515_SAI1, + __bf_shf(SSM3515_SAI1_DAC_POL), 1, 0), + SOC_ENUM("DAC Analog Gain Select", ssm3515_ana_gain_enum), +}; + +static void ssm3515_read_faults(struct snd_soc_component *component) +{ + int ret; + + ret = snd_soc_component_read(component, SSM3515_STATUS); + if (ret <= 0) { + /* + * If the read was erroneous, ASoC core has printed a message, + * and that's all that's appropriate in handling the error here. + */ + return; + } + + dev_err(component->dev, "device reports:%s%s%s%s%s%s%s\n", + FIELD_GET(SSM3515_STATUS_UVLO_REG, ret) ? " voltage regulator fault" : "", + FIELD_GET(SSM3515_STATUS_LIM_EG, ret) ? " limiter engaged" : "", + FIELD_GET(SSM3515_STATUS_CLIP, ret) ? " clipping detected" : "", + FIELD_GET(SSM3515_STATUS_AMP_OC, ret) ? " amp over-current fault" : "", + FIELD_GET(SSM3515_STATUS_OTF, ret) ? " overtemperature fault" : "", + FIELD_GET(SSM3515_STATUS_OTW, ret) ? " overtemperature warning" : "", + FIELD_GET(SSM3515_STATUS_BAT_WARN, ret) ? " bat voltage low warning" : ""); +} + +static int ssm3515_probe(struct snd_soc_component *component) +{ + int ret; + + /* Start out muted */ + ret = snd_soc_component_update_bits(component, SSM3515_DAC, + SSM3515_DAC_MUTE, SSM3515_DAC_MUTE); + if (ret < 0) + return ret; + + /* Disable the 'master power-down' */ + ret = snd_soc_component_update_bits(component, SSM3515_PWR, + SSM3515_PWR_SPWDN, 0); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_mute(struct snd_soc_dai *dai, int mute, int direction) +{ + int ret; + + ret = snd_soc_component_update_bits(dai->component, + SSM3515_DAC, + SSM3515_DAC_MUTE, + FIELD_PREP(SSM3515_DAC_MUTE, mute)); + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + int ret, rateval; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16: + case SNDRV_PCM_FORMAT_S24: + ret = snd_soc_component_update_bits(component, + SSM3515_SAI2, SSM3515_SAI2_DATA_WIDTH, + FIELD_PREP(SSM3515_SAI2_DATA_WIDTH, + params_width(params) == 16)); + if (ret < 0) + return ret; + break; + + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000 ... 12000: + rateval = 0; + break; + case 16000 ... 24000: + rateval = 1; + break; + case 32000 ... 48000: + rateval = 2; + break; + case 64000 ... 96000: + rateval = 3; + break; + case 128000 ... 192000: + rateval = 4; + break; + case 48001 ... 63999: /* this is ...72000 but overlaps */ + rateval = 5; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, + SSM3515_DAC, SSM3515_DAC_FS, + FIELD_PREP(SSM3515_DAC_FS, rateval)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + bool fpol_inv = false; /* non-inverted: frame starts with low-to-high FSYNC */ + int ret; + u8 sai1 = 0; + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_IB_NF: + case SND_SOC_DAIFMT_IB_IF: + sai1 |= SSM3515_SAI1_BCLK_POL; + break; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + fpol_inv = 1; + sai1 &= ~SSM3515_SAI1_SDATA_FMT; /* 1 bit start delay */ + break; + case SND_SOC_DAIFMT_LEFT_J: + fpol_inv = 0; + sai1 |= SSM3515_SAI1_SDATA_FMT; /* no start delay */ + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + case SND_SOC_DAIFMT_IB_IF: + fpol_inv ^= 1; + break; + } + + /* Set the serial input to 'TDM mode' */ + sai1 |= SSM3515_SAI1_SAI_MODE; + + if (fpol_inv) { + /* + * We configure the codec in a 'TDM mode', in which the + * FSYNC_MODE bit of SAI1 is supposed to select between + * what the datasheet calls 'Pulsed FSYNC mode' and '50% + * FSYNC mode'. + * + * Experiments suggest that this bit in fact simply selects + * the FSYNC polarity, so go with that. + */ + sai1 |= SSM3515_SAI1_FSYNC_MODE; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_BCLK_POL | SSM3515_SAI1_SDATA_FMT | + SSM3515_SAI1_SAI_MODE | SSM3515_SAI1_FSYNC_MODE, sai1); + + if (ret < 0) + return ret; + return 0; +} + +static int ssm3515_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, + unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + int slot, tdm_bclks_val, ret; + + if (tx_mask == 0 || rx_mask != 0) + return -EINVAL; + + slot = __ffs(tx_mask); + + if (tx_mask & ~BIT(slot)) + return -EINVAL; + + switch (slot_width) { + case 16: + tdm_bclks_val = 0; + break; + case 24: + tdm_bclks_val = 1; + break; + case 32: + tdm_bclks_val = 2; + break; + case 48: + tdm_bclks_val = 3; + break; + case 64: + tdm_bclks_val = 4; + break; + default: + return -EINVAL; + } + + ret = snd_soc_component_update_bits(component, SSM3515_SAI1, + SSM3515_SAI1_TDM_BCLKS, + FIELD_PREP(SSM3515_SAI1_TDM_BCLKS, tdm_bclks_val)); + if (ret < 0) + return ret; + + ret = snd_soc_component_update_bits(component, SSM3515_SAI2, + SSM3515_SAI2_TDM_SLOT, + FIELD_PREP(SSM3515_SAI2_TDM_SLOT, slot)); + if (ret < 0) + return ret; + + return 0; +} + +static int ssm3515_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + /* + * We don't get live notification of faults, so at least at + * this time, when playback is over, check if we have tripped + * over anything and if so, log it. + */ + ssm3515_read_faults(dai->component); + return 0; +} + +static const struct snd_soc_dai_ops ssm3515_dai_ops = { + .mute_stream = ssm3515_mute, + .hw_params = ssm3515_hw_params, + .set_fmt = ssm3515_set_fmt, + .set_tdm_slot = ssm3515_set_tdm_slot, + .hw_free = ssm3515_hw_free, +}; + +static struct snd_soc_dai_driver ssm3515_dai_driver = { + .name = "SSM3515 SAI", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 1, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ssm3515_dai_ops, +}; + +static const struct snd_soc_dapm_widget ssm3515_dapm_widgets[] = { + SND_SOC_DAPM_DAC("DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OUT"), +}; + +static const struct snd_soc_dapm_route ssm3515_dapm_routes[] = { + {"OUT", NULL, "DAC"}, + {"DAC", NULL, "Playback"}, +}; + +static const struct snd_soc_component_driver ssm3515_asoc_component = { + .probe = ssm3515_probe, + .controls = ssm3515_snd_controls, + .num_controls = ARRAY_SIZE(ssm3515_snd_controls), + .dapm_widgets = ssm3515_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ssm3515_dapm_widgets), + .dapm_routes = ssm3515_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(ssm3515_dapm_routes), + .endianness = 1, +}; + +static int ssm3515_i2c_probe(struct i2c_client *client) +{ + struct ssm3515_data *data; + int ret; + + data = devm_kzalloc(&client->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->dev = &client->dev; + i2c_set_clientdata(client, data); + + data->regmap = devm_regmap_init_i2c(client, &ssm3515_i2c_regmap); + if (IS_ERR(data->regmap)) + return dev_err_probe(data->dev, PTR_ERR(data->regmap), + "initializing register map\n"); + + /* Perform a reset */ + ret = regmap_update_bits(data->regmap, SSM3515_PWR, + SSM3515_PWR_S_RST, SSM3515_PWR_S_RST); + if (ret < 0) + return dev_err_probe(data->dev, ret, + "performing software reset\n"); + regmap_reinit_cache(data->regmap, &ssm3515_i2c_regmap); + + return devm_snd_soc_register_component(data->dev, + &ssm3515_asoc_component, + &ssm3515_dai_driver, 1); +} + +static const struct of_device_id ssm3515_of_match[] = { + { .compatible = "adi,ssm3515" }, + {} +}; +MODULE_DEVICE_TABLE(of, ssm3515_of_match); + +static struct i2c_driver ssm3515_i2c_driver = { + .driver = { + .name = "ssm3515", + .of_match_table = of_match_ptr(ssm3515_of_match), + }, + .probe = ssm3515_i2c_probe, +}; +module_i2c_driver(ssm3515_i2c_driver); + +MODULE_AUTHOR("Martin PoviÅ¡er <povik+lin@cutebit.org>"); +MODULE_DESCRIPTION("ASoC SSM3515 audio amp driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c index 4b0265617c7b..0a6f04d8f636 100644 --- a/sound/soc/codecs/ssm4567.c +++ b/sound/soc/codecs/ssm4567.c @@ -500,7 +500,7 @@ static struct i2c_driver ssm4567_driver = { .of_match_table = of_match_ptr(ssm4567_of_match), .acpi_match_table = ACPI_PTR(ssm4567_acpi_match), }, - .probe_new = ssm4567_i2c_probe, + .probe = ssm4567_i2c_probe, .id_table = ssm4567_i2c_ids, }; module_i2c_driver(ssm4567_driver); diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c index 29af9595dac1..4a694d0bfd68 100644 --- a/sound/soc/codecs/sta32x.c +++ b/sound/soc/codecs/sta32x.c @@ -1167,7 +1167,7 @@ static struct i2c_driver sta32x_i2c_driver = { .name = "sta32x", .of_match_table = of_match_ptr(st32x_dt_ids), }, - .probe_new = sta32x_i2c_probe, + .probe = sta32x_i2c_probe, .id_table = sta32x_i2c_id, }; diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c index b033a5fcd6c0..d05f3fd57661 100644 --- a/sound/soc/codecs/sta350.c +++ b/sound/soc/codecs/sta350.c @@ -1249,7 +1249,7 @@ static struct i2c_driver sta350_i2c_driver = { .name = "sta350", .of_match_table = of_match_ptr(st350_dt_ids), }, - .probe_new = sta350_i2c_probe, + .probe = sta350_i2c_probe, .remove = sta350_i2c_remove, .id_table = sta350_i2c_id, }; diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c index 313957099145..0ac08478ddac 100644 --- a/sound/soc/codecs/sta529.c +++ b/sound/soc/codecs/sta529.c @@ -379,7 +379,7 @@ static struct i2c_driver sta529_i2c_driver = { .name = "sta529", .of_match_table = sta529_of_match, }, - .probe_new = sta529_i2c_probe, + .probe = sta529_i2c_probe, .id_table = sta529_i2c_id, }; diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c index 59a4ea5f6e30..8c9dc318b0e8 100644 --- a/sound/soc/codecs/tas2552.c +++ b/sound/soc/codecs/tas2552.c @@ -761,7 +761,7 @@ static struct i2c_driver tas2552_i2c_driver = { .of_match_table = of_match_ptr(tas2552_of_match), .pm = &tas2552_pm, }, - .probe_new = tas2552_probe, + .probe = tas2552_probe, .remove = tas2552_i2c_remove, .id_table = tas2552_id, }; diff --git a/sound/soc/codecs/tas2562.c b/sound/soc/codecs/tas2562.c index b486d0bd86c9..962c2cdfa017 100644 --- a/sound/soc/codecs/tas2562.c +++ b/sound/soc/codecs/tas2562.c @@ -8,7 +8,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/gpio/consumer.h> @@ -784,7 +783,7 @@ static struct i2c_driver tas2562_i2c_driver = { .name = "tas2562", .of_match_table = of_match_ptr(tas2562_of_match), }, - .probe_new = tas2562_probe, + .probe = tas2562_probe, .id_table = tas2562_id, }; diff --git a/sound/soc/codecs/tas2764.c b/sound/soc/codecs/tas2764.c index 2e0ed3e68fa5..a9838e0738cc 100644 --- a/sound/soc/codecs/tas2764.c +++ b/sound/soc/codecs/tas2764.c @@ -756,7 +756,7 @@ static struct i2c_driver tas2764_i2c_driver = { .name = "tas2764", .of_match_table = of_match_ptr(tas2764_of_match), }, - .probe_new = tas2764_i2c_probe, + .probe = tas2764_i2c_probe, .id_table = tas2764_i2c_id, }; module_i2c_driver(tas2764_i2c_driver); diff --git a/sound/soc/codecs/tas2770.c b/sound/soc/codecs/tas2770.c index 8557759acb1f..99bf402eb566 100644 --- a/sound/soc/codecs/tas2770.c +++ b/sound/soc/codecs/tas2770.c @@ -720,7 +720,7 @@ static struct i2c_driver tas2770_i2c_driver = { .name = "tas2770", .of_match_table = of_match_ptr(tas2770_of_match), }, - .probe_new = tas2770_i2c_probe, + .probe = tas2770_i2c_probe, .id_table = tas2770_i2c_id, }; module_i2c_driver(tas2770_i2c_driver); diff --git a/sound/soc/codecs/tas2780.c b/sound/soc/codecs/tas2780.c index 09e7ada1bca4..86bd6c18a944 100644 --- a/sound/soc/codecs/tas2780.c +++ b/sound/soc/codecs/tas2780.c @@ -645,7 +645,7 @@ static struct i2c_driver tas2780_i2c_driver = { .name = "tas2780", .of_match_table = of_match_ptr(tas2780_of_match), }, - .probe_new = tas2780_i2c_probe, + .probe = tas2780_i2c_probe, .id_table = tas2780_i2c_id, }; module_i2c_driver(tas2780_i2c_driver); diff --git a/sound/soc/codecs/tas2781-comlib.c b/sound/soc/codecs/tas2781-comlib.c new file mode 100644 index 000000000000..a88c6c28a394 --- /dev/null +++ b/sound/soc/codecs/tas2781-comlib.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tas2781-lib.c -- TAS2781 Common functions for HDA and ASoC Audio drivers +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781.h> + +#define TASDEVICE_CRC8_POLYNOMIAL 0x4d + +static const struct regmap_range_cfg tasdevice_ranges[] = { + { + .range_min = 0, + .range_max = 256 * 128, + .selector_reg = TASDEVICE_PAGE_SELECT, + .selector_mask = 0xff, + .selector_shift = 0, + .window_start = 0, + .window_len = 128, + }, +}; + +static const struct regmap_config tasdevice_regmap = { + .reg_bits = 8, + .val_bits = 8, + .cache_type = REGCACHE_RBTREE, + .ranges = tasdevice_ranges, + .num_ranges = ARRAY_SIZE(tasdevice_ranges), + .max_register = 256 * 128, +}; + +static int tasdevice_change_chn_book(struct tasdevice_priv *tas_priv, + unsigned short chn, int book) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + int ret = 0; + + if (chn < tas_priv->ndev) { + struct tasdevice *tasdev = &tas_priv->tasdevice[chn]; + struct regmap *map = tas_priv->regmap; + + if (client->addr != tasdev->dev_addr) { + client->addr = tasdev->dev_addr; + if (tasdev->cur_book == book) { + ret = regmap_write(map, + TASDEVICE_PAGE_SELECT, 0); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + } + goto out; + } + + if (tasdev->cur_book != book) { + ret = regmap_write(map, TASDEVICE_BOOKCTL_REG, book); + if (ret < 0) { + dev_err(tas_priv->dev, "%s, E=%d\n", + __func__, ret); + goto out; + } + tasdev->cur_book = book; + } + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} + +int tasdevice_dev_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int *val) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_read(map, TASDEVICE_PGRG(reg), val); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_read); + +int tasdevice_dev_write(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_write(map, TASDEVICE_PGRG(reg), + value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_write); + +int tasdevice_dev_bulk_write( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_write(map, TASDEVICE_PGRG(reg), + data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + ret = -EINVAL; + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_write); + +int tasdevice_dev_bulk_read(struct tasdevice_priv *tas_priv, + unsigned short chn, unsigned int reg, unsigned char *data, + unsigned int len) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_bulk_read(map, TASDEVICE_PGRG(reg), data, len); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_bulk_read); + +int tasdevice_dev_update_bits( + struct tasdevice_priv *tas_priv, unsigned short chn, + unsigned int reg, unsigned int mask, unsigned int value) +{ + int ret = 0; + + if (chn < tas_priv->ndev) { + struct regmap *map = tas_priv->regmap; + + ret = tasdevice_change_chn_book(tas_priv, chn, + TASDEVICE_BOOK_ID(reg)); + if (ret < 0) + goto out; + + ret = regmap_update_bits(map, TASDEVICE_PGRG(reg), + mask, value); + if (ret < 0) + dev_err(tas_priv->dev, "%s, E=%d\n", __func__, ret); + } else { + dev_err(tas_priv->dev, "%s, no such channel(%d)\n", __func__, + chn); + ret = -EINVAL; + } + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_dev_update_bits); + +struct tasdevice_priv *tasdevice_kzalloc(struct i2c_client *i2c) +{ + struct tasdevice_priv *tas_priv; + + tas_priv = devm_kzalloc(&i2c->dev, sizeof(*tas_priv), GFP_KERNEL); + if (!tas_priv) + return NULL; + tas_priv->dev = &i2c->dev; + tas_priv->client = (void *)i2c; + + return tas_priv; +} +EXPORT_SYMBOL_GPL(tasdevice_kzalloc); + +void tas2781_reset(struct tasdevice_priv *tas_dev) +{ + int ret, i; + + if (tas_dev->reset) { + gpiod_set_value_cansleep(tas_dev->reset, 0); + usleep_range(500, 1000); + gpiod_set_value_cansleep(tas_dev->reset, 1); + } else { + for (i = 0; i < tas_dev->ndev; i++) { + ret = tasdevice_dev_write(tas_dev, i, + TAS2781_REG_SWRESET, + TAS2781_REG_SWRESET_RESET); + if (ret < 0) + dev_err(tas_dev->dev, + "dev %d swreset fail, %d\n", + i, ret); + } + } + usleep_range(1000, 1050); +} +EXPORT_SYMBOL_GPL(tas2781_reset); + +int tascodec_init(struct tasdevice_priv *tas_priv, void *codec, + void (*cont)(const struct firmware *fw, void *context)) +{ + int ret = 0; + + /* Codec Lock Hold to ensure that codec_probe and firmware parsing and + * loading do not simultaneously execute. + */ + mutex_lock(&tas_priv->codec_lock); + + scnprintf(tas_priv->rca_binaryname, 64, "%sRCA%d.bin", + tas_priv->dev_name, tas_priv->ndev); + crc8_populate_msb(tas_priv->crc8_lkp_tbl, TASDEVICE_CRC8_POLYNOMIAL); + tas_priv->codec = codec; + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, + tas_priv->rca_binaryname, tas_priv->dev, GFP_KERNEL, tas_priv, + cont); + if (ret) + dev_err(tas_priv->dev, "request_firmware_nowait err:0x%08x\n", + ret); + + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + return ret; +} +EXPORT_SYMBOL_GPL(tascodec_init); + +int tasdevice_init(struct tasdevice_priv *tas_priv) +{ + int ret = 0; + int i; + + tas_priv->regmap = devm_regmap_init_i2c(tas_priv->client, + &tasdevice_regmap); + if (IS_ERR(tas_priv->regmap)) { + ret = PTR_ERR(tas_priv->regmap); + dev_err(tas_priv->dev, "Failed to allocate register map: %d\n", + ret); + goto out; + } + + tas_priv->cur_prog = -1; + tas_priv->cur_conf = -1; + + for (i = 0; i < tas_priv->ndev; i++) { + tas_priv->tasdevice[i].cur_book = -1; + tas_priv->tasdevice[i].cur_prog = -1; + tas_priv->tasdevice[i].cur_conf = -1; + } + + dev_set_drvdata(tas_priv->dev, tas_priv); + + mutex_init(&tas_priv->codec_lock); + +out: + return ret; +} +EXPORT_SYMBOL_GPL(tasdevice_init); + +static void tasdev_dsp_prog_blk_remove(struct tasdevice_prog *prog) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (!prog) + return; + + tas_dt = &(prog->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); +} + +static void tasdev_dsp_prog_remove(struct tasdevice_prog *prog, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_prog_blk_remove(&prog[i]); + kfree(prog); +} + +static void tasdev_dsp_cfg_blk_remove(struct tasdevice_config *cfg) +{ + struct tasdevice_data *tas_dt; + struct tasdev_blk *blk; + unsigned int i; + + if (cfg) { + tas_dt = &(cfg->dev_data); + + if (!tas_dt->dev_blks) + return; + + for (i = 0; i < tas_dt->nr_blk; i++) { + blk = &(tas_dt->dev_blks[i]); + kfree(blk->data); + } + kfree(tas_dt->dev_blks); + } +} + +static void tasdev_dsp_cfg_remove(struct tasdevice_config *config, + unsigned short nr) +{ + int i; + + for (i = 0; i < nr; i++) + tasdev_dsp_cfg_blk_remove(&config[i]); + kfree(config); +} + +void tasdevice_dsp_remove(void *context) +{ + struct tasdevice_priv *tas_dev = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_dev->fmw; + + if (!tas_dev->fmw) + return; + + if (tas_fmw->programs) + tasdev_dsp_prog_remove(tas_fmw->programs, + tas_fmw->nr_programs); + if (tas_fmw->configs) + tasdev_dsp_cfg_remove(tas_fmw->configs, + tas_fmw->nr_configurations); + kfree(tas_fmw); + tas_dev->fmw = NULL; +} +EXPORT_SYMBOL_GPL(tasdevice_dsp_remove); + +void tasdevice_remove(struct tasdevice_priv *tas_priv) +{ + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) + gpio_free(tas_priv->irq_info.irq_gpio); + kfree(tas_priv->acpi_subsystem_id); + mutex_destroy(&tas_priv->codec_lock); +} +EXPORT_SYMBOL_GPL(tasdevice_remove); + +static int tasdevice_clamp(int val, int max, unsigned int invert) +{ + if (val > max) + val = max; + if (invert) + val = max - val; + if (val < 0) + val = 0; + return val; +} + +int tasdevice_amp_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask; + int max = mc->max; + int err_cnt = 0; + int val, i, ret; + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_update_bits(tas_priv, i, + mc->reg, mask, (unsigned int)(val << mc->shift)); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, "set AMP vol error in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; +} +EXPORT_SYMBOL_GPL(tasdevice_amp_putvol); + +int tasdevice_amp_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + unsigned char mask = 0; + int max = mc->max; + int ret = 0; + int val; + + /* Read the primary device */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get AMP vol error\n", __func__); + goto out; + } + + mask = (1 << fls(max)) - 1; + mask <<= mc->shift; + val = (val & mask) >> mc->shift; + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_amp_getvol); + +int tasdevice_digital_putvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int err_cnt = 0; + int ret; + int val, i; + + val = tasdevice_clamp(ucontrol->value.integer.value[0], max, invert); + + for (i = 0; i < tas_priv->ndev; i++) { + ret = tasdevice_dev_write(tas_priv, i, mc->reg, + (unsigned int)val); + if (!ret) + continue; + err_cnt++; + dev_err(tas_priv->dev, + "set digital vol err in dev %d\n", i); + } + + /* All the devices set error, return 0 */ + return (err_cnt == tas_priv->ndev) ? 0 : 1; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_putvol); + +int tasdevice_digital_getvol(struct tasdevice_priv *tas_priv, + struct snd_ctl_elem_value *ucontrol, struct soc_mixer_control *mc) +{ + unsigned int invert = mc->invert; + int max = mc->max; + int ret, val; + + /* Read the primary device as the whole */ + ret = tasdevice_dev_read(tas_priv, 0, mc->reg, &val); + if (ret) { + dev_err(tas_priv->dev, "%s, get digital vol error\n", + __func__); + goto out; + } + + val = tasdevice_clamp(val, max, invert); + ucontrol->value.integer.value[0] = val; + +out: + return ret; + +} +EXPORT_SYMBOL_GPL(tasdevice_digital_getvol); + +MODULE_DESCRIPTION("TAS2781 common library"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-fmwlib.c b/sound/soc/codecs/tas2781-fmwlib.c new file mode 100644 index 000000000000..eb55abae0d7b --- /dev/null +++ b/sound/soc/codecs/tas2781-fmwlib.c @@ -0,0 +1,2428 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// tasdevice-fmw.c -- TASDEVICE firmware support +// +// Copyright 2023 Texas Instruments, Inc. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> +#include <sound/tas2781.h> + + +#define ERROR_PRAM_CRCCHK 0x0000000 +#define ERROR_YRAM_CRCCHK 0x0000001 +#define PPC_DRIVER_CRCCHK 0x00000200 + +#define TAS2781_SA_COEFF_SWAP_REG TASDEVICE_REG(0, 0x35, 0x2c) +#define TAS2781_YRAM_BOOK1 140 +#define TAS2781_YRAM1_PAGE 42 +#define TAS2781_YRAM1_START_REG 88 + +#define TAS2781_YRAM2_START_PAGE 43 +#define TAS2781_YRAM2_END_PAGE 49 +#define TAS2781_YRAM2_START_REG 8 +#define TAS2781_YRAM2_END_REG 127 + +#define TAS2781_YRAM3_PAGE 50 +#define TAS2781_YRAM3_START_REG 8 +#define TAS2781_YRAM3_END_REG 27 + +/*should not include B0_P53_R44-R47 */ +#define TAS2781_YRAM_BOOK2 0 +#define TAS2781_YRAM4_START_PAGE 50 +#define TAS2781_YRAM4_END_PAGE 60 + +#define TAS2781_YRAM5_PAGE 61 +#define TAS2781_YRAM5_START_REG TAS2781_YRAM3_START_REG +#define TAS2781_YRAM5_END_REG TAS2781_YRAM3_END_REG + +#define TASDEVICE_MAXPROGRAM_NUM_KERNEL 5 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS 64 +#define TASDEVICE_MAXCONFIG_NUM_KERNEL 10 +#define MAIN_ALL_DEVICES_1X 0x01 +#define MAIN_DEVICE_A_1X 0x02 +#define MAIN_DEVICE_B_1X 0x03 +#define MAIN_DEVICE_C_1X 0x04 +#define MAIN_DEVICE_D_1X 0x05 +#define COEFF_DEVICE_A_1X 0x12 +#define COEFF_DEVICE_B_1X 0x13 +#define COEFF_DEVICE_C_1X 0x14 +#define COEFF_DEVICE_D_1X 0x15 +#define PRE_DEVICE_A_1X 0x22 +#define PRE_DEVICE_B_1X 0x23 +#define PRE_DEVICE_C_1X 0x24 +#define PRE_DEVICE_D_1X 0x25 +#define PRE_SOFTWARE_RESET_DEVICE_A 0x41 +#define PRE_SOFTWARE_RESET_DEVICE_B 0x42 +#define PRE_SOFTWARE_RESET_DEVICE_C 0x43 +#define PRE_SOFTWARE_RESET_DEVICE_D 0x44 +#define POST_SOFTWARE_RESET_DEVICE_A 0x45 +#define POST_SOFTWARE_RESET_DEVICE_B 0x46 +#define POST_SOFTWARE_RESET_DEVICE_C 0x47 +#define POST_SOFTWARE_RESET_DEVICE_D 0x48 + +struct tas_crc { + unsigned char offset; + unsigned char len; +}; + +static const char deviceNumber[TASDEVICE_DSP_TAS_MAX_DEVICE] = { + 1, 2, 1, 2, 1, 1, 0, 2, 4, 3, 1, 2, 3, 4 +}; + +static struct tasdevice_config_info *tasdevice_add_config( + struct tasdevice_priv *tas_priv, unsigned char *config_data, + unsigned int config_size, int *status) +{ + struct tasdevice_config_info *cfg_info; + struct tasdev_blk_data **bk_da; + unsigned int config_offset = 0; + unsigned int i; + + /* In most projects are many audio cases, such as music, handfree, + * receiver, games, audio-to-haptics, PMIC record, bypass mode, + * portrait, landscape, etc. Even in multiple audios, one or + * two of the chips will work for the special case, such as + * ultrasonic application. In order to support these variable-numbers + * of audio cases, flexible configs have been introduced in the + * dsp firmware. + */ + cfg_info = kzalloc(sizeof(struct tasdevice_config_info), GFP_KERNEL); + if (!cfg_info) { + *status = -ENOMEM; + goto out; + } + + if (tas_priv->rcabin.fw_hdr.binary_version_num >= 0x105) { + if (config_offset + 64 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add conf: Out of boundary\n"); + goto out; + } + config_offset += 64; + } + + if (config_offset + 4 > (int)config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, "add config: Out of boundary\n"); + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + cfg_info->nblocks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + /* Several kinds of dsp/algorithm firmwares can run on tas2781, + * the number and size of blk are not fixed and different among + * these firmwares. + */ + bk_da = cfg_info->blk_data = kcalloc(cfg_info->nblocks, + sizeof(struct tasdev_blk_data *), GFP_KERNEL); + if (!bk_da) { + *status = -ENOMEM; + goto out; + } + cfg_info->real_nblocks = 0; + for (i = 0; i < cfg_info->nblocks; i++) { + if (config_offset + 12 > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d nblocks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + bk_da[i] = kzalloc(sizeof(struct tasdev_blk_data), GFP_KERNEL); + if (!bk_da[i]) { + *status = -ENOMEM; + break; + } + + bk_da[i]->dev_idx = config_data[config_offset]; + config_offset++; + + bk_da[i]->block_type = config_data[config_offset]; + config_offset++; + + if (bk_da[i]->block_type == TASDEVICE_BIN_BLK_PRE_POWER_UP) { + if (bk_da[i]->dev_idx == 0) + cfg_info->active_dev = + (1 << tas_priv->ndev) - 1; + else + cfg_info->active_dev |= 1 << + (bk_da[i]->dev_idx - 1); + + } + bk_da[i]->yram_checksum = + be16_to_cpup((__be16 *)&config_data[config_offset]); + config_offset += 2; + bk_da[i]->block_size = + be32_to_cpup((__be32 *)&config_data[config_offset]); + config_offset += 4; + + bk_da[i]->n_subblks = + be32_to_cpup((__be32 *)&config_data[config_offset]); + + config_offset += 4; + + if (config_offset + bk_da[i]->block_size > config_size) { + *status = -EINVAL; + dev_err(tas_priv->dev, + "%s: Out of boundary: i = %d blks = %u!\n", + __func__, i, cfg_info->nblocks); + break; + } + /* instead of kzalloc+memcpy */ + bk_da[i]->regdata = kmemdup(&config_data[config_offset], + bk_da[i]->block_size, GFP_KERNEL); + if (!bk_da[i]->regdata) { + *status = -ENOMEM; + goto out; + } + + config_offset += bk_da[i]->block_size; + cfg_info->real_nblocks += 1; + } + +out: + return cfg_info; +} + +int tasdevice_rca_parser(void *context, const struct firmware *fmw) +{ + struct tasdevice_priv *tas_priv = context; + struct tasdevice_config_info **cfg_info; + struct tasdevice_rca_hdr *fw_hdr; + struct tasdevice_rca *rca; + unsigned int total_config_sz = 0; + unsigned char *buf; + int offset = 0; + int ret = 0; + int i; + + rca = &(tas_priv->rcabin); + fw_hdr = &(rca->fw_hdr); + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "Failed to read %s\n", + tas_priv->rca_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + buf = (unsigned char *)fmw->data; + + fw_hdr->img_sz = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_hdr->img_sz != fmw->size) { + dev_err(tas_priv->dev, + "File size not match, %d %u", (int)fmw->size, + fw_hdr->img_sz); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + + fw_hdr->checksum = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + fw_hdr->binary_version_num = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->binary_version_num < 0x103) { + dev_err(tas_priv->dev, "File version 0x%04x is too low", + fw_hdr->binary_version_num); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->drv_fw_version = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_hdr->plat_type = buf[offset]; + offset += 1; + fw_hdr->dev_family = buf[offset]; + offset += 1; + fw_hdr->reserve = buf[offset]; + offset += 1; + fw_hdr->ndev = buf[offset]; + offset += 1; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "ndev(%u) in rcabin mismatch ndev(%u) in DTS\n", + fw_hdr->ndev, tas_priv->ndev); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + ret = -EINVAL; + goto out; + } + if (offset + TASDEVICE_DEVICE_SUM > fw_hdr->img_sz) { + dev_err(tas_priv->dev, "rca_ready: Out of boundary!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + for (i = 0; i < TASDEVICE_DEVICE_SUM; i++, offset++) + fw_hdr->devs[i] = buf[offset]; + + fw_hdr->nconfig = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + for (i = 0; i < TASDEVICE_CONFIG_SUM; i++) { + fw_hdr->config_size[i] = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + total_config_sz += fw_hdr->config_size[i]; + } + + if (fw_hdr->img_sz - total_config_sz != (unsigned int)offset) { + dev_err(tas_priv->dev, "Bin file error!\n"); + ret = -EINVAL; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + + cfg_info = kcalloc(fw_hdr->nconfig, sizeof(*cfg_info), GFP_KERNEL); + if (!cfg_info) { + ret = -ENOMEM; + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + rca->cfg_info = cfg_info; + rca->ncfgs = 0; + for (i = 0; i < (int)fw_hdr->nconfig; i++) { + rca->ncfgs += 1; + cfg_info[i] = tasdevice_add_config(tas_priv, &buf[offset], + fw_hdr->config_size[i], &ret); + if (ret) { + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + offset += (int)fw_hdr->config_size[i]; + } +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_rca_parser, SND_SOC_TAS2781_FMWLIB); + +static int fw_parse_block_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + + if (offset + 16 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + /* convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + + block->blk_size = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + block->nr_subblocks = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (offset + block->blk_size > fmw->size) { + dev_err(tas_fmw->dev, "%s: nSublocks error\n", __func__); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], block->blk_size, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += block->blk_size; + +out: + return offset; +} + +static int fw_parse_data_kernel(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = fmw->data; + struct tasdev_blk *blk; + unsigned int i; + + if (offset + 4 > fmw->size) { + dev_err(tas_fmw->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + img_data->nr_blk = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data_kernel(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + break; + } + } + +out: + return offset; +} + +static int fw_parse_program_data_kernel( + struct tasdevice_priv *tas_priv, struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + struct tasdevice_prog *program; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + if (offset + 72 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + /*skip 72 unused byts*/ + offset += 72; + + offset = fw_parse_data_kernel(tas_fmw, &(program->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_configuration_data_kernel( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + const unsigned char *data = fmw->data; + struct tasdevice_config *config; + unsigned int i; + + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 80 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + /*skip extra 16 bytes*/ + offset += 80; + + offset = fw_parse_data_kernel(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static int fw_parse_variable_header_kernel( + struct tasdevice_priv *tas_priv, const struct firmware *fmw, + int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_prog *program; + struct tasdevice_config *config; + const unsigned char *buf = fmw->data; + unsigned short max_confs; + unsigned int i; + + if (offset + 12 + 4 * TASDEVICE_MAXPROGRAM_NUM_KERNEL > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + fw_hdr->device_family = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s:not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->device = be16_to_cpup((__be16 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 2; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + goto out; + } + + tas_fmw->nr_programs = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + if (tas_fmw->nr_programs == 0 || tas_fmw->nr_programs > + TASDEVICE_MAXPROGRAM_NUM_KERNEL) { + dev_err(tas_priv->dev, "mnPrograms is invalid\n"); + offset = -EINVAL; + goto out; + } + + tas_fmw->programs = kcalloc(tas_fmw->nr_programs, + sizeof(struct tasdevice_prog), GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + program = &(tas_fmw->programs[i]); + program->prog_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused prog_size */ + offset += 4 * (TASDEVICE_MAXPROGRAM_NUM_KERNEL - tas_fmw->nr_programs); + + tas_fmw->nr_configurations = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + + /* The max number of config in firmware greater than 4 pieces of + * tas2781s is different from the one lower than 4 pieces of + * tas2781s. + */ + max_confs = (fw_hdr->ndev >= 4) ? + TASDEVICE_MAXCONFIG_NUM_KERNEL_MULTIPLE_AMPS : + TASDEVICE_MAXCONFIG_NUM_KERNEL; + if (tas_fmw->nr_configurations == 0 || + tas_fmw->nr_configurations > max_confs) { + dev_err(tas_priv->dev, "%s: Conf is invalid\n", __func__); + offset = -EINVAL; + goto out; + } + + if (offset + 4 * max_confs > fmw->size) { + dev_err(tas_priv->dev, "%s: mpConfigurations err\n", __func__); + offset = -EINVAL; + goto out; + } + + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + + for (i = 0; i < tas_fmw->nr_programs; i++) { + config = &(tas_fmw->configs[i]); + config->cfg_size = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + } + + /* Skip the unused configs */ + offset += 4 * (max_confs - tas_fmw->nr_programs); + +out: + return offset; +} + +static int tasdevice_process_block(void *context, unsigned char *data, + unsigned char dev_idx, int sublocksize) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + int subblk_offset, chn, chnend, rc; + unsigned char subblk_typ = data[1]; + int blktyp = dev_idx & 0xC0; + int idx = dev_idx & 0x3F; + bool is_err = false; + + if (idx) { + chn = idx - 1; + chnend = idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + + for (; chn < chnend; chn++) { + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + + is_err = false; + subblk_offset = 2; + switch (subblk_typ) { + case TASDEVICE_CMD_SING_W: { + int i; + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 * len > sublocksize) { + dev_err(tas_priv->dev, + "process_block: Out of boundary\n"); + is_err = true; + break; + } + + for (i = 0; i < len; i++) { + rc = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + data[subblk_offset + 3]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "process_block: single write error\n"); + } + subblk_offset += 4; + } + } + break; + case TASDEVICE_CMD_BURST: { + unsigned short len = be16_to_cpup((__be16 *)&data[2]); + + subblk_offset += 2; + if (subblk_offset + 4 + len > sublocksize) { + dev_err(tas_priv->dev, + "%s: BST Out of boundary\n", + __func__); + is_err = true; + break; + } + if (len % 4) { + dev_err(tas_priv->dev, + "%s:Bst-len(%u)not div by 4\n", + __func__, len); + break; + } + + rc = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset], + data[subblk_offset + 1], + data[subblk_offset + 2]), + &(data[subblk_offset + 4]), len); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: bulk_write error = %d\n", + __func__, rc); + } + subblk_offset += (len + 4); + } + break; + case TASDEVICE_CMD_DELAY: { + unsigned int sleep_time = 0; + + if (subblk_offset + 2 > sublocksize) { + dev_err(tas_priv->dev, + "%s: delay Out of boundary\n", + __func__); + is_err = true; + break; + } + sleep_time = be16_to_cpup((__be16 *)&data[2]) * 1000; + usleep_range(sleep_time, sleep_time + 50); + subblk_offset += 2; + } + break; + case TASDEVICE_CMD_FIELD_W: + if (subblk_offset + 6 > sublocksize) { + dev_err(tas_priv->dev, + "%s: bit write Out of boundary\n", + __func__); + is_err = true; + break; + } + rc = tasdevice_dev_update_bits(tas_priv, chn, + TASDEVICE_REG(data[subblk_offset + 2], + data[subblk_offset + 3], + data[subblk_offset + 4]), + data[subblk_offset + 1], + data[subblk_offset + 5]); + if (rc < 0) { + is_err = true; + dev_err(tas_priv->dev, + "%s: update_bits error = %d\n", + __func__, rc); + } + subblk_offset += 6; + break; + default: + break; + } + if (is_err == true && blktyp != 0) { + if (blktyp == 0x80) { + tas_priv->tasdevice[chn].cur_prog = -1; + tas_priv->tasdevice[chn].cur_conf = -1; + } else + tas_priv->tasdevice[chn].cur_conf = -1; + } + } + + return subblk_offset; +} + +void tasdevice_select_cfg_blk(void *pContext, int conf_no, + unsigned char block_type) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) pContext; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdev_blk_data **blk_data; + int j, k, chn, chnend; + + if (conf_no >= rca->ncfgs || conf_no < 0 || !cfg_info) { + dev_err(tas_priv->dev, "conf_no should be not more than %u\n", + rca->ncfgs); + return; + } + blk_data = cfg_info[conf_no]->blk_data; + + for (j = 0; j < (int)cfg_info[conf_no]->real_nblocks; j++) { + unsigned int length = 0, rc = 0; + + if (block_type > 5 || block_type < 2) { + dev_err(tas_priv->dev, + "block_type should be in range from 2 to 5\n"); + break; + } + if (block_type != blk_data[j]->block_type) + continue; + + for (k = 0; k < (int)blk_data[j]->n_subblks; k++) { + if (blk_data[j]->dev_idx) { + chn = blk_data[j]->dev_idx - 1; + chnend = blk_data[j]->dev_idx; + } else { + chn = 0; + chnend = tas_priv->ndev; + } + for (; chn < chnend; chn++) + tas_priv->tasdevice[chn].is_loading = true; + + rc = tasdevice_process_block(tas_priv, + blk_data[j]->regdata + length, + blk_data[j]->dev_idx, + blk_data[j]->block_size - length); + length += rc; + if (blk_data[j]->block_size < length) { + dev_err(tas_priv->dev, + "%s: %u %u out of boundary\n", + __func__, length, + blk_data[j]->block_size); + break; + } + } + if (length != blk_data[j]->block_size) + dev_err(tas_priv->dev, "%s: %u %u size is not same\n", + __func__, length, blk_data[j]->block_size); + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_cfg_blk, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_block_kernel( + struct tasdevice_priv *tasdevice, struct tasdev_blk *block) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tasdevice->fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned int blk_size = block->blk_size; + unsigned int i, length; + unsigned char *data = block->data; + unsigned char dev_idx = 0; + + if (fw_fixed_hdr->ppcver >= PPC3_VERSION_TAS2781) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + case PRE_SOFTWARE_RESET_DEVICE_A: + case POST_SOFTWARE_RESET_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + case PRE_SOFTWARE_RESET_DEVICE_B: + case POST_SOFTWARE_RESET_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + case PRE_SOFTWARE_RESET_DEVICE_C: + case POST_SOFTWARE_RESET_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + case PRE_SOFTWARE_RESET_DEVICE_D: + case POST_SOFTWARE_RESET_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else if (fw_fixed_hdr->ppcver >= + PPC3_VERSION) { + switch (block->type) { + case MAIN_ALL_DEVICES_1X: + dev_idx = 0x80; + break; + case MAIN_DEVICE_A_1X: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A_1X: + case PRE_DEVICE_A_1X: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B_1X: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B_1X: + case PRE_DEVICE_B_1X: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C_1X: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C_1X: + case PRE_DEVICE_C_1X: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D_1X: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D_1X: + case PRE_DEVICE_D_1X: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } else { + switch (block->type) { + case MAIN_ALL_DEVICES: + dev_idx = 0|0x80; + break; + case MAIN_DEVICE_A: + dev_idx = 0x81; + break; + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + dev_idx = 0xC1; + break; + case MAIN_DEVICE_B: + dev_idx = 0x82; + break; + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + dev_idx = 0xC2; + break; + case MAIN_DEVICE_C: + dev_idx = 0x83; + break; + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + dev_idx = 0xC3; + break; + case MAIN_DEVICE_D: + dev_idx = 0x84; + break; + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + dev_idx = 0xC4; + break; + default: + dev_info(tasdevice->dev, + "%s: load block: Other Type = 0x%02x\n", + __func__, block->type); + break; + } + } + + for (i = 0, length = 0; i < block->nr_subblocks; i++) { + int rc = tasdevice_process_block(tasdevice, data + length, + dev_idx, blk_size - length); + if (rc < 0) { + dev_err(tasdevice->dev, + "%s: %u %u sublock write error\n", + __func__, length, blk_size); + break; + } + length += (unsigned int)rc; + if (blk_size < length) { + dev_err(tasdevice->dev, "%s: %u %u out of boundary\n", + __func__, length, blk_size); + break; + } + } + + return 0; +} + +static int fw_parse_variable_hdr(struct tasdevice_priv + *tas_priv, struct tasdevice_dspfw_hdr *fw_hdr, + const struct firmware *fmw, int offset) +{ + const unsigned char *buf = fmw->data; + int len = strlen((char *)&buf[offset]); + + len++; + + if (offset + len + 8 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + + offset += len; + + fw_hdr->device_family = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device_family != 0) { + dev_err(tas_priv->dev, "%s: not TAS device\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + fw_hdr->device = be32_to_cpup((__be32 *)&buf[offset]); + if (fw_hdr->device >= TASDEVICE_DSP_TAS_MAX_DEVICE || + fw_hdr->device == 6) { + dev_err(tas_priv->dev, "Unsupported dev %d\n", fw_hdr->device); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_hdr->ndev = deviceNumber[fw_hdr->device]; + +out: + return offset; +} + +static int fw_parse_variable_header_git(struct tasdevice_priv + *tas_priv, const struct firmware *fmw, int offset) +{ + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != tas_priv->ndev) { + dev_err(tas_priv->dev, + "%s: ndev(%u) in dspbin mismatch ndev(%u) in DTS\n", + __func__, fw_hdr->ndev, tas_priv->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +static int fw_parse_block_data(struct tasdevice_fw *tas_fmw, + struct tasdev_blk *block, const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + int n; + + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Type error\n", __func__); + offset = -EINVAL; + goto out; + } + block->type = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + if (tas_fmw->fw_hdr.fixed_hdr.drv_ver >= PPC_DRIVER_CRCCHK) { + if (offset + 8 > fmw->size) { + dev_err(tas_fmw->dev, "PChkSumPresent error\n"); + offset = -EINVAL; + goto out; + } + block->is_pchksum_present = data[offset]; + offset++; + + block->pchksum = data[offset]; + offset++; + + block->is_ychksum_present = data[offset]; + offset++; + + block->ychksum = data[offset]; + offset++; + } else { + block->is_pchksum_present = 0; + block->is_ychksum_present = 0; + } + + block->nr_cmds = be32_to_cpup((__be32 *)&data[offset]); + offset += 4; + + n = block->nr_cmds * 4; + if (offset + n > fmw->size) { + dev_err(tas_fmw->dev, + "%s: File Size(%lu) error offset = %d n = %d\n", + __func__, (unsigned long)fmw->size, offset, n); + offset = -EINVAL; + goto out; + } + /* instead of kzalloc+memcpy */ + block->data = kmemdup(&data[offset], n, GFP_KERNEL); + if (!block->data) { + offset = -ENOMEM; + goto out; + } + offset += n; + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_data(struct tasdevice_fw *tas_fmw, + struct tasdevice_data *img_data, const struct firmware *fmw, + int offset) +{ + const unsigned char *data = (unsigned char *)fmw->data; + struct tasdev_blk *blk; + unsigned int i; + int n; + + if (offset + 64 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Name error\n", __func__); + offset = -EINVAL; + goto out; + } + memcpy(img_data->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n++; + if (offset + n + 2 > fmw->size) { + dev_err(tas_fmw->dev, "%s: Description error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += n; + img_data->nr_blk = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + img_data->dev_blks = kcalloc(img_data->nr_blk, + sizeof(struct tasdev_blk), GFP_KERNEL); + if (!img_data->dev_blks) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < img_data->nr_blk; i++) { + blk = &(img_data->dev_blks[i]); + offset = fw_parse_block_data(tas_fmw, blk, fmw, offset); + if (offset < 0) { + offset = -EINVAL; + goto out; + } + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_program_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + unsigned char *buf = (unsigned char *)fmw->data; + struct tasdevice_prog *program; + int i; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_programs = be16_to_cpup((__be16 *)&buf[offset]); + offset += 2; + + if (tas_fmw->nr_programs == 0) { + /*Not error in calibration Data file, return directly*/ + dev_info(tas_priv->dev, "%s: No Programs data, maybe calbin\n", + __func__); + goto out; + } + + tas_fmw->programs = + kcalloc(tas_fmw->nr_programs, sizeof(struct tasdevice_prog), + GFP_KERNEL); + if (!tas_fmw->programs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_programs; i++) { + int n = 0; + + program = &(tas_fmw->programs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "%s: mpName error\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 64; + + n = strlen((char *)&buf[offset]); + /* skip '\0' and 5 unused bytes */ + n += 6; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(program->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +/* When parsing error occurs, all the memory resource will be released + * in the end of tasdevice_rca_ready. + */ +static int fw_parse_configuration_data( + struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, + const struct firmware *fmw, int offset) +{ + unsigned char *data = (unsigned char *)fmw->data; + struct tasdevice_config *config; + unsigned int i; + int n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_configurations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_configurations == 0) { + dev_err(tas_priv->dev, "%s: Conf is zero\n", __func__); + /*Not error for calibration Data file, return directly*/ + goto out; + } + tas_fmw->configs = kcalloc(tas_fmw->nr_configurations, + sizeof(struct tasdevice_config), GFP_KERNEL); + if (!tas_fmw->configs) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_configurations; i++) { + config = &(tas_fmw->configs[i]); + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "File Size err\n"); + offset = -EINVAL; + goto out; + } + memcpy(config->name, &data[offset], 64); + offset += 64; + + n = strlen((char *)&data[offset]); + n += 15; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + + offset += n; + + offset = fw_parse_data(tas_fmw, &(config->dev_data), + fmw, offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +static bool check_inpage_yram_rg(struct tas_crc *cd, + unsigned char reg, unsigned char len) +{ + bool in = false; + + + if (reg <= TAS2781_YRAM5_END_REG && + reg >= TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_END_REG) + cd->len = TAS2781_YRAM5_END_REG - reg + 1; + else + cd->len = len; + cd->offset = reg; + in = true; + } else if (reg < TAS2781_YRAM5_START_REG) { + if (reg + len > TAS2781_YRAM5_START_REG) { + cd->offset = TAS2781_YRAM5_START_REG; + cd->len = len - TAS2781_YRAM5_START_REG + reg; + in = true; + } + } + + return in; +} + +static bool check_inpage_yram_bk1(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (page == TAS2781_YRAM1_PAGE) { + if (reg >= TAS2781_YRAM1_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg + len > TAS2781_YRAM1_START_REG) { + cd->offset = TAS2781_YRAM1_START_REG; + cd->len = len - TAS2781_YRAM1_START_REG + reg; + in = true; + } + } else if (page == TAS2781_YRAM3_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + + return in; +} + +/* Return Code: + * true -- the registers are in the inpage yram + * false -- the registers are NOT in the inpage yram + */ +static bool check_inpage_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1) { + in = check_inpage_yram_bk1(cd, page, reg, len); + goto end; + } + if (book == TAS2781_YRAM_BOOK2 && page == TAS2781_YRAM5_PAGE) + in = check_inpage_yram_rg(cd, reg, len); + +end: + return in; +} + +static bool check_inblock_yram_bk(struct tas_crc *cd, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if ((page >= TAS2781_YRAM4_START_PAGE && + page <= TAS2781_YRAM4_END_PAGE) || + (page >= TAS2781_YRAM2_START_PAGE && + page <= TAS2781_YRAM2_END_PAGE)) { + if (reg <= TAS2781_YRAM2_END_REG && + reg >= TAS2781_YRAM2_START_REG) { + cd->offset = reg; + cd->len = len; + in = true; + } else if (reg < TAS2781_YRAM2_START_REG) { + if (reg + len - 1 >= TAS2781_YRAM2_START_REG) { + cd->offset = TAS2781_YRAM2_START_REG; + cd->len = reg + len - TAS2781_YRAM2_START_REG; + in = true; + } + } + } + + return in; +} + +/* Return Code: + * true -- the registers are in the inblock yram + * false -- the registers are NOT in the inblock yram + */ +static bool check_inblock_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in = false; + + if (book == TAS2781_YRAM_BOOK1 || book == TAS2781_YRAM_BOOK2) + in = check_inblock_yram_bk(cd, page, reg, len); + + return in; +} + +static bool check_yram(struct tas_crc *cd, unsigned char book, + unsigned char page, unsigned char reg, unsigned char len) +{ + bool in; + + in = check_inpage_yram(cd, book, page, reg, len); + if (in) + goto end; + in = check_inblock_yram(cd, book, page, reg, len); + +end: + return in; +} + +static int tasdev_multibytes_chksum(struct tasdevice_priv *tasdevice, + unsigned short chn, unsigned char book, unsigned char page, + unsigned char reg, unsigned int len) +{ + struct tas_crc crc_data; + unsigned char crc_chksum = 0; + unsigned char nBuf1[128]; + int ret = 0; + int i; + bool in; + + if ((reg + len - 1) > 127) { + ret = -EINVAL; + dev_err(tasdevice->dev, "firmware error\n"); + goto end; + } + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg == TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (len == 4)) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, len); + if (!in) + goto end; + + if (len == 1) { + dev_err(tasdevice->dev, "firmware error\n"); + ret = -EINVAL; + goto end; + } + + ret = tasdevice_dev_bulk_read(tasdevice, chn, + TASDEVICE_REG(book, page, crc_data.offset), + nBuf1, crc_data.len); + if (ret < 0) + goto end; + + for (i = 0; i < crc_data.len; i++) { + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID( + TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && ((i + crc_data.offset) + <= (TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG) + + 4))) + /*DSP swap command, bypass */ + continue; + else + crc_chksum += crc8(tasdevice->crc8_lkp_tbl, &nBuf1[i], + 1, 0); + } + + ret = crc_chksum; + +end: + return ret; +} + +static int do_singlereg_checksum(struct tasdevice_priv *tasdevice, + unsigned short chl, unsigned char book, unsigned char page, + unsigned char reg, unsigned char val) +{ + struct tas_crc crc_data; + unsigned int nData1; + int ret = 0; + bool in; + + if ((book == TASDEVICE_BOOK_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (page == TASDEVICE_PAGE_ID(TAS2781_SA_COEFF_SWAP_REG)) + && (reg >= TASDEVICE_PAGE_REG(TAS2781_SA_COEFF_SWAP_REG)) + && (reg <= (TASDEVICE_PAGE_REG( + TAS2781_SA_COEFF_SWAP_REG) + 4))) { + /*DSP swap command, pass */ + ret = 0; + goto end; + } + + in = check_yram(&crc_data, book, page, reg, 1); + if (!in) + goto end; + ret = tasdevice_dev_read(tasdevice, chl, + TASDEVICE_REG(book, page, reg), &nData1); + if (ret < 0) + goto end; + + if (nData1 != val) { + dev_err(tasdevice->dev, + "B[0x%x]P[0x%x]R[0x%x] W[0x%x], R[0x%x]\n", + book, page, reg, val, nData1); + tasdevice->tasdevice[chl].err_code |= ERROR_YRAM_CRCCHK; + ret = -EAGAIN; + goto end; + } + + ret = crc8(tasdevice->crc8_lkp_tbl, &val, 1, 0); + +end: + return ret; +} + +static void set_err_prg_cfg(unsigned int type, struct tasdevice *dev) +{ + if ((type == MAIN_ALL_DEVICES) || (type == MAIN_DEVICE_A) + || (type == MAIN_DEVICE_B) || (type == MAIN_DEVICE_C) + || (type == MAIN_DEVICE_D)) + dev->cur_prog = -1; + else + dev->cur_conf = -1; +} + +static int tasdev_bytes_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned int len, + unsigned char val, unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) + ret = tasdev_multibytes_chksum(tas_priv, chn, book, page, reg, + len); + else + ret = do_singlereg_checksum(tas_priv, chn, book, page, reg, + val); + + if (ret > 0) { + *crc_chksum += (unsigned char)ret; + goto end; + } + + if (ret != -EAGAIN) + goto end; + + block->nr_retry--; + if (block->nr_retry > 0) + goto end; + + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + +end: + return ret; +} + +static int tasdev_multibytes_wr(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn, unsigned char book, + unsigned char page, unsigned char reg, unsigned char *data, + unsigned int len, unsigned int *nr_cmds, + unsigned char *crc_chksum) +{ + int ret; + + if (len > 1) { + ret = tasdevice_dev_bulk_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data + 3, len); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, + book, page, reg, len, 0, crc_chksum); + } else { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, reg), data[3]); + if (ret < 0) + goto end; + if (block->is_ychksum_present) + ret = tasdev_bytes_chksum(tas_priv, block, chn, book, + page, reg, 1, data[3], crc_chksum); + } + + if (!block->is_ychksum_present || ret >= 0) { + *nr_cmds += 1; + if (len >= 2) + *nr_cmds += ((len - 2) / 4) + 1; + } + +end: + return ret; +} + +static int tasdev_block_chksum(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int nr_value; + int ret; + + ret = tasdevice_dev_read(tas_priv, chn, TASDEVICE_I2CChecksum, + &nr_value); + if (ret < 0) { + dev_err(tas_priv->dev, "%s: Chn %d\n", __func__, chn); + set_err_prg_cfg(block->type, &tas_priv->tasdevice[chn]); + goto end; + } + + if ((nr_value & 0xff) != block->pchksum) { + dev_err(tas_priv->dev, "%s: Blk PChkSum Chn %d ", __func__, + chn); + dev_err(tas_priv->dev, "PChkSum = 0x%x, Reg = 0x%x\n", + block->pchksum, (nr_value & 0xff)); + tas_priv->tasdevice[chn].err_code |= ERROR_PRAM_CRCCHK; + ret = -EAGAIN; + block->nr_retry--; + + if (block->nr_retry <= 0) + set_err_prg_cfg(block->type, + &tas_priv->tasdevice[chn]); + } else + tas_priv->tasdevice[chn].err_code &= ~ERROR_PRAM_CRCCHK; + +end: + return ret; +} + +static int tasdev_load_blk(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block, int chn) +{ + unsigned int sleep_time; + unsigned int len; + unsigned int nr_cmds; + unsigned char *data = block->data; + unsigned char crc_chksum = 0; + unsigned char offset; + unsigned char book; + unsigned char page; + unsigned char val; + int ret = 0; + + while (block->nr_retry > 0) { + if (block->is_pchksum_present) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_I2CChecksum, 0); + if (ret < 0) + break; + } + + if (block->is_ychksum_present) + crc_chksum = 0; + + nr_cmds = 0; + + while (nr_cmds < block->nr_cmds) { + data = block->data + nr_cmds * 4; + + book = data[0]; + page = data[1]; + offset = data[2]; + val = data[3]; + + nr_cmds++; + /*Single byte write*/ + if (offset <= 0x7F) { + ret = tasdevice_dev_write(tas_priv, chn, + TASDEVICE_REG(book, page, offset), + val); + if (ret < 0) + goto end; + if (block->is_ychksum_present) { + ret = tasdev_bytes_chksum(tas_priv, + block, chn, book, page, offset, + 1, val, &crc_chksum); + if (ret < 0) + break; + } + continue; + } + /*sleep command*/ + if (offset == 0x81) { + /*book -- data[0] page -- data[1]*/ + sleep_time = ((book << 8) + page)*1000; + usleep_range(sleep_time, sleep_time + 50); + continue; + } + /*Multiple bytes write*/ + if (offset == 0x85) { + data += 4; + len = (book << 8) + page; + book = data[0]; + page = data[1]; + offset = data[2]; + ret = tasdev_multibytes_wr(tas_priv, + block, chn, book, page, offset, data, + len, &nr_cmds, &crc_chksum); + if (ret < 0) + break; + } + } + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + + if (block->is_pchksum_present) { + ret = tasdev_block_chksum(tas_priv, block, chn); + if (ret == -EAGAIN) { + if (block->nr_retry > 0) + continue; + } else if (ret < 0) /*err in current device, skip it*/ + break; + } + + if (block->is_ychksum_present) { + /* TBD, open it when FW ready */ + dev_err(tas_priv->dev, + "Blk YChkSum: FW = 0x%x, YCRC = 0x%x\n", + block->ychksum, crc_chksum); + + tas_priv->tasdevice[chn].err_code &= + ~ERROR_YRAM_CRCCHK; + ret = 0; + } + /*skip current blk*/ + break; + } + +end: + return ret; +} + +static int tasdevice_load_block(struct tasdevice_priv *tas_priv, + struct tasdev_blk *block) +{ + int chnend = 0; + int ret = 0; + int chn = 0; + int rc = 0; + + switch (block->type) { + case MAIN_ALL_DEVICES: + chn = 0; + chnend = tas_priv->ndev; + break; + case MAIN_DEVICE_A: + case COEFF_DEVICE_A: + case PRE_DEVICE_A: + chn = 0; + chnend = 1; + break; + case MAIN_DEVICE_B: + case COEFF_DEVICE_B: + case PRE_DEVICE_B: + chn = 1; + chnend = 2; + break; + case MAIN_DEVICE_C: + case COEFF_DEVICE_C: + case PRE_DEVICE_C: + chn = 2; + chnend = 3; + break; + case MAIN_DEVICE_D: + case COEFF_DEVICE_D: + case PRE_DEVICE_D: + chn = 3; + chnend = 4; + break; + default: + dev_dbg(tas_priv->dev, "load blk: Other Type = 0x%02x\n", + block->type); + break; + } + + for (; chn < chnend; chn++) { + block->nr_retry = 6; + if (tas_priv->tasdevice[chn].is_loading == false) + continue; + ret = tasdev_load_blk(tas_priv, block, chn); + if (ret < 0) + dev_err(tas_priv->dev, "dev %d, Blk (%d) load error\n", + chn, block->type); + rc |= ret; + } + + return rc; +} + +static int dspfw_default_callback(struct tasdevice_priv *tas_priv, + unsigned int drv_ver, unsigned int ppcver) +{ + int rc = 0; + + if (drv_ver == 0x100) { + if (ppcver >= PPC3_VERSION) { + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + } else { + switch (ppcver) { + case 0x00: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + dev_err(tas_priv->dev, + "%s: PPCVer must be 0x0 or 0x%02x", + __func__, PPC3_VERSION); + dev_err(tas_priv->dev, " Current:0x%02x\n", + ppcver); + rc = -EINVAL; + break; + } + } + } else { + dev_err(tas_priv->dev, + "DrvVer must be 0x0, 0x230 or above 0x230 "); + dev_err(tas_priv->dev, "current is 0x%02x\n", drv_ver); + rc = -EINVAL; + } + + return rc; +} + +static int load_calib_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +static int fw_parse_header(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr = &(fw_hdr->fixed_hdr); + const unsigned char magic_number[] = { 0x35, 0x35, 0x35, 0x32 }; + const unsigned char *buf = (unsigned char *)fmw->data; + + if (offset + 92 > fmw->size) { + dev_err(tas_priv->dev, "%s: File Size error\n", __func__); + offset = -EINVAL; + goto out; + } + if (memcmp(&buf[offset], magic_number, 4)) { + dev_err(tas_priv->dev, "%s: Magic num NOT match\n", __func__); + offset = -EINVAL; + goto out; + } + offset += 4; + + /* Convert data[offset], data[offset + 1], data[offset + 2] and + * data[offset + 3] into host + */ + fw_fixed_hdr->fwsize = be32_to_cpup((__be32 *)&buf[offset]); + offset += 4; + if (fw_fixed_hdr->fwsize != fmw->size) { + dev_err(tas_priv->dev, "File size not match, %lu %u", + (unsigned long)fmw->size, fw_fixed_hdr->fwsize); + offset = -EINVAL; + goto out; + } + offset += 4; + fw_fixed_hdr->ppcver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 8; + fw_fixed_hdr->drv_ver = be32_to_cpup((__be32 *)&buf[offset]); + offset += 72; + + out: + return offset; +} + +static int fw_parse_variable_hdr_cal(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_dspfw_hdr *fw_hdr = &(tas_fmw->fw_hdr); + + offset = fw_parse_variable_hdr(tas_priv, fw_hdr, fmw, offset); + if (offset < 0) + goto out; + if (fw_hdr->ndev != 1) { + dev_err(tas_priv->dev, + "%s: calbin must be 1, but currently ndev(%u)\n", + __func__, fw_hdr->ndev); + offset = -EINVAL; + } + +out: + return offset; +} + +/* When calibrated data parsing error occurs, DSP can still work with default + * calibrated data, memory resource related to calibrated data will be + * released in the tasdevice_codec_remove. + */ +static int fw_parse_calibration_data(struct tasdevice_priv *tas_priv, + struct tasdevice_fw *tas_fmw, const struct firmware *fmw, int offset) +{ + struct tasdevice_calibration *calibration; + unsigned char *data = (unsigned char *)fmw->data; + unsigned int i, n; + + if (offset + 2 > fmw->size) { + dev_err(tas_priv->dev, "%s: Calibrations error\n", __func__); + offset = -EINVAL; + goto out; + } + tas_fmw->nr_calibrations = be16_to_cpup((__be16 *)&data[offset]); + offset += 2; + + if (tas_fmw->nr_calibrations != 1) { + dev_err(tas_priv->dev, + "%s: only supports one calibration (%d)!\n", + __func__, tas_fmw->nr_calibrations); + goto out; + } + + tas_fmw->calibrations = kcalloc(tas_fmw->nr_calibrations, + sizeof(struct tasdevice_calibration), GFP_KERNEL); + if (!tas_fmw->calibrations) { + offset = -ENOMEM; + goto out; + } + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + if (offset + 64 > fmw->size) { + dev_err(tas_priv->dev, "Calibrations error\n"); + offset = -EINVAL; + goto out; + } + calibration = &(tas_fmw->calibrations[i]); + offset += 64; + + n = strlen((char *)&data[offset]); + /* skip '\0' and 2 unused bytes */ + n += 3; + if (offset + n > fmw->size) { + dev_err(tas_priv->dev, "Description err\n"); + offset = -EINVAL; + goto out; + } + offset += n; + + offset = fw_parse_data(tas_fmw, &(calibration->dev_data), fmw, + offset); + if (offset < 0) + goto out; + } + +out: + return offset; +} + +int tas2781_load_calibration(void *context, char *file_name, + unsigned short i) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + struct tasdevice *tasdev = &(tas_priv->tasdevice[i]); + const struct firmware *fw_entry; + struct tasdevice_fw *tas_fmw; + struct firmware fmw; + int offset = 0; + int ret; + + ret = request_firmware(&fw_entry, file_name, tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: Request firmware %s failed\n", + __func__, file_name); + goto out; + } + + if (!fw_entry->size) { + dev_err(tas_priv->dev, "%s: file read error: size = %lu\n", + __func__, (unsigned long)fw_entry->size); + ret = -EINVAL; + goto out; + } + fmw.size = fw_entry->size; + fmw.data = fw_entry->data; + + tas_fmw = tasdev->cali_data_fmw = kzalloc(sizeof(struct tasdevice_fw), + GFP_KERNEL); + if (!tasdev->cali_data_fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, "fw_parse_header EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_variable_hdr_cal(tas_priv, tas_fmw, &fmw, offset); + if (offset == -EINVAL) { + dev_err(tas_priv->dev, + "%s: fw_parse_variable_header_cal EXIT!\n", __func__); + ret = offset; + goto out; + } + offset = fw_parse_program_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_program_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_configuration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_configuration_data EXIT!\n"); + ret = offset; + goto out; + } + offset = fw_parse_calibration_data(tas_priv, tas_fmw, &fmw, offset); + if (offset < 0) { + dev_err(tas_priv->dev, "fw_parse_calibration_data EXIT!\n"); + ret = offset; + goto out; + } + +out: + if (fw_entry) + release_firmware(fw_entry); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(tas2781_load_calibration, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_dspfw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw_fixed_hdr *fw_fixed_hdr; + struct tasdevice_fw *tas_fmw; + int offset = 0; + int ret = 0; + + if (!fmw || !fmw->data) { + dev_err(tas_priv->dev, "%s: Failed to read firmware %s\n", + __func__, tas_priv->coef_binaryname); + ret = -EINVAL; + goto out; + } + + tas_priv->fmw = kzalloc(sizeof(struct tasdevice_fw), GFP_KERNEL); + if (!tas_priv->fmw) { + ret = -ENOMEM; + goto out; + } + tas_fmw = tas_priv->fmw; + tas_fmw->dev = tas_priv->dev; + offset = fw_parse_header(tas_priv, tas_fmw, fmw, offset); + + if (offset == -EINVAL) { + ret = -EINVAL; + goto out; + } + fw_fixed_hdr = &(tas_fmw->fw_hdr.fixed_hdr); + /* Support different versions of firmware */ + switch (fw_fixed_hdr->drv_ver) { + case 0x301: + case 0x302: + case 0x502: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_kernel; + tas_priv->fw_parse_program_data = + fw_parse_program_data_kernel; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data_kernel; + tas_priv->tasdevice_load_block = + tasdevice_load_block_kernel; + break; + case 0x202: + case 0x400: + tas_priv->fw_parse_variable_header = + fw_parse_variable_header_git; + tas_priv->fw_parse_program_data = + fw_parse_program_data; + tas_priv->fw_parse_configuration_data = + fw_parse_configuration_data; + tas_priv->tasdevice_load_block = + tasdevice_load_block; + break; + default: + ret = dspfw_default_callback(tas_priv, + fw_fixed_hdr->drv_ver, fw_fixed_hdr->ppcver); + if (ret) + goto out; + break; + } + + offset = tas_priv->fw_parse_variable_header(tas_priv, fmw, offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_program_data(tas_priv, tas_fmw, fmw, + offset); + if (offset < 0) { + ret = offset; + goto out; + } + offset = tas_priv->fw_parse_configuration_data(tas_priv, + tas_fmw, fmw, offset); + if (offset < 0) + ret = offset; + +out: + return ret; +} + +int tasdevice_dsp_parser(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *)context; + const struct firmware *fw_entry; + int ret; + + ret = request_firmware(&fw_entry, tas_priv->coef_binaryname, + tas_priv->dev); + if (ret) { + dev_err(tas_priv->dev, "%s: load %s error\n", __func__, + tas_priv->coef_binaryname); + goto out; + } + + ret = tasdevice_dspfw_ready(fw_entry, tas_priv); + release_firmware(fw_entry); + fw_entry = NULL; + +out: + return ret; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_dsp_parser, SND_SOC_TAS2781_FMWLIB); + +static void tas2781_clear_calfirmware(struct tasdevice_fw *tas_fmw) +{ + struct tasdevice_calibration *calibration; + struct tasdev_blk *block; + struct tasdevice_data *im; + unsigned int blks; + int i; + + if (!tas_fmw->calibrations) + goto out; + + for (i = 0; i < tas_fmw->nr_calibrations; i++) { + calibration = &(tas_fmw->calibrations[i]); + if (!calibration) + continue; + + im = &(calibration->dev_data); + + if (!im->dev_blks) + continue; + + for (blks = 0; blks < im->nr_blk; blks++) { + block = &(im->dev_blks[blks]); + if (!block) + continue; + kfree(block->data); + } + kfree(im->dev_blks); + } + kfree(tas_fmw->calibrations); +out: + kfree(tas_fmw); +} + +void tasdevice_calbin_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice *tasdev; + int i; + + if (!tas_priv) + return; + + for (i = 0; i < tas_priv->ndev; i++) { + tasdev = &(tas_priv->tasdevice[i]); + if (!tasdev->cali_data_fmw) + continue; + tas2781_clear_calfirmware(tasdev->cali_data_fmw); + tasdev->cali_data_fmw = NULL; + } +} +EXPORT_SYMBOL_NS_GPL(tasdevice_calbin_remove, SND_SOC_TAS2781_FMWLIB); + +void tasdevice_config_info_remove(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **ci = rca->cfg_info; + int i, j; + + if (!ci) + return; + for (i = 0; i < rca->ncfgs; i++) { + if (!ci[i]) + continue; + if (ci[i]->blk_data) { + for (j = 0; j < (int)ci[i]->real_nblocks; j++) { + if (!ci[i]->blk_data[j]) + continue; + kfree(ci[i]->blk_data[j]->regdata); + kfree(ci[i]->blk_data[j]); + } + kfree(ci[i]->blk_data); + } + kfree(ci[i]); + } + kfree(ci); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_config_info_remove, SND_SOC_TAS2781_FMWLIB); + +static int tasdevice_load_data(struct tasdevice_priv *tas_priv, + struct tasdevice_data *dev_data) +{ + struct tasdev_blk *block; + unsigned int i; + int ret = 0; + + for (i = 0; i < dev_data->nr_blk; i++) { + block = &(dev_data->dev_blks[i]); + ret = tas_priv->tasdevice_load_block(tas_priv, block); + if (ret < 0) + break; + } + + return ret; +} + +int tasdevice_select_tuningprm_cfg(void *context, int prm_no, + int cfg_no, int rca_conf_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_rca *rca = &(tas_priv->rcabin); + struct tasdevice_config_info **cfg_info = rca->cfg_info; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + struct tasdevice_config *conf; + int prog_status = 0; + int status, i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (cfg_no >= tas_fmw->nr_configurations) { + dev_err(tas_priv->dev, + "%s: cfg(%d) is not in range of conf %u\n", + __func__, cfg_no, tas_fmw->nr_configurations); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + if (rca_conf_no >= rca->ncfgs || rca_conf_no < 0 || + !cfg_info) { + dev_err(tas_priv->dev, + "conf_no:%d should be in range from 0 to %u\n", + rca_conf_no, rca->ncfgs-1); + goto out; + } + + conf = &(tas_fmw->configs[cfg_no]); + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (cfg_info[rca_conf_no]->active_dev & (1 << i)) { + if (tas_priv->tasdevice[i].cur_prog != prm_no + || tas_priv->force_fwload_status) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } else + tas_priv->tasdevice[i].is_loading = false; + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration + *cal = cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + + for (i = 0, status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_conf != cfg_no + && (cfg_info[rca_conf_no]->active_dev & (1 << i)) + && (tas_priv->tasdevice[i].is_loaderr == false)) { + status++; + tas_priv->tasdevice[i].is_loading = true; + } else + tas_priv->tasdevice[i].is_loading = false; + } + + if (status) { + status = 0; + tasdevice_load_data(tas_priv, &(conf->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) { + status |= 1 << (i + 4); + continue; + } else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_conf = cfg_no; + } + } else + dev_dbg(tas_priv->dev, "%s: Unneeded loading dsp conf %d\n", + __func__, cfg_no); + + status |= cfg_info[rca_conf_no]->active_dev; + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_select_tuningprm_cfg, + SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_load, SND_SOC_TAS2781_FMWLIB); + +int tasdevice_prmg_calibdata_load(void *context, int prm_no) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + struct tasdevice_prog *program; + int prog_status = 0; + int i; + + if (!tas_fmw) { + dev_err(tas_priv->dev, "%s: Firmware is NULL\n", __func__); + goto out; + } + + if (prm_no >= tas_fmw->nr_programs) { + dev_err(tas_priv->dev, + "%s: prm(%d) is not in range of Programs %u\n", + __func__, prm_no, tas_fmw->nr_programs); + goto out; + } + + for (i = 0, prog_status = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].cur_prog != prm_no) { + tas_priv->tasdevice[i].cur_conf = -1; + tas_priv->tasdevice[i].is_loading = true; + prog_status++; + } + tas_priv->tasdevice[i].is_loaderr = false; + } + + if (prog_status) { + program = &(tas_fmw->programs[prm_no]); + tasdevice_load_data(tas_priv, &(program->dev_data)); + for (i = 0; i < tas_priv->ndev; i++) { + if (tas_priv->tasdevice[i].is_loaderr == true) + continue; + else if (tas_priv->tasdevice[i].is_loaderr == false + && tas_priv->tasdevice[i].is_loading == true) { + struct tasdevice_fw *cal_fmw = + tas_priv->tasdevice[i].cali_data_fmw; + + if (cal_fmw) { + struct tasdevice_calibration *cal = + cal_fmw->calibrations; + + if (cal) + load_calib_data(tas_priv, + &(cal->dev_data)); + } + tas_priv->tasdevice[i].cur_prog = prm_no; + } + } + } + +out: + return prog_status; +} +EXPORT_SYMBOL_NS_GPL(tasdevice_prmg_calibdata_load, + SND_SOC_TAS2781_FMWLIB); + +void tasdevice_tuning_switch(void *context, int state) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + struct tasdevice_fw *tas_fmw = tas_priv->fmw; + int profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + return; + } + + if (state == 0) { + if (tas_priv->cur_prog < tas_fmw->nr_programs) { + /*dsp mode or tuning mode*/ + profile_cfg_id = tas_priv->rcabin.profile_cfg_id; + tasdevice_select_tuningprm_cfg(tas_priv, + tas_priv->cur_prog, tas_priv->cur_conf, + profile_cfg_id); + } + + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_POWER_UP); + } else + tasdevice_select_cfg_blk(tas_priv, profile_cfg_id, + TASDEVICE_BIN_BLK_PRE_SHUTDOWN); +} +EXPORT_SYMBOL_NS_GPL(tasdevice_tuning_switch, + SND_SOC_TAS2781_FMWLIB); + +MODULE_DESCRIPTION("Texas Firmware Support"); +MODULE_AUTHOR("Shenghao Ding, TI, <shenghao-ding@ti.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/tas2781-i2c.c b/sound/soc/codecs/tas2781-i2c.c new file mode 100644 index 000000000000..4c59429a42b7 --- /dev/null +++ b/sound/soc/codecs/tas2781-i2c.c @@ -0,0 +1,763 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// ALSA SoC Texas Instruments TAS2781 Audio Smart Amplifier +// +// Copyright (C) 2022 - 2023 Texas Instruments Incorporated +// https://www.ti.com +// +// The TAS2781 driver implements a flexible and configurable +// algo coefficient setting for one, two, or even multiple +// TAS2781 chips. +// +// Author: Shenghao Ding <shenghao-ding@ti.com> +// Author: Kevin Lu <kevin-lu@ti.com> +// + +#include <linux/crc8.h> +#include <linux/firmware.h> +#include <linux/gpio/consumer.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/of_irq.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tas2781.h> +#include <sound/tlv.h> +#include <sound/tas2781-tlv.h> + +static const struct i2c_device_id tasdevice_id[] = { + { "tas2781", TAS2781 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, tasdevice_id); + +#ifdef CONFIG_OF +static const struct of_device_id tasdevice_of_match[] = { + { .compatible = "ti,tas2781" }, + {}, +}; +MODULE_DEVICE_TABLE(of, tasdevice_of_match); +#endif + +/** + * tas2781_digital_getvol - get the volum control + * @kcontrol: control pointer + * @ucontrol: User data + * Customer Kcontrol for tas2781 is primarily for regmap booking, paging + * depends on internal regmap mechanism. + * tas2781 contains book and page two-level register map, especially + * book switching will set the register BXXP00R7F, after switching to the + * correct book, then leverage the mechanism for paging to access the + * register. + */ +static int tas2781_digital_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_digital_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_digital_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_getvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_getvol(tas_priv, ucontrol, mc); +} + +static int tas2781_amp_putvol(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(codec); + struct soc_mixer_control *mc = + (struct soc_mixer_control *)kcontrol->private_value; + + return tasdevice_amp_putvol(tas_priv, ucontrol, mc); +} + +static int tas2781_force_fwload_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = (int)tas_priv->force_fwload_status; + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return 0; +} + +static int tas2781_force_fwload_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = + snd_soc_component_get_drvdata(component); + bool change, val = (bool)ucontrol->value.integer.value[0]; + + if (tas_priv->force_fwload_status == val) + change = false; + else { + change = true; + tas_priv->force_fwload_status = val; + } + dev_dbg(tas_priv->dev, "%s : Force FWload %s\n", __func__, + tas_priv->force_fwload_status ? "ON" : "OFF"); + + return change; +} + +static const struct snd_kcontrol_new tas2781_snd_controls[] = { + SOC_SINGLE_RANGE_EXT_TLV("Speaker Analog Gain", TAS2781_AMP_LEVEL, + 1, 0, 20, 0, tas2781_amp_getvol, + tas2781_amp_putvol, amp_vol_tlv), + SOC_SINGLE_RANGE_EXT_TLV("Speaker Digital Gain", TAS2781_DVC_LVL, + 0, 0, 200, 1, tas2781_digital_getvol, + tas2781_digital_putvol, dvc_tlv), + SOC_SINGLE_BOOL_EXT("Speaker Force Firmware Load", 0, + tas2781_force_fwload_get, tas2781_force_fwload_put), +}; + +static int tasdevice_set_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->rcabin.profile_cfg_id != + ucontrol->value.integer.value[0]) { + tas_priv->rcabin.profile_cfg_id = + ucontrol->value.integer.value[0]; + ret = 1; + } + + return ret; +} + +static int tasdevice_info_programs(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_programs; + + return 0; +} + +static int tasdevice_info_configurations( + struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = + snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + struct tasdevice_fw *tas_fw = tas_priv->fmw; + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = (int)tas_fw->nr_configurations - 1; + + return 0; +} + +static int tasdevice_info_profile(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = tas_priv->rcabin.ncfgs - 1; + + return 0; +} + +static int tasdevice_get_profile_id(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->rcabin.profile_cfg_id; + + return 0; +} + +static int tasdevice_create_control(struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *prof_ctrls; + int nr_controls = 1; + int mix_index = 0; + int ret; + char *name; + + prof_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(prof_ctrls[0]), GFP_KERNEL); + if (!prof_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!name) { + ret = -ENOMEM; + goto out; + } + scnprintf(name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "Speaker Profile Id"); + prof_ctrls[mix_index].name = name; + prof_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + prof_ctrls[mix_index].info = tasdevice_info_profile; + prof_ctrls[mix_index].get = tasdevice_get_profile_id; + prof_ctrls[mix_index].put = tasdevice_set_profile_id; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, + prof_ctrls, nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static int tasdevice_program_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_prog; + + return 0; +} + +static int tasdevice_program_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_program = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_prog != nr_program) { + tas_priv->cur_prog = nr_program; + ret = 1; + } + + return ret; +} + +static int tasdevice_configuration_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + ucontrol->value.integer.value[0] = tas_priv->cur_conf; + + return 0; +} + +static int tasdevice_configuration_put( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + unsigned int nr_configuration = ucontrol->value.integer.value[0]; + int ret = 0; + + if (tas_priv->cur_conf != nr_configuration) { + tas_priv->cur_conf = nr_configuration; + ret = 1; + } + + return ret; +} + +static int tasdevice_dsp_create_ctrls( + struct tasdevice_priv *tas_priv) +{ + struct snd_kcontrol_new *dsp_ctrls; + char *prog_name, *conf_name; + int nr_controls = 2; + int mix_index = 0; + int ret; + + /* Alloc kcontrol via devm_kzalloc, which don't manually + * free the kcontrol + */ + dsp_ctrls = devm_kcalloc(tas_priv->dev, nr_controls, + sizeof(dsp_ctrls[0]), GFP_KERNEL); + if (!dsp_ctrls) { + ret = -ENOMEM; + goto out; + } + + /* Create a mixer item for selecting the active profile */ + prog_name = devm_kzalloc(tas_priv->dev, + SNDRV_CTL_ELEM_ID_NAME_MAXLEN, GFP_KERNEL); + conf_name = devm_kzalloc(tas_priv->dev, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + GFP_KERNEL); + if (!prog_name || !conf_name) { + ret = -ENOMEM; + goto out; + } + + scnprintf(prog_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Program Id"); + dsp_ctrls[mix_index].name = prog_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_programs; + dsp_ctrls[mix_index].get = tasdevice_program_get; + dsp_ctrls[mix_index].put = tasdevice_program_put; + mix_index++; + + scnprintf(conf_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, + "Speaker Config Id"); + dsp_ctrls[mix_index].name = conf_name; + dsp_ctrls[mix_index].iface = SNDRV_CTL_ELEM_IFACE_MIXER; + dsp_ctrls[mix_index].info = tasdevice_info_configurations; + dsp_ctrls[mix_index].get = tasdevice_configuration_get; + dsp_ctrls[mix_index].put = tasdevice_configuration_put; + mix_index++; + + ret = snd_soc_add_component_controls(tas_priv->codec, dsp_ctrls, + nr_controls < mix_index ? nr_controls : mix_index); + +out: + return ret; +} + +static void tasdevice_fw_ready(const struct firmware *fmw, + void *context) +{ + struct tasdevice_priv *tas_priv = context; + int ret = 0; + int i; + + mutex_lock(&tas_priv->codec_lock); + + ret = tasdevice_rca_parser(tas_priv, fmw); + if (ret) + goto out; + tasdevice_create_control(tas_priv); + + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; + scnprintf(tas_priv->coef_binaryname, 64, "%s_coef.bin", + tas_priv->dev_name); + ret = tasdevice_dsp_parser(tas_priv); + if (ret) { + dev_err(tas_priv->dev, "dspfw load %s error\n", + tas_priv->coef_binaryname); + tas_priv->fw_state = TASDEVICE_DSP_FW_FAIL; + goto out; + } + tasdevice_dsp_create_ctrls(tas_priv); + + tas_priv->fw_state = TASDEVICE_DSP_FW_ALL_OK; + + /* If calibrated data occurs error, dsp will still works with default + * calibrated data inside algo. + */ + for (i = 0; i < tas_priv->ndev; i++) { + scnprintf(tas_priv->cal_binaryname[i], 64, "%s_cal_0x%02x.bin", + tas_priv->dev_name, tas_priv->tasdevice[i].dev_addr); + ret = tas2781_load_calibration(tas_priv, + tas_priv->cal_binaryname[i], i); + if (ret != 0) + dev_err(tas_priv->dev, + "%s: load %s error, default will effect\n", + __func__, tas_priv->cal_binaryname[i]); + } + + tasdevice_prmg_calibdata_load(tas_priv, 0); + tas_priv->cur_prog = 0; +out: + if (tas_priv->fw_state == TASDEVICE_DSP_FW_FAIL) { + /*If DSP FW fail, kcontrol won't be created */ + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + } + mutex_unlock(&tas_priv->codec_lock); + if (fmw) + release_firmware(fmw); +} + +static int tasdevice_dapm_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *codec = snd_soc_dapm_to_component(w->dapm); + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int state = 0; + + /* Codec Lock Hold */ + mutex_lock(&tas_priv->codec_lock); + if (event == SND_SOC_DAPM_PRE_PMD) + state = 1; + tasdevice_tuning_switch(tas_priv, state); + /* Codec Lock Release*/ + mutex_unlock(&tas_priv->codec_lock); + + return 0; +} + +static const struct snd_soc_dapm_widget tasdevice_dapm_widgets[] = { + SND_SOC_DAPM_AIF_IN("ASI", "ASI Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT_E("ASI OUT", "ASI Capture", 0, SND_SOC_NOPM, + 0, 0, tasdevice_dapm_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + SND_SOC_DAPM_SPK("SPK", tasdevice_dapm_event), + SND_SOC_DAPM_OUTPUT("OUT"), + SND_SOC_DAPM_INPUT("DMIC") +}; + +static const struct snd_soc_dapm_route tasdevice_audio_map[] = { + {"SPK", NULL, "ASI"}, + {"OUT", NULL, "SPK"}, + {"ASI OUT", NULL, "DMIC"} +}; + +static int tasdevice_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *codec = dai->component; + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + int ret = 0; + + if (tas_priv->fw_state != TASDEVICE_DSP_FW_ALL_OK) { + dev_err(tas_priv->dev, "DSP bin file not loaded\n"); + ret = -EINVAL; + } + + return ret; +} + +static int tasdevice_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(dai); + unsigned int slot_width; + unsigned int fsrate; + int bclk_rate; + int rc = 0; + + fsrate = params_rate(params); + switch (fsrate) { + case 48000: + case 44100: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect sample rate = %u\n", + __func__, fsrate); + rc = -EINVAL; + goto out; + } + + slot_width = params_width(params); + switch (slot_width) { + case 16: + case 20: + case 24: + case 32: + break; + default: + dev_err(tas_priv->dev, "%s: incorrect slot width = %u\n", + __func__, slot_width); + rc = -EINVAL; + goto out; + } + + bclk_rate = snd_soc_params_to_bclk(params); + if (bclk_rate < 0) { + dev_err(tas_priv->dev, "%s: incorrect bclk rate = %d\n", + __func__, bclk_rate); + rc = bclk_rate; + goto out; + } + +out: + return rc; +} + +static int tasdevice_set_dai_sysclk(struct snd_soc_dai *codec_dai, + int clk_id, unsigned int freq, int dir) +{ + struct tasdevice_priv *tas_priv = snd_soc_dai_get_drvdata(codec_dai); + + tas_priv->sysclk = freq; + + return 0; +} + +static const struct snd_soc_dai_ops tasdevice_dai_ops = { + .startup = tasdevice_startup, + .hw_params = tasdevice_hw_params, + .set_sysclk = tasdevice_set_dai_sysclk, +}; + +static struct snd_soc_dai_driver tasdevice_dai_driver[] = { + { + .name = "tas2781_codec", + .id = 0, + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 4, + .rates = TASDEVICE_RATES, + .formats = TASDEVICE_FORMATS, + }, + .ops = &tasdevice_dai_ops, + .symmetric_rate = 1, + }, +}; + +static int tasdevice_codec_probe(struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + return tascodec_init(tas_priv, codec, tasdevice_fw_ready); +} + +static void tasdevice_deinit(void *context) +{ + struct tasdevice_priv *tas_priv = (struct tasdevice_priv *) context; + + tasdevice_config_info_remove(tas_priv); + tasdevice_dsp_remove(tas_priv); + tasdevice_calbin_remove(tas_priv); + tas_priv->fw_state = TASDEVICE_DSP_FW_PENDING; +} + +static void tasdevice_codec_remove( + struct snd_soc_component *codec) +{ + struct tasdevice_priv *tas_priv = snd_soc_component_get_drvdata(codec); + + tasdevice_deinit(tas_priv); +} + +static const struct snd_soc_component_driver + soc_codec_driver_tasdevice = { + .probe = tasdevice_codec_probe, + .remove = tasdevice_codec_remove, + .controls = tas2781_snd_controls, + .num_controls = ARRAY_SIZE(tas2781_snd_controls), + .dapm_widgets = tasdevice_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tasdevice_dapm_widgets), + .dapm_routes = tasdevice_audio_map, + .num_dapm_routes = ARRAY_SIZE(tasdevice_audio_map), + .idle_bias_on = 1, + .endianness = 1, +}; + +static void tasdevice_parse_dt(struct tasdevice_priv *tas_priv) +{ + struct i2c_client *client = (struct i2c_client *)tas_priv->client; + unsigned int dev_addrs[TASDEVICE_MAX_CHANNELS]; + int rc, i, ndev = 0; + + if (tas_priv->isacpi) { + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", NULL, 0); + if (ndev <= 0) { + ndev = 1; + dev_addrs[0] = client->addr; + } else { + ndev = (ndev < ARRAY_SIZE(dev_addrs)) + ? ndev : ARRAY_SIZE(dev_addrs); + ndev = device_property_read_u32_array(&client->dev, + "ti,audio-slots", dev_addrs, ndev); + } + + tas_priv->irq_info.irq_gpio = + acpi_dev_gpio_irq_get(ACPI_COMPANION(&client->dev), 0); + } else { + struct device_node *np = tas_priv->dev->of_node; +#ifdef CONFIG_OF + const __be32 *reg, *reg_end; + int len, sw, aw; + + aw = of_n_addr_cells(np); + sw = of_n_size_cells(np); + if (sw == 0) { + reg = (const __be32 *)of_get_property(np, + "reg", &len); + reg_end = reg + len/sizeof(*reg); + ndev = 0; + do { + dev_addrs[ndev] = of_read_number(reg, aw); + reg += aw; + ndev++; + } while (reg < reg_end); + } else { + ndev = 1; + dev_addrs[0] = client->addr; + } +#else + ndev = 1; + dev_addrs[0] = client->addr; +#endif + tas_priv->irq_info.irq_gpio = of_irq_get(np, 0); + } + tas_priv->ndev = ndev; + for (i = 0; i < ndev; i++) + tas_priv->tasdevice[i].dev_addr = dev_addrs[i]; + + tas_priv->reset = devm_gpiod_get_optional(&client->dev, + "reset-gpios", GPIOD_OUT_HIGH); + if (IS_ERR(tas_priv->reset)) + dev_err(tas_priv->dev, "%s Can't get reset GPIO\n", + __func__); + + strcpy(tas_priv->dev_name, tasdevice_id[tas_priv->chip_id].name); + + if (gpio_is_valid(tas_priv->irq_info.irq_gpio)) { + rc = gpio_request(tas_priv->irq_info.irq_gpio, + "AUDEV-IRQ"); + if (!rc) { + gpio_direction_input( + tas_priv->irq_info.irq_gpio); + + tas_priv->irq_info.irq = + gpio_to_irq(tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, "%s: GPIO %d request error\n", + __func__, tas_priv->irq_info.irq_gpio); + } else + dev_err(tas_priv->dev, + "Looking up irq-gpio property failed %d\n", + tas_priv->irq_info.irq_gpio); +} + +static int tasdevice_i2c_probe(struct i2c_client *i2c) +{ + const struct i2c_device_id *id = i2c_match_id(tasdevice_id, i2c); + const struct acpi_device_id *acpi_id; + struct tasdevice_priv *tas_priv; + int ret; + + tas_priv = tasdevice_kzalloc(i2c); + if (!tas_priv) + return -ENOMEM; + + if (ACPI_HANDLE(&i2c->dev)) { + acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table, + &i2c->dev); + if (!acpi_id) { + dev_err(&i2c->dev, "No driver data\n"); + ret = -EINVAL; + goto err; + } + tas_priv->chip_id = acpi_id->driver_data; + tas_priv->isacpi = true; + } else { + tas_priv->chip_id = id ? id->driver_data : 0; + tas_priv->isacpi = false; + } + + tasdevice_parse_dt(tas_priv); + + ret = tasdevice_init(tas_priv); + if (ret) + goto err; + + ret = devm_snd_soc_register_component(tas_priv->dev, + &soc_codec_driver_tasdevice, + tasdevice_dai_driver, ARRAY_SIZE(tasdevice_dai_driver)); + if (ret) { + dev_err(tas_priv->dev, "%s: codec register error:0x%08x\n", + __func__, ret); + goto err; + } +err: + if (ret < 0) + tasdevice_remove(tas_priv); + return ret; +} + +static void tasdevice_i2c_remove(struct i2c_client *client) +{ + struct tasdevice_priv *tas_priv = i2c_get_clientdata(client); + + tasdevice_remove(tas_priv); +} + +#ifdef CONFIG_ACPI +static const struct acpi_device_id tasdevice_acpi_match[] = { + { "TAS2781", TAS2781 }, + {}, +}; + +MODULE_DEVICE_TABLE(acpi, tasdevice_acpi_match); +#endif + +static struct i2c_driver tasdevice_i2c_driver = { + .driver = { + .name = "tas2781-codec", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(tasdevice_of_match), +#ifdef CONFIG_ACPI + .acpi_match_table = ACPI_PTR(tasdevice_acpi_match), +#endif + }, + .probe = tasdevice_i2c_probe, + .remove = tasdevice_i2c_remove, + .id_table = tasdevice_id, +}; + +module_i2c_driver(tasdevice_i2c_driver); + +MODULE_AUTHOR("Shenghao Ding <shenghao-ding@ti.com>"); +MODULE_AUTHOR("Kevin Lu <kevin-lu@ti.com>"); +MODULE_DESCRIPTION("ASoC TAS2781 Driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(SND_SOC_TAS2781_FMWLIB); diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c index f9e7122894bd..60e59e993ba6 100644 --- a/sound/soc/codecs/tas5086.c +++ b/sound/soc/codecs/tas5086.c @@ -990,7 +990,7 @@ static struct i2c_driver tas5086_i2c_driver = { .of_match_table = of_match_ptr(tas5086_dt_ids), }, .id_table = tas5086_i2c_id, - .probe_new = tas5086_i2c_probe, + .probe = tas5086_i2c_probe, .remove = tas5086_i2c_remove, }; diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c index f39c3273b2fd..1756edb35961 100644 --- a/sound/soc/codecs/tas571x.c +++ b/sound/soc/codecs/tas571x.c @@ -975,7 +975,7 @@ static struct i2c_driver tas571x_i2c_driver = { .name = "tas571x", .of_match_table = of_match_ptr(tas571x_of_match), }, - .probe_new = tas571x_i2c_probe, + .probe = tas571x_i2c_probe, .remove = tas571x_i2c_remove, .id_table = tas571x_i2c_id, }; diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c index 4d27b60bd804..6dd6c0896eff 100644 --- a/sound/soc/codecs/tas5720.c +++ b/sound/soc/codecs/tas5720.c @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> @@ -821,7 +820,7 @@ static struct i2c_driver tas5720_i2c_driver = { .name = "tas5720", .of_match_table = of_match_ptr(tas5720_of_match), }, - .probe_new = tas5720_probe, + .probe = tas5720_probe, .id_table = tas5720_id, }; diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c index 4e38eb7acea1..aca3756ffab6 100644 --- a/sound/soc/codecs/tas5805m.c +++ b/sound/soc/codecs/tas5805m.c @@ -597,7 +597,7 @@ MODULE_DEVICE_TABLE(of, tas5805m_of_match); #endif static struct i2c_driver tas5805m_i2c_driver = { - .probe_new = tas5805m_i2c_probe, + .probe = tas5805m_i2c_probe, .remove = tas5805m_i2c_remove, .id_table = tas5805m_i2c_id, .driver = { diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c index f8ff69fa2549..da89e8c681dd 100644 --- a/sound/soc/codecs/tas6424.c +++ b/sound/soc/codecs/tas6424.c @@ -11,7 +11,6 @@ #include <linux/errno.h> #include <linux/device.h> #include <linux/i2c.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/slab.h> #include <linux/regulator/consumer.h> @@ -803,7 +802,7 @@ static struct i2c_driver tas6424_i2c_driver = { .name = "tas6424", .of_match_table = of_match_ptr(tas6424_of_ids), }, - .probe_new = tas6424_i2c_probe, + .probe = tas6424_i2c_probe, .remove = tas6424_i2c_remove, .id_table = tas6424_i2c_ids, }; diff --git a/sound/soc/codecs/tda7419.c b/sound/soc/codecs/tda7419.c index d964e5207569..e187d74a1737 100644 --- a/sound/soc/codecs/tda7419.c +++ b/sound/soc/codecs/tda7419.c @@ -629,7 +629,7 @@ static struct i2c_driver tda7419_driver = { .name = "tda7419", .of_match_table = tda7419_of_match, }, - .probe_new = tda7419_probe, + .probe = tda7419_probe, .id_table = tda7419_i2c_id, }; diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c index 9f7902ec40db..8cca2ceadd9e 100644 --- a/sound/soc/codecs/tfa9879.c +++ b/sound/soc/codecs/tfa9879.c @@ -312,7 +312,7 @@ static struct i2c_driver tfa9879_i2c_driver = { .name = "tfa9879", .of_match_table = tfa9879_of_match, }, - .probe_new = tfa9879_i2c_probe, + .probe = tfa9879_i2c_probe, .id_table = tfa9879_i2c_id, }; diff --git a/sound/soc/codecs/tfa989x.c b/sound/soc/codecs/tfa989x.c index b853507e65a8..79847a90ac46 100644 --- a/sound/soc/codecs/tfa989x.c +++ b/sound/soc/codecs/tfa989x.c @@ -416,7 +416,7 @@ static struct i2c_driver tfa989x_i2c_driver = { .name = "tfa989x", .of_match_table = tfa989x_of_match, }, - .probe_new = tfa989x_i2c_probe, + .probe = tfa989x_i2c_probe, }; module_i2c_driver(tfa989x_i2c_driver); diff --git a/sound/soc/codecs/tlv320adc3xxx.c b/sound/soc/codecs/tlv320adc3xxx.c index 52bb55724724..b976c1946286 100644 --- a/sound/soc/codecs/tlv320adc3xxx.c +++ b/sound/soc/codecs/tlv320adc3xxx.c @@ -1451,7 +1451,7 @@ static struct i2c_driver adc3xxx_i2c_driver = { .name = "tlv320adc3xxx-codec", .of_match_table = tlv320adc3xxx_of_match, }, - .probe_new = adc3xxx_i2c_probe, + .probe = adc3xxx_i2c_probe, .remove = __exit_p(adc3xxx_i2c_remove), .id_table = adc3xxx_i2c_id, }; diff --git a/sound/soc/codecs/tlv320adcx140.c b/sound/soc/codecs/tlv320adcx140.c index 530f321d08e9..41342b340680 100644 --- a/sound/soc/codecs/tlv320adcx140.c +++ b/sound/soc/codecs/tlv320adcx140.c @@ -1208,7 +1208,7 @@ static struct i2c_driver adcx140_i2c_driver = { .name = "tlv320adcx140-codec", .of_match_table = of_match_ptr(tlv320adcx140_of_match), }, - .probe_new = adcx140_i2c_probe, + .probe = adcx140_i2c_probe, .id_table = adcx140_i2c_id, }; module_i2c_driver(adcx140_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic23-i2c.c b/sound/soc/codecs/tlv320aic23-i2c.c index 1f97673a1cc0..9692ae007c91 100644 --- a/sound/soc/codecs/tlv320aic23-i2c.c +++ b/sound/soc/codecs/tlv320aic23-i2c.c @@ -47,7 +47,7 @@ static struct i2c_driver tlv320aic23_i2c_driver = { .name = "tlv320aic23-codec", .of_match_table = of_match_ptr(tlv320aic23_of_match), }, - .probe_new = tlv320aic23_i2c_probe, + .probe = tlv320aic23_i2c_probe, .id_table = tlv320aic23_id, }; diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c index 0847302121f6..9611aa8acb0d 100644 --- a/sound/soc/codecs/tlv320aic31xx.c +++ b/sound/soc/codecs/tlv320aic31xx.c @@ -1746,7 +1746,7 @@ static struct i2c_driver aic31xx_i2c_driver = { .of_match_table = of_match_ptr(tlv320aic31xx_of_match), .acpi_match_table = ACPI_PTR(aic31xx_acpi_match), }, - .probe_new = aic31xx_i2c_probe, + .probe = aic31xx_i2c_probe, .id_table = aic31xx_i2c_id, }; module_i2c_driver(aic31xx_i2c_driver); diff --git a/sound/soc/codecs/tlv320aic32x4-i2c.c b/sound/soc/codecs/tlv320aic32x4-i2c.c index d1e543ca3521..49b33a256cd7 100644 --- a/sound/soc/codecs/tlv320aic32x4-i2c.c +++ b/sound/soc/codecs/tlv320aic32x4-i2c.c @@ -71,7 +71,7 @@ static struct i2c_driver aic32x4_i2c_driver = { .name = "tlv320aic32x4", .of_match_table = aic32x4_of_id, }, - .probe_new = aic32x4_i2c_probe, + .probe = aic32x4_i2c_probe, .remove = aic32x4_i2c_remove, .id_table = aic32x4_i2c_id, }; diff --git a/sound/soc/codecs/tlv320aic3x-i2c.c b/sound/soc/codecs/tlv320aic3x-i2c.c index d7e94d564dbf..bb33fd3dfb4f 100644 --- a/sound/soc/codecs/tlv320aic3x-i2c.c +++ b/sound/soc/codecs/tlv320aic3x-i2c.c @@ -61,7 +61,7 @@ static struct i2c_driver aic3x_i2c_driver = { .name = "tlv320aic3x", .of_match_table = aic3x_of_id, }, - .probe_new = aic3x_i2c_probe, + .probe = aic3x_i2c_probe, .remove = aic3x_i2c_remove, .id_table = aic3x_i2c_id, }; diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c index 16ce3ef1134b..fa46f51d4341 100644 --- a/sound/soc/codecs/tlv320dac33.c +++ b/sound/soc/codecs/tlv320dac33.c @@ -1560,7 +1560,7 @@ static struct i2c_driver tlv320dac33_i2c_driver = { .driver = { .name = "tlv320dac33-codec", }, - .probe_new = dac33_i2c_probe, + .probe = dac33_i2c_probe, .remove = dac33_i2c_remove, .id_table = tlv320dac33_i2c_id, }; diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c index 8c00db32996b..5bc486283fde 100644 --- a/sound/soc/codecs/tpa6130a2.c +++ b/sound/soc/codecs/tpa6130a2.c @@ -319,7 +319,7 @@ static struct i2c_driver tpa6130a2_i2c_driver = { .name = "tpa6130a2", .of_match_table = of_match_ptr(tpa6130a2_of_match), }, - .probe_new = tpa6130a2_probe, + .probe = tpa6130a2_probe, .id_table = tpa6130a2_id, }; diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c index 5282112c7d8d..6d9166d9116a 100644 --- a/sound/soc/codecs/ts3a227e.c +++ b/sound/soc/codecs/ts3a227e.c @@ -455,7 +455,7 @@ static struct i2c_driver ts3a227e_driver = { .of_match_table = of_match_ptr(ts3a227e_of_match), .acpi_match_table = ACPI_PTR(ts3a227e_acpi_match), }, - .probe_new = ts3a227e_i2c_probe, + .probe = ts3a227e_i2c_probe, .id_table = ts3a227e_i2c_ids, }; module_i2c_driver(ts3a227e_driver); diff --git a/sound/soc/codecs/tscs42xx.c b/sound/soc/codecs/tscs42xx.c index fa0c525189c2..1eefc1fe6ea8 100644 --- a/sound/soc/codecs/tscs42xx.c +++ b/sound/soc/codecs/tscs42xx.c @@ -1503,7 +1503,7 @@ static struct i2c_driver tscs42xx_i2c_driver = { .name = "tscs42xx", .of_match_table = tscs42xx_of_match, }, - .probe_new = tscs42xx_i2c_probe, + .probe = tscs42xx_i2c_probe, .id_table = tscs42xx_i2c_id, }; diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c index 38622bc247fc..744aef32a21f 100644 --- a/sound/soc/codecs/tscs454.c +++ b/sound/soc/codecs/tscs454.c @@ -3473,7 +3473,7 @@ static struct i2c_driver tscs454_i2c_driver = { .name = "tscs454", .of_match_table = tscs454_of_match, }, - .probe_new = tscs454_i2c_probe, + .probe = tscs454_i2c_probe, .id_table = tscs454_i2c_id, }; diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c index fdaaee845176..5c5751dc14e5 100644 --- a/sound/soc/codecs/uda1380.c +++ b/sound/soc/codecs/uda1380.c @@ -798,7 +798,7 @@ static struct i2c_driver uda1380_i2c_driver = { .name = "uda1380-codec", .of_match_table = uda1380_of_match, }, - .probe_new = uda1380_i2c_probe, + .probe = uda1380_i2c_probe, .id_table = uda1380_i2c_id, }; diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 9c10200ff34b..bd0e9fbc12eb 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1270,7 +1270,7 @@ static int wcd9380_probe(struct sdw_slave *pdev, /* Start in cache-only until device is enumerated */ regcache_cache_only(wcd->regmap, true); - }; + } pm_runtime_set_autosuspend_delay(dev, 3000); pm_runtime_use_autosuspend(dev); diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c index 034a4e858c7e..1d4259433f47 100644 --- a/sound/soc/codecs/wm0010.c +++ b/sound/soc/codecs/wm0010.c @@ -994,3 +994,6 @@ module_spi_driver(wm0010_spi_driver); MODULE_DESCRIPTION("ASoC WM0010 driver"); MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); MODULE_LICENSE("GPL"); + +MODULE_FIRMWARE("wm0010.dfw"); +MODULE_FIRMWARE("wm0010_stage2.bin"); diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c index 0064a607ec68..d7eeb41ba60f 100644 --- a/sound/soc/codecs/wm1250-ev1.c +++ b/sound/soc/codecs/wm1250-ev1.c @@ -243,7 +243,7 @@ static struct i2c_driver wm1250_ev1_i2c_driver = { .driver = { .name = "wm1250-ev1", }, - .probe_new = wm1250_ev1_probe, + .probe = wm1250_ev1_probe, .remove = wm1250_ev1_remove, .id_table = wm1250_ev1_i2c_id, }; diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c index 14b4fd97488c..9571ea53cb3f 100644 --- a/sound/soc/codecs/wm2000.c +++ b/sound/soc/codecs/wm2000.c @@ -938,7 +938,7 @@ static struct i2c_driver wm2000_i2c_driver = { .driver = { .name = "wm2000", }, - .probe_new = wm2000_i2c_probe, + .probe = wm2000_i2c_probe, .id_table = wm2000_i2c_id, }; diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c index 0a65afa44a59..277b8c468c78 100644 --- a/sound/soc/codecs/wm2200.c +++ b/sound/soc/codecs/wm2200.c @@ -2485,7 +2485,7 @@ static struct i2c_driver wm2200_i2c_driver = { .name = "wm2200", .pm = &wm2200_pm, }, - .probe_new = wm2200_i2c_probe, + .probe = wm2200_i2c_probe, .remove = wm2200_i2c_remove, .id_table = wm2200_i2c_id, }; diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c index 3b09d4a1684f..a86eacb2a9bb 100644 --- a/sound/soc/codecs/wm5100.c +++ b/sound/soc/codecs/wm5100.c @@ -2709,7 +2709,7 @@ static struct i2c_driver wm5100_i2c_driver = { .name = "wm5100", .pm = &wm5100_pm, }, - .probe_new = wm5100_i2c_probe, + .probe = wm5100_i2c_probe, .remove = wm5100_i2c_remove, .id_table = wm5100_i2c_id, }; diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c index e13f9780a111..c0ed76d5b65f 100644 --- a/sound/soc/codecs/wm8510.c +++ b/sound/soc/codecs/wm8510.c @@ -678,7 +678,7 @@ static struct i2c_driver wm8510_i2c_driver = { .name = "wm8510", .of_match_table = wm8510_of_match, }, - .probe_new = wm8510_i2c_probe, + .probe = wm8510_i2c_probe, .id_table = wm8510_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c index 66f6371d8acf..55c72c5ac845 100644 --- a/sound/soc/codecs/wm8523.c +++ b/sound/soc/codecs/wm8523.c @@ -527,7 +527,7 @@ static struct i2c_driver wm8523_i2c_driver = { .name = "wm8523", .of_match_table = wm8523_of_match, }, - .probe_new = wm8523_i2c_probe, + .probe = wm8523_i2c_probe, .id_table = wm8523_i2c_id, }; diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c index ca796aa0aeb7..34ae7fe05398 100644 --- a/sound/soc/codecs/wm8580.c +++ b/sound/soc/codecs/wm8580.c @@ -1049,7 +1049,7 @@ static struct i2c_driver wm8580_i2c_driver = { .name = "wm8580", .of_match_table = wm8580_of_match, }, - .probe_new = wm8580_i2c_probe, + .probe = wm8580_i2c_probe, .id_table = wm8580_i2c_id, }; diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c index 383c6796e8a3..903a0147d584 100644 --- a/sound/soc/codecs/wm8711.c +++ b/sound/soc/codecs/wm8711.c @@ -464,7 +464,7 @@ static struct i2c_driver wm8711_i2c_driver = { .name = "wm8711", .of_match_table = wm8711_of_match, }, - .probe_new = wm8711_i2c_probe, + .probe = wm8711_i2c_probe, .id_table = wm8711_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c index a3dbdbf40723..5ea6d8fd10f6 100644 --- a/sound/soc/codecs/wm8728.c +++ b/sound/soc/codecs/wm8728.c @@ -305,7 +305,7 @@ static struct i2c_driver wm8728_i2c_driver = { .name = "wm8728", .of_match_table = wm8728_of_match, }, - .probe_new = wm8728_i2c_probe, + .probe = wm8728_i2c_probe, .id_table = wm8728_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8731-i2c.c b/sound/soc/codecs/wm8731-i2c.c index fdf03bf91606..c39e637d813d 100644 --- a/sound/soc/codecs/wm8731-i2c.c +++ b/sound/soc/codecs/wm8731-i2c.c @@ -57,7 +57,7 @@ static struct i2c_driver wm8731_i2c_driver = { .name = "wm8731", .of_match_table = wm8731_of_match, }, - .probe_new = wm8731_i2c_probe, + .probe = wm8731_i2c_probe, .id_table = wm8731_i2c_id, }; diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c index 90b54343370c..9f4e372e90ea 100644 --- a/sound/soc/codecs/wm8737.c +++ b/sound/soc/codecs/wm8737.c @@ -649,7 +649,7 @@ static struct i2c_driver wm8737_i2c_driver = { .name = "wm8737", .of_match_table = wm8737_of_match, }, - .probe_new = wm8737_i2c_probe, + .probe = wm8737_i2c_probe, .id_table = wm8737_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c index c7afa4f2795d..787156b980a1 100644 --- a/sound/soc/codecs/wm8741.c +++ b/sound/soc/codecs/wm8741.c @@ -616,7 +616,7 @@ static struct i2c_driver wm8741_i2c_driver = { .name = "wm8741", .of_match_table = wm8741_of_match, }, - .probe_new = wm8741_i2c_probe, + .probe = wm8741_i2c_probe, .id_table = wm8741_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c index 2f6ee8d6639f..20dc9ff9fea9 100644 --- a/sound/soc/codecs/wm8750.c +++ b/sound/soc/codecs/wm8750.c @@ -813,7 +813,7 @@ static struct i2c_driver wm8750_i2c_driver = { .name = "wm8750", .of_match_table = wm8750_of_match, }, - .probe_new = wm8750_i2c_probe, + .probe = wm8750_i2c_probe, .id_table = wm8750_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c index bb18f58dc670..5e8a8eb41b2b 100644 --- a/sound/soc/codecs/wm8753.c +++ b/sound/soc/codecs/wm8753.c @@ -1590,7 +1590,7 @@ static struct i2c_driver wm8753_i2c_driver = { .name = "wm8753", .of_match_table = wm8753_of_match, }, - .probe_new = wm8753_i2c_probe, + .probe = wm8753_i2c_probe, .id_table = wm8753_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c index 936ea24621b0..212224a68006 100644 --- a/sound/soc/codecs/wm8776.c +++ b/sound/soc/codecs/wm8776.c @@ -523,7 +523,7 @@ static struct i2c_driver wm8776_i2c_driver = { .name = "wm8776", .of_match_table = wm8776_of_match, }, - .probe_new = wm8776_i2c_probe, + .probe = wm8776_i2c_probe, .id_table = wm8776_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8804-i2c.c b/sound/soc/codecs/wm8804-i2c.c index 3ce1a39d76eb..7062a8b2f8b5 100644 --- a/sound/soc/codecs/wm8804-i2c.c +++ b/sound/soc/codecs/wm8804-i2c.c @@ -60,7 +60,7 @@ static struct i2c_driver wm8804_i2c_driver = { .of_match_table = of_match_ptr(wm8804_of_match), .acpi_match_table = ACPI_PTR(wm8804_acpi_match), }, - .probe_new = wm8804_i2c_probe, + .probe = wm8804_i2c_probe, .remove = wm8804_i2c_remove, .id_table = wm8804_i2c_id }; diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c index 03bbd85ebdf4..320ccd92f318 100644 --- a/sound/soc/codecs/wm8900.c +++ b/sound/soc/codecs/wm8900.c @@ -1295,7 +1295,7 @@ static struct i2c_driver wm8900_i2c_driver = { .driver = { .name = "wm8900", }, - .probe_new = wm8900_i2c_probe, + .probe = wm8900_i2c_probe, .remove = wm8900_i2c_remove, .id_table = wm8900_i2c_id, }; diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c index 1dc8e20bdace..901b65ef8de5 100644 --- a/sound/soc/codecs/wm8903.c +++ b/sound/soc/codecs/wm8903.c @@ -2209,7 +2209,7 @@ static struct i2c_driver wm8903_i2c_driver = { .name = "wm8903", .of_match_table = wm8903_of_match, }, - .probe_new = wm8903_i2c_probe, + .probe = wm8903_i2c_probe, .remove = wm8903_i2c_remove, .id_table = wm8903_i2c_id, }; diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c index 791d8738d1c0..068e610b1b4c 100644 --- a/sound/soc/codecs/wm8904.c +++ b/sound/soc/codecs/wm8904.c @@ -2337,7 +2337,7 @@ static struct i2c_driver wm8904_i2c_driver = { .name = "wm8904", .of_match_table = of_match_ptr(wm8904_of_match), }, - .probe_new = wm8904_i2c_probe, + .probe = wm8904_i2c_probe, .id_table = wm8904_i2c_id, }; diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c index 8eb4782c9232..53c27986d216 100644 --- a/sound/soc/codecs/wm8940.c +++ b/sound/soc/codecs/wm8940.c @@ -860,7 +860,7 @@ static struct i2c_driver wm8940_i2c_driver = { .name = "wm8940", .of_match_table = wm8940_of_match, }, - .probe_new = wm8940_i2c_probe, + .probe = wm8940_i2c_probe, .id_table = wm8940_i2c_id, }; diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c index 05ef45672ebc..78044f580a67 100644 --- a/sound/soc/codecs/wm8955.c +++ b/sound/soc/codecs/wm8955.c @@ -1003,7 +1003,7 @@ static struct i2c_driver wm8955_i2c_driver = { .driver = { .name = "wm8955", }, - .probe_new = wm8955_i2c_probe, + .probe = wm8955_i2c_probe, .id_table = wm8955_i2c_id, }; diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c index 0d167238a369..366f5d769d6d 100644 --- a/sound/soc/codecs/wm8960.c +++ b/sound/soc/codecs/wm8960.c @@ -1518,7 +1518,7 @@ static struct i2c_driver wm8960_i2c_driver = { .of_match_table = of_match_ptr(wm8960_of_match), .acpi_match_table = ACPI_PTR(wm8960_acpi_match), }, - .probe_new = wm8960_i2c_probe, + .probe = wm8960_i2c_probe, .remove = wm8960_i2c_remove, .id_table = wm8960_i2c_id, }; diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c index a4857024711d..c076f78d04ce 100644 --- a/sound/soc/codecs/wm8961.c +++ b/sound/soc/codecs/wm8961.c @@ -982,7 +982,7 @@ static struct i2c_driver wm8961_i2c_driver = { .name = "wm8961", .of_match_table = of_match_ptr(wm8961_of_match), }, - .probe_new = wm8961_i2c_probe, + .probe = wm8961_i2c_probe, .id_table = wm8961_i2c_id, }; diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c index b901e4c65e8a..68ea15be7330 100644 --- a/sound/soc/codecs/wm8962.c +++ b/sound/soc/codecs/wm8962.c @@ -3946,7 +3946,7 @@ static struct i2c_driver wm8962_i2c_driver = { .of_match_table = wm8962_of_match, .pm = &wm8962_pm, }, - .probe_new = wm8962_i2c_probe, + .probe = wm8962_i2c_probe, .remove = wm8962_i2c_remove, .id_table = wm8962_i2c_id, }; diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c index 4db9248de54b..b22d8f0b59be 100644 --- a/sound/soc/codecs/wm8971.c +++ b/sound/soc/codecs/wm8971.c @@ -700,7 +700,7 @@ static struct i2c_driver wm8971_i2c_driver = { .driver = { .name = "wm8971", }, - .probe_new = wm8971_i2c_probe, + .probe = wm8971_i2c_probe, .id_table = wm8971_i2c_id, }; diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c index 010a394c705c..044b6f604c09 100644 --- a/sound/soc/codecs/wm8974.c +++ b/sound/soc/codecs/wm8974.c @@ -723,7 +723,7 @@ static struct i2c_driver wm8974_i2c_driver = { .name = "wm8974", .of_match_table = wm8974_of_match, }, - .probe_new = wm8974_i2c_probe, + .probe = wm8974_i2c_probe, .id_table = wm8974_i2c_id, }; diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c index aa2f55401a88..5c829301cf4c 100644 --- a/sound/soc/codecs/wm8978.c +++ b/sound/soc/codecs/wm8978.c @@ -1072,7 +1072,7 @@ static struct i2c_driver wm8978_i2c_driver = { .name = "wm8978", .of_match_table = wm8978_of_match, }, - .probe_new = wm8978_i2c_probe, + .probe = wm8978_i2c_probe, .id_table = wm8978_i2c_id, }; diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c index 50e6ac6ccbe0..2bd26e2478d9 100644 --- a/sound/soc/codecs/wm8983.c +++ b/sound/soc/codecs/wm8983.c @@ -1068,7 +1068,7 @@ static struct i2c_driver wm8983_i2c_driver = { .driver = { .name = "wm8983", }, - .probe_new = wm8983_i2c_probe, + .probe = wm8983_i2c_probe, .id_table = wm8983_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c index 751aa6730833..c0816bcfa294 100644 --- a/sound/soc/codecs/wm8985.c +++ b/sound/soc/codecs/wm8985.c @@ -1206,7 +1206,7 @@ static struct i2c_driver wm8985_i2c_driver = { .driver = { .name = "wm8985", }, - .probe_new = wm8985_i2c_probe, + .probe = wm8985_i2c_probe, .id_table = wm8985_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8988.c b/sound/soc/codecs/wm8988.c index 5dbdf647cd97..b440719cca7d 100644 --- a/sound/soc/codecs/wm8988.c +++ b/sound/soc/codecs/wm8988.c @@ -905,7 +905,7 @@ static struct i2c_driver wm8988_i2c_driver = { .driver = { .name = "wm8988", }, - .probe_new = wm8988_i2c_probe, + .probe = wm8988_i2c_probe, .id_table = wm8988_i2c_id, }; #endif diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c index 589af286f133..5a8e765090af 100644 --- a/sound/soc/codecs/wm8990.c +++ b/sound/soc/codecs/wm8990.c @@ -1247,7 +1247,7 @@ static struct i2c_driver wm8990_i2c_driver = { .driver = { .name = "wm8990", }, - .probe_new = wm8990_i2c_probe, + .probe = wm8990_i2c_probe, .id_table = wm8990_i2c_id, }; diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c index 30121993b7b4..8cb2ae829699 100644 --- a/sound/soc/codecs/wm8991.c +++ b/sound/soc/codecs/wm8991.c @@ -1323,7 +1323,7 @@ static struct i2c_driver wm8991_i2c_driver = { .driver = { .name = "wm8991", }, - .probe_new = wm8991_i2c_probe, + .probe = wm8991_i2c_probe, .id_table = wm8991_i2c_id, }; diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c index 22a47acbc6d1..feb997c698e2 100644 --- a/sound/soc/codecs/wm8993.c +++ b/sound/soc/codecs/wm8993.c @@ -1741,7 +1741,7 @@ static struct i2c_driver wm8993_i2c_driver = { .driver = { .name = "wm8993", }, - .probe_new = wm8993_i2c_probe, + .probe = wm8993_i2c_probe, .remove = wm8993_i2c_remove, .id_table = wm8993_i2c_id, }; diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c index eed48bf339f2..90588614edcc 100644 --- a/sound/soc/codecs/wm8995.c +++ b/sound/soc/codecs/wm8995.c @@ -2268,7 +2268,7 @@ static struct i2c_driver wm8995_i2c_driver = { .driver = { .name = "wm8995", }, - .probe_new = wm8995_i2c_probe, + .probe = wm8995_i2c_probe, .id_table = wm8995_i2c_id }; #endif diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c index b52ed89d631a..5d0eb0ae0475 100644 --- a/sound/soc/codecs/wm8996.c +++ b/sound/soc/codecs/wm8996.c @@ -3086,7 +3086,7 @@ static struct i2c_driver wm8996_i2c_driver = { .driver = { .name = "wm8996", }, - .probe_new = wm8996_i2c_probe, + .probe = wm8996_i2c_probe, .remove = wm8996_i2c_remove, .id_table = wm8996_i2c_id, }; diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c index 513ec0ba81bb..34a07db7342a 100644 --- a/sound/soc/codecs/wm9081.c +++ b/sound/soc/codecs/wm9081.c @@ -1369,7 +1369,7 @@ static struct i2c_driver wm9081_i2c_driver = { .driver = { .name = "wm9081", }, - .probe_new = wm9081_i2c_probe, + .probe = wm9081_i2c_probe, .remove = wm9081_i2c_remove, .id_table = wm9081_i2c_id, }; diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c index ef3524c3f07f..432729c753dd 100644 --- a/sound/soc/codecs/wm9090.c +++ b/sound/soc/codecs/wm9090.c @@ -616,7 +616,7 @@ static struct i2c_driver wm9090_i2c_driver = { .driver = { .name = "wm9090", }, - .probe_new = wm9090_i2c_probe, + .probe = wm9090_i2c_probe, .id_table = wm9090_id, }; diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c index 216120b68b64..5a89abfe8784 100644 --- a/sound/soc/codecs/wm_adsp.c +++ b/sound/soc/codecs/wm_adsp.c @@ -15,7 +15,6 @@ #include <linux/firmware.h> #include <linux/list.h> #include <linux/pm.h> -#include <linux/pm_runtime.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/slab.h> @@ -686,8 +685,6 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, { struct cs_dsp_coeff_ctl *cs_ctl = cs_dsp_get_ctl(&dsp->cs_dsp, name, type, alg); struct wm_coeff_ctl *ctl; - struct snd_kcontrol *kcontrol; - char ctl_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; int ret; ret = cs_dsp_coeff_write_ctrl(cs_ctl, 0, buf, len); @@ -699,23 +696,7 @@ int wm_adsp_write_ctl(struct wm_adsp *dsp, const char *name, int type, ctl = cs_ctl->priv; - if (dsp->component->name_prefix) - snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s %s", - dsp->component->name_prefix, ctl->name); - else - snprintf(ctl_name, SNDRV_CTL_ELEM_ID_NAME_MAXLEN, "%s", - ctl->name); - - kcontrol = snd_soc_card_get_kcontrol(dsp->component->card, ctl_name); - if (!kcontrol) { - adsp_err(dsp, "Can't find kcontrol %s\n", ctl_name); - return -EINVAL; - } - - snd_ctl_notify(dsp->component->card->snd_card, - SNDRV_CTL_EVENT_MASK_VALUE, &kcontrol->id); - - return 0; + return snd_soc_component_notify_control(dsp->component, ctl->name); } EXPORT_SYMBOL_GPL(wm_adsp_write_ctl); diff --git a/sound/soc/codecs/wsa883x.c b/sound/soc/codecs/wsa883x.c index e80b53143569..e40d583a1ce6 100644 --- a/sound/soc/codecs/wsa883x.c +++ b/sound/soc/codecs/wsa883x.c @@ -1333,7 +1333,8 @@ static int wsa883x_digital_mute(struct snd_soc_dai *dai, int mute, int stream) WSA883X_DRE_GAIN_EN_MASK, WSA883X_DRE_GAIN_FROM_CSR); snd_soc_component_write_field(component, WSA883X_PA_FSM_CTL, - WSA883X_GLOBAL_PA_EN_MASK, 1); + WSA883X_GLOBAL_PA_EN_MASK, + WSA883X_GLOBAL_PA_ENABLE); } diff --git a/sound/soc/codecs/wsa884x.c b/sound/soc/codecs/wsa884x.c new file mode 100644 index 000000000000..993d76b18b53 --- /dev/null +++ b/sound/soc/codecs/wsa884x.c @@ -0,0 +1,1936 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2023, Linaro Ltd. + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/gpio/consumer.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/soundwire/sdw.h> +#include <linux/soundwire/sdw_registers.h> +#include <linux/soundwire/sdw_type.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc-dapm.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define WSA884X_BASE 0x3000 +#define WSA884X_ANA_BG_TSADC_BASE (WSA884X_BASE + 0x0001) +#define WSA884X_BG_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x00) +#define WSA884X_ADC_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x01) +#define WSA884X_BOP1_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x02) +#define WSA884X_BOP2_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x03) +#define WSA884X_BOP2_PROG_BOP2_VTH_MASK 0xf0 +#define WSA884X_BOP2_PROG_BOP2_VTH_SHIFT 4 +#define WSA884X_BOP2_PROG_BOP2_HYST_MASK 0x0f +#define WSA884X_BOP2_PROG_BOP2_HYST_SHIFT 0 +#define WSA884X_UVLO_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x04) +#define WSA884X_UVLO_PROG1 (WSA884X_ANA_BG_TSADC_BASE + 0x05) +#define WSA884X_SPARE_CTRL_0 (WSA884X_ANA_BG_TSADC_BASE + 0x06) +#define WSA884X_SPARE_CTRL_1 (WSA884X_ANA_BG_TSADC_BASE + 0x07) +#define WSA884X_SPARE_CTRL_2 (WSA884X_ANA_BG_TSADC_BASE + 0x08) +#define WSA884X_SPARE_CTRL_3 (WSA884X_ANA_BG_TSADC_BASE + 0x09) +#define WSA884X_REF_CTRL (WSA884X_ANA_BG_TSADC_BASE + 0x0a) +#define WSA884X_REF_CTRL_BG_RDY_SEL_MASK 0x03 +#define WSA884X_REF_CTRL_BG_RDY_SEL_SHIFT 0 +#define WSA884X_BG_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0b) +#define WSA884X_BG_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x0c) +#define WSA884X_ADC_PROG (WSA884X_ANA_BG_TSADC_BASE + 0x0d) +#define WSA884X_ADC_IREF_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0e) +#define WSA884X_ADC_ISENS_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x0f) +#define WSA884X_ADC_CLK_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x10) +#define WSA884X_ADC_TEST_CTL (WSA884X_ANA_BG_TSADC_BASE + 0x11) +#define WSA884X_ADC_BIAS (WSA884X_ANA_BG_TSADC_BASE + 0x12) +#define WSA884X_VBAT_SNS (WSA884X_ANA_BG_TSADC_BASE + 0x13) +#define WSA884X_DOUT_MSB (WSA884X_ANA_BG_TSADC_BASE + 0x14) +#define WSA884X_DOUT_LSB (WSA884X_ANA_BG_TSADC_BASE + 0x15) +#define WSA884X_BOP_ATEST_SEL (WSA884X_ANA_BG_TSADC_BASE + 0x16) +#define WSA884X_MISC0 (WSA884X_ANA_BG_TSADC_BASE + 0x17) +#define WSA884X_MISC1 (WSA884X_ANA_BG_TSADC_BASE + 0x18) +#define WSA884X_MISC2 (WSA884X_ANA_BG_TSADC_BASE + 0x19) +#define WSA884X_MISC3 (WSA884X_ANA_BG_TSADC_BASE + 0x1a) +#define WSA884X_SPARE_TSBG_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1b) +#define WSA884X_SPARE_TUNE_0 (WSA884X_ANA_BG_TSADC_BASE + 0x1c) +#define WSA884X_SPARE_TUNE_1 (WSA884X_ANA_BG_TSADC_BASE + 0x1d) + +#define WSA884X_ANA_IVSENSE_BASE (WSA884X_BASE + 0x0020) +#define WSA884X_VSENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x00) +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK 0xe0 +#define WSA884X_VSENSE1_GAIN_VSENSE_FE_SHIFT 5 +#define WSA884X_ISENSE2 (WSA884X_ANA_IVSENSE_BASE + 0x01) +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK 0xe0 +#define WSA884X_ISENSE2_ISENSE_GAIN_CTL_SHIFT 5 + +#define WSA884X_SPARE_CTL_1 (WSA884X_ANA_IVSENSE_BASE + 0x02) +#define WSA884X_SPARE_CTL_2 (WSA884X_ANA_IVSENSE_BASE + 0x03) +#define WSA884X_SPARE_CTL_3 (WSA884X_ANA_IVSENSE_BASE + 0x04) +#define WSA884X_SPARE_CTL_4 (WSA884X_ANA_IVSENSE_BASE + 0x05) +#define WSA884X_EN (WSA884X_ANA_IVSENSE_BASE + 0x06) +#define WSA884X_OVERRIDE1 (WSA884X_ANA_IVSENSE_BASE + 0x07) +#define WSA884X_OVERRIDE2 (WSA884X_ANA_IVSENSE_BASE + 0x08) +#define WSA884X_ISENSE1 (WSA884X_ANA_IVSENSE_BASE + 0x09) +#define WSA884X_ISENSE_CAL (WSA884X_ANA_IVSENSE_BASE + 0x0a) +#define WSA884X_MISC (WSA884X_ANA_IVSENSE_BASE + 0x0b) +#define WSA884X_ADC_0 (WSA884X_ANA_IVSENSE_BASE + 0x0c) +#define WSA884X_ADC_1 (WSA884X_ANA_IVSENSE_BASE + 0x0d) +#define WSA884X_ADC_2 (WSA884X_ANA_IVSENSE_BASE + 0x0e) +#define WSA884X_ADC_3 (WSA884X_ANA_IVSENSE_BASE + 0x0f) +#define WSA884X_ADC_4 (WSA884X_ANA_IVSENSE_BASE + 0x10) +#define WSA884X_ADC_5 (WSA884X_ANA_IVSENSE_BASE + 0x11) +#define WSA884X_ADC_6 (WSA884X_ANA_IVSENSE_BASE + 0x12) +#define WSA884X_ADC_7 (WSA884X_ANA_IVSENSE_BASE + 0x13) +#define WSA884X_STATUS (WSA884X_ANA_IVSENSE_BASE + 0x14) +#define WSA884X_IVSENSE_SPARE_TUNE_1 (WSA884X_ANA_IVSENSE_BASE + 0x15) +#define WSA884X_SPARE_TUNE_2 (WSA884X_ANA_IVSENSE_BASE + 0x16) +#define WSA884X_SPARE_TUNE_3 (WSA884X_ANA_IVSENSE_BASE + 0x17) +#define WSA884X_SPARE_TUNE_4 (WSA884X_ANA_IVSENSE_BASE + 0x18) + +#define WSA884X_ANA_SPK_TOP_BASE (WSA884X_BASE + 0x0040) +#define WSA884X_TOP_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x00) +#define WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK 0x01 +#define WSA884X_CLIP_DET_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x01) +#define WSA884X_CLIP_DET_CTRL2 (WSA884X_ANA_SPK_TOP_BASE + 0x02) +#define WSA884X_DAC_CTRL1 (WSA884X_ANA_SPK_TOP_BASE + 0x03) +#define WSA884X_DAC_VCM_CTRL_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x04) +#define WSA884X_DAC_VCM_CTRL_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x05) +#define WSA884X_DAC_VCM_CTRL_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x06) +#define WSA884X_DAC_VCM_CTRL_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x07) +#define WSA884X_DAC_VCM_CTRL_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x08) +#define WSA884X_DAC_VCM_CTRL_REG6 (WSA884X_ANA_SPK_TOP_BASE + 0x09) +#define WSA884X_PWM_CLK_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0a) +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_MASK 0x80 +#define WSA884X_PWM_CLK_CTL_VCMO_INT1_IDLE_MODE_OVRT_SHIFT 7 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_MASK 0x40 +#define WSA884X_PWM_CLK_CTL_REG_MCLK_DIV_RATIO_SHIFT 6 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_MASK 0x30 +#define WSA884X_PWM_CLK_CTL_PWM_DEGLITCH_CLK_DELAY_CTRL_SHIFT 4 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK 0x08 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_SHIFT 3 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_MASK 0x06 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_RATIO_SHIFT 1 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_MASK 0x01 +#define WSA884X_PWM_CLK_CTL_PWM_CLK_DIV_BYPASS_SHIFT 0 +#define WSA884X_DRV_LF_LDO_SEL (WSA884X_ANA_SPK_TOP_BASE + 0x0b) +#define WSA884X_OCP_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0c) +#define WSA884X_PDRV_HS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0d) +#define WSA884X_PDRV_LS_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x0e) +#define WSA884X_SPK_TOP_SPARE_CTL_1 (WSA884X_ANA_SPK_TOP_BASE + 0x0f) +#define WSA884X_SPK_TOP_SPARE_CTL_2 (WSA884X_ANA_SPK_TOP_BASE + 0x10) +#define WSA884X_SPK_TOP_SPARE_CTL_3 (WSA884X_ANA_SPK_TOP_BASE + 0x11) +#define WSA884X_SPK_TOP_SPARE_CTL_4 (WSA884X_ANA_SPK_TOP_BASE + 0x12) +#define WSA884X_SPARE_CTL_5 (WSA884X_ANA_SPK_TOP_BASE + 0x13) +#define WSA884X_DAC_EN_DEBUG_REG (WSA884X_ANA_SPK_TOP_BASE + 0x14) +#define WSA884X_DAC_OPAMP_BIAS1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x15) +#define WSA884X_DAC_OPAMP_BIAS2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x16) +#define WSA884X_DAC_TUNE1 (WSA884X_ANA_SPK_TOP_BASE + 0x17) +#define WSA884X_DAC_VOLTAGE_CTRL_REG (WSA884X_ANA_SPK_TOP_BASE + 0x18) +#define WSA884X_ATEST1_REG (WSA884X_ANA_SPK_TOP_BASE + 0x19) +#define WSA884X_ATEST2_REG (WSA884X_ANA_SPK_TOP_BASE + 0x1a) +#define WSA884X_TOP_BIAS_REG1 (WSA884X_ANA_SPK_TOP_BASE + 0x1b) +#define WSA884X_TOP_BIAS_REG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1c) +#define WSA884X_TOP_BIAS_REG3 (WSA884X_ANA_SPK_TOP_BASE + 0x1d) +#define WSA884X_TOP_BIAS_REG4 (WSA884X_ANA_SPK_TOP_BASE + 0x1e) +#define WSA884X_PWRSTG_DBG2 (WSA884X_ANA_SPK_TOP_BASE + 0x1f) +#define WSA884X_DRV_LF_BLK_EN (WSA884X_ANA_SPK_TOP_BASE + 0x20) +#define WSA884X_DRV_LF_EN (WSA884X_ANA_SPK_TOP_BASE + 0x21) +#define WSA884X_DRV_LF_MASK_DCC_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x22) +#define WSA884X_DRV_LF_MISC_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x23) +#define WSA884X_DRV_LF_REG_GAIN (WSA884X_ANA_SPK_TOP_BASE + 0x24) +#define WSA884X_DRV_OS_CAL_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x25) +#define WSA884X_DRV_OS_CAL_CTL1 (WSA884X_ANA_SPK_TOP_BASE + 0x26) +#define WSA884X_PWRSTG_DBG (WSA884X_ANA_SPK_TOP_BASE + 0x27) +#define WSA884X_BBM_CTL (WSA884X_ANA_SPK_TOP_BASE + 0x28) +#define WSA884X_TOP_MISC1 (WSA884X_ANA_SPK_TOP_BASE + 0x29) +#define WSA884X_DAC_VCM_CTRL_REG7 (WSA884X_ANA_SPK_TOP_BASE + 0x2a) +#define WSA884X_TOP_BIAS_REG5 (WSA884X_ANA_SPK_TOP_BASE + 0x2b) +#define WSA884X_DRV_LF_MISC_CTL2 (WSA884X_ANA_SPK_TOP_BASE + 0x2c) +#define WSA884X_SPK_TOP_SPARE_TUNE_2 (WSA884X_ANA_SPK_TOP_BASE + 0x2d) +#define WSA884X_SPK_TOP_SPARE_TUNE_3 (WSA884X_ANA_SPK_TOP_BASE + 0x2e) +#define WSA884X_SPK_TOP_SPARE_TUNE_4 (WSA884X_ANA_SPK_TOP_BASE + 0x2f) +#define WSA884X_SPARE_TUNE_5 (WSA884X_ANA_SPK_TOP_BASE + 0x30) +#define WSA884X_SPARE_TUNE_6 (WSA884X_ANA_SPK_TOP_BASE + 0x31) +#define WSA884X_SPARE_TUNE_7 (WSA884X_ANA_SPK_TOP_BASE + 0x32) +#define WSA884X_SPARE_TUNE_8 (WSA884X_ANA_SPK_TOP_BASE + 0x33) +#define WSA884X_SPARE_TUNE_9 (WSA884X_ANA_SPK_TOP_BASE + 0x34) +#define WSA884X_SPARE_TUNE_10 (WSA884X_ANA_SPK_TOP_BASE + 0x35) +#define WSA884X_PA_STATUS0 (WSA884X_ANA_SPK_TOP_BASE + 0x36) +#define WSA884X_PA_STATUS1 (WSA884X_ANA_SPK_TOP_BASE + 0x37) +#define WSA884X_PA_STATUS2 (WSA884X_ANA_SPK_TOP_BASE + 0x38) +#define WSA884X_PA_STATUS3 (WSA884X_ANA_SPK_TOP_BASE + 0x39) +#define WSA884X_PA_STATUS4 (WSA884X_ANA_SPK_TOP_BASE + 0x3a) +#define WSA884X_PA_STATUS5 (WSA884X_ANA_SPK_TOP_BASE + 0x3b) +#define WSA884X_SPARE_RO_1 (WSA884X_ANA_SPK_TOP_BASE + 0x3c) +#define WSA884X_SPARE_RO_2 (WSA884X_ANA_SPK_TOP_BASE + 0x3d) +#define WSA884X_SPARE_RO_3 (WSA884X_ANA_SPK_TOP_BASE + 0x3e) + +#define WSA884X_ANA_BOOST_BASE (WSA884X_BASE + 0x0090) +#define WSA884X_STB_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x00) +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK 0xf8 +#define WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_SHIFT 3 +#define WSA884X_STB_CTRL1_VOUT_FS_MASK 0x07 +#define WSA884X_STB_CTRL1_VOUT_FS_SHIFT 0 +#define WSA884X_CURRENT_LIMIT (WSA884X_ANA_BOOST_BASE + 0x01) +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK 0x80 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_SHIFT 7 +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK 0x7c +#define WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_SHIFT 2 +#define WSA884X_CURRENT_LIMIT_CLK_PHASE_SHIFT 0 +#define WSA884X_BYP_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x02) +#define WSA884X_SPARE_CTL_0 (WSA884X_ANA_BOOST_BASE + 0x03) +#define WSA884X_BOOST_SPARE_CTL_1 (WSA884X_ANA_BOOST_BASE + 0x04) +#define WSA884X_SPARE_RO_0 (WSA884X_ANA_BOOST_BASE + 0x05) +#define WSA884X_BOOST_SPARE_RO_1 (WSA884X_ANA_BOOST_BASE + 0x06) +#define WSA884X_IBIAS1 (WSA884X_ANA_BOOST_BASE + 0x07) +#define WSA884X_IBIAS2 (WSA884X_ANA_BOOST_BASE + 0x08) +#define WSA884X_IBIAS3 (WSA884X_ANA_BOOST_BASE + 0x09) +#define WSA884X_EN_CTRL (WSA884X_ANA_BOOST_BASE + 0x0a) +#define WSA884X_STB_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0b) +#define WSA884X_STB_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0c) +#define WSA884X_STB_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x0d) +#define WSA884X_BYP_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x0e) +#define WSA884X_BYP_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x0f) +#define WSA884X_ZX_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x10) +#define WSA884X_ZX_CTRL1_ZX_DET_EN_MASK 0x80 +#define WSA884X_ZX_CTRL1_ZX_DET_EN_SHIFT 7 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_MASK 0x40 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_EN_SHIFT 6 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_MASK 0x20 +#define WSA884X_ZX_CTRL1_ZX_DET_STAGE_DEFAULT_SHIFT 5 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK 0x18 +#define WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_SHIFT 3 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_MASK 0x04 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_IGNORE_SHIFT 2 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_MASK 0x02 +#define WSA884X_ZX_CTRL1_ZX_BYP_MASK_DEL_SHIFT 1 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_MASK 0x01 +#define WSA884X_ZX_CTRL1_BOOTCAP_REFRESH_DIS_SHIFT 0 +#define WSA884X_ZX_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x11) +#define WSA884X_BLEEDER_CTRL (WSA884X_ANA_BOOST_BASE + 0x12) +#define WSA884X_BOOST_MISC (WSA884X_ANA_BOOST_BASE + 0x13) +#define WSA884X_PWRSTAGE_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x14) +#define WSA884X_PWRSTAGE_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x15) +#define WSA884X_PWRSTAGE_CTRL3 (WSA884X_ANA_BOOST_BASE + 0x16) +#define WSA884X_PWRSTAGE_CTRL4 (WSA884X_ANA_BOOST_BASE + 0x17) +#define WSA884X_MAXD_REG1 (WSA884X_ANA_BOOST_BASE + 0x18) +#define WSA884X_MAXD_REG2 (WSA884X_ANA_BOOST_BASE + 0x19) +#define WSA884X_ILIM_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1a) +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_MASK 0x80 +#define WSA884X_ILIM_CTRL1_EN_AUTO_MAXD_SEL_SHIFT 0x07 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_MASK 0x40 +#define WSA884X_ILIM_CTRL1_EN_ILIM_SW_CLH_SHIFT 0x06 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_MASK 0x38 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_CLH_SHIFT 0x03 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK 0x07 +#define WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_SHIFT 0x00 +#define WSA884X_ILIM_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1b) +#define WSA884X_TEST_CTRL1 (WSA884X_ANA_BOOST_BASE + 0x1c) +#define WSA884X_TEST_CTRL2 (WSA884X_ANA_BOOST_BASE + 0x1d) +#define WSA884X_SPARE1 (WSA884X_ANA_BOOST_BASE + 0x1e) +#define WSA884X_BOOT_CAP_CHECK (WSA884X_ANA_BOOST_BASE + 0x1f) + +#define WSA884X_ANA_PON_LDOL_BASE (WSA884X_BASE + 0x00b0) +#define WSA884X_PON_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x00) +#define WSA884X_PWRSAV_CTL (WSA884X_ANA_PON_LDOL_BASE + 0x01) +#define WSA884X_PON_LDOL_SPARE_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x02) +#define WSA884X_PON_LDOL_SPARE_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x03) +#define WSA884X_PON_LDOL_SPARE_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x04) +#define WSA884X_PON_LDOL_SPARE_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x05) +#define WSA884X_PON_CLT_1 (WSA884X_ANA_PON_LDOL_BASE + 0x06) +#define WSA884X_PON_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x07) +#define WSA884X_PON_CTL_3 (WSA884X_ANA_PON_LDOL_BASE + 0x08) +#define WSA884X_CKWD_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x09) +#define WSA884X_CKWD_CTL_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0a) +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK 0x20 +#define WSA884X_CKWD_CTL_1_VPP_SW_CTL_SHIFT 5 +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK 0x1f +#define WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_SHIFT 0 +#define WSA884X_CKWD_CTL_2 (WSA884X_ANA_PON_LDOL_BASE + 0x0b) +#define WSA884X_CKSK_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0c) +#define WSA884X_PADSW_CTL_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0d) +#define WSA884X_TEST_0 (WSA884X_ANA_PON_LDOL_BASE + 0x0e) +#define WSA884X_TEST_1 (WSA884X_ANA_PON_LDOL_BASE + 0x0f) +#define WSA884X_STATUS_0 (WSA884X_ANA_PON_LDOL_BASE + 0x10) +#define WSA884X_STATUS_1 (WSA884X_ANA_PON_LDOL_BASE + 0x11) +#define WSA884X_PON_LDOL_SPARE_TUNE_0 (WSA884X_ANA_PON_LDOL_BASE + 0x12) +#define WSA884X_PON_LDOL_SPARE_TUNE_1 (WSA884X_ANA_PON_LDOL_BASE + 0x13) +#define WSA884X_PON_LDOL_SPARE_TUNE_2 (WSA884X_ANA_PON_LDOL_BASE + 0x14) +#define WSA884X_PON_LDOL_SPARE_TUNE_3 (WSA884X_ANA_PON_LDOL_BASE + 0x15) +#define WSA884X_PON_LDOL_SPARE_TUNE_4 (WSA884X_ANA_PON_LDOL_BASE + 0x16) + +#define WSA884X_DIG_CTRL0_BASE (WSA884X_BASE + 0x0400) +#define WSA884X_DIG_CTRL0_PAGE (WSA884X_DIG_CTRL0_BASE + 0x00) +#define WSA884X_CHIP_ID0 (WSA884X_DIG_CTRL0_BASE + 0x01) +#define WSA884X_CHIP_ID1 (WSA884X_DIG_CTRL0_BASE + 0x02) +#define WSA884X_CHIP_ID2 (WSA884X_DIG_CTRL0_BASE + 0x03) +#define WSA884X_CHIP_ID3 (WSA884X_DIG_CTRL0_BASE + 0x04) +#define WSA884X_BUS_ID (WSA884X_DIG_CTRL0_BASE + 0x05) +#define WSA884X_CDC_RST_CTL (WSA884X_DIG_CTRL0_BASE + 0x10) +#define WSA884X_SWR_RESET_EN (WSA884X_DIG_CTRL0_BASE + 0x14) +#define WSA884X_TOP_CLK_CFG (WSA884X_DIG_CTRL0_BASE + 0x18) +#define WSA884X_SWR_CLK_RATE (WSA884X_DIG_CTRL0_BASE + 0x19) +#define WSA884X_CDC_PATH_MODE (WSA884X_DIG_CTRL0_BASE + 0x1a) +#define WSA884X_CDC_PATH_MODE_RXD_MODE_MASK 0x02 +#define WSA884X_CDC_PATH_MODE_RXD_MODE_SHIFT 0 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_MASK 0x01 +#define WSA884X_CDC_PATH_MODE_TXD_MODE_SHIFT 0 +#define WSA884X_CDC_CLK_CTL (WSA884X_DIG_CTRL0_BASE + 0x1c) +#define WSA884X_PA_FSM_EN (WSA884X_DIG_CTRL0_BASE + 0x30) +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK 0x01 +#define WSA884X_PA_FSM_EN_GLOBAL_PA_EN_SHIFT 0 +#define WSA884X_PA_FSM_CTL0 (WSA884X_DIG_CTRL0_BASE + 0x31) +#define WSA884X_PA_FSM_CTL1 (WSA884X_DIG_CTRL0_BASE + 0x32) +#define WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK 0x38 +#define WSA884X_PA_FSM_TIMER0 (WSA884X_DIG_CTRL0_BASE + 0x33) +#define WSA884X_PA_FSM_TIMER1 (WSA884X_DIG_CTRL0_BASE + 0x34) +#define WSA884X_PA_FSM_STA0 (WSA884X_DIG_CTRL0_BASE + 0x35) +#define WSA884X_PA_FSM_STA1 (WSA884X_DIG_CTRL0_BASE + 0x36) +#define WSA884X_PA_FSM_ERR_CTL (WSA884X_DIG_CTRL0_BASE + 0x37) +#define WSA884X_PA_FSM_ERR_COND0 (WSA884X_DIG_CTRL0_BASE + 0x38) +#define WSA884X_PA_FSM_ERR_COND1 (WSA884X_DIG_CTRL0_BASE + 0x39) +#define WSA884X_PA_FSM_MSK0 (WSA884X_DIG_CTRL0_BASE + 0x3a) +#define WSA884X_PA_FSM_MSK1 (WSA884X_DIG_CTRL0_BASE + 0x3b) +#define WSA884X_PA_FSM_BYP_CTL (WSA884X_DIG_CTRL0_BASE + 0x3c) +#define WSA884X_PA_FSM_BYP0 (WSA884X_DIG_CTRL0_BASE + 0x3d) +#define WSA884X_PA_FSM_BYP1 (WSA884X_DIG_CTRL0_BASE + 0x3e) +#define WSA884X_TADC_VALUE_CTL (WSA884X_DIG_CTRL0_BASE + 0x50) +#define WSA884X_TEMP_DETECT_CTL (WSA884X_DIG_CTRL0_BASE + 0x51) +#define WSA884X_TEMP_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x52) +#define WSA884X_TEMP_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x53) +#define WSA884X_TEMP_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x54) +#define WSA884X_TEMP_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x55) +#define WSA884X_TEMP_CONFIG0 (WSA884X_DIG_CTRL0_BASE + 0x56) +#define WSA884X_TEMP_CONFIG1 (WSA884X_DIG_CTRL0_BASE + 0x57) +#define WSA884X_VBAT_THRM_FLT_CTL (WSA884X_DIG_CTRL0_BASE + 0x58) +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_MASK 0xe0 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_COEF_SEL_SHIFT 5 +#define WSA884X_VBAT_THRM_FLT_CTL_THRM_FLT_EN_SHIFT 4 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK 0x0e +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_SHIFT 1 +#define WSA884X_VBAT_THRM_FLT_CTL_VBAT_FLT_EN_SHIFT 0 +#define WSA884X_VBAT_CAL_CTL (WSA884X_DIG_CTRL0_BASE + 0x59) +#define WSA884X_VBAT_CAL_CTL_RESERVE_MASK 0x0e +#define WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK 0x01 +#define WSA884X_VBAT_DIN_MSB (WSA884X_DIG_CTRL0_BASE + 0x5a) +#define WSA884X_VBAT_DIN_LSB (WSA884X_DIG_CTRL0_BASE + 0x5b) +#define WSA884X_VBAT_DOUT_MSB (WSA884X_DIG_CTRL0_BASE + 0x5c) +#define WSA884X_VBAT_DOUT_LSB (WSA884X_DIG_CTRL0_BASE + 0x5d) +#define WSA884X_VBAT_CAL_MSB (WSA884X_DIG_CTRL0_BASE + 0x5e) +#define WSA884X_VBAT_CAL_LSB (WSA884X_DIG_CTRL0_BASE + 0x5f) +#define WSA884X_UVLO_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x60) +#define WSA884X_BOP_DEGLITCH_CTL (WSA884X_DIG_CTRL0_BASE + 0x61) +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK 0x1e +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_SHIFT 1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK 0x1 +#define WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_SHIFT 0 +#define WSA884X_VBAT_ZONE_DETC_CTL (WSA884X_DIG_CTRL0_BASE + 0x64) +#define WSA884X_CPS_CTL (WSA884X_DIG_CTRL0_BASE + 0x68) +#define WSA884X_CDC_RX_CTL (WSA884X_DIG_CTRL0_BASE + 0x70) +#define WSA884X_CDC_SPK_DSM_A1_0 (WSA884X_DIG_CTRL0_BASE + 0x71) +#define WSA884X_CDC_SPK_DSM_A1_1 (WSA884X_DIG_CTRL0_BASE + 0x72) +#define WSA884X_CDC_SPK_DSM_A2_0 (WSA884X_DIG_CTRL0_BASE + 0x73) +#define WSA884X_CDC_SPK_DSM_A2_1 (WSA884X_DIG_CTRL0_BASE + 0x74) +#define WSA884X_CDC_SPK_DSM_A3_0 (WSA884X_DIG_CTRL0_BASE + 0x75) +#define WSA884X_CDC_SPK_DSM_A3_1 (WSA884X_DIG_CTRL0_BASE + 0x76) +#define WSA884X_CDC_SPK_DSM_A4_0 (WSA884X_DIG_CTRL0_BASE + 0x77) +#define WSA884X_CDC_SPK_DSM_A4_1 (WSA884X_DIG_CTRL0_BASE + 0x78) +#define WSA884X_CDC_SPK_DSM_A5_0 (WSA884X_DIG_CTRL0_BASE + 0x79) +#define WSA884X_CDC_SPK_DSM_A5_1 (WSA884X_DIG_CTRL0_BASE + 0x7a) +#define WSA884X_CDC_SPK_DSM_A6_0 (WSA884X_DIG_CTRL0_BASE + 0x7b) +#define WSA884X_CDC_SPK_DSM_A7_0 (WSA884X_DIG_CTRL0_BASE + 0x7c) +#define WSA884X_CDC_SPK_DSM_C_0 (WSA884X_DIG_CTRL0_BASE + 0x7d) +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C3_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_0_COEF_C2_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_1 (WSA884X_DIG_CTRL0_BASE + 0x7e) +#define WSA884X_CDC_SPK_DSM_C_2 (WSA884X_DIG_CTRL0_BASE + 0x7f) +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK 0xf0 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C7_SHIFT 4 +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_MASK 0x0f +#define WSA884X_CDC_SPK_DSM_C_2_COEF_C6_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_C_3 (WSA884X_DIG_CTRL0_BASE + 0x80) +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK 0x3f +#define WSA884X_CDC_SPK_DSM_C_3_COEF_C7_SHIFT 0 +#define WSA884X_CDC_SPK_DSM_R1 (WSA884X_DIG_CTRL0_BASE + 0x81) +#define WSA884X_CDC_SPK_DSM_R2 (WSA884X_DIG_CTRL0_BASE + 0x82) +#define WSA884X_CDC_SPK_DSM_R3 (WSA884X_DIG_CTRL0_BASE + 0x83) +#define WSA884X_CDC_SPK_DSM_R4 (WSA884X_DIG_CTRL0_BASE + 0x84) +#define WSA884X_CDC_SPK_DSM_R5 (WSA884X_DIG_CTRL0_BASE + 0x85) +#define WSA884X_CDC_SPK_DSM_R6 (WSA884X_DIG_CTRL0_BASE + 0x86) +#define WSA884X_CDC_SPK_DSM_R7 (WSA884X_DIG_CTRL0_BASE + 0x87) +#define WSA884X_CDC_SPK_GAIN_PDM_0 (WSA884X_DIG_CTRL0_BASE + 0x88) +#define WSA884X_CDC_SPK_GAIN_PDM_1 (WSA884X_DIG_CTRL0_BASE + 0x89) +#define WSA884X_CDC_SPK_GAIN_PDM_2 (WSA884X_DIG_CTRL0_BASE + 0x8a) +#define WSA884X_PDM_WD_CTL (WSA884X_DIG_CTRL0_BASE + 0x8b) +#define WSA884X_PDM_WD_CTL_HOLD_OFF_MASK 0x04 +#define WSA884X_PDM_WD_CTL_HOLD_OFF_SHIFT 2 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_MASK 0x02 +#define WSA884X_PDM_WD_CTL_TIME_OUT_SEL_SHIFT 1 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK 0x01 +#define WSA884X_PDM_WD_CTL_PDM_WD_EN_SHIFT 0 +#define WSA884X_DEM_BYPASS_DATA0 (WSA884X_DIG_CTRL0_BASE + 0x90) +#define WSA884X_DEM_BYPASS_DATA1 (WSA884X_DIG_CTRL0_BASE + 0x91) +#define WSA884X_DEM_BYPASS_DATA2 (WSA884X_DIG_CTRL0_BASE + 0x92) +#define WSA884X_DEM_BYPASS_DATA3 (WSA884X_DIG_CTRL0_BASE + 0x93) +#define WSA884X_DRE_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xb0) +#define WSA884X_DRE_CTL_0_PROG_DELAY_MASK 0xf0 +#define WSA884X_DRE_CTL_0_PROG_DELAY_SHIFT 4 +#define WSA884X_DRE_CTL_0_OFFSET_MASK 0x07 +#define WSA884X_DRE_CTL_0_OFFSET_SHIFT 0 +#define WSA884X_DRE_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xb1) +#define WSA884X_DRE_CTL_1_CSR_GAIN_MASK 0x3e +#define WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT 1 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK 0x01 +#define WSA884X_DRE_CTL_1_CSR_GAIN_EN_SHIFT 0 +#define WSA884X_DRE_IDLE_DET_CTL (WSA884X_DIG_CTRL0_BASE + 0xb2) +#define WSA884X_GAIN_RAMPING_CTL (WSA884X_DIG_CTRL0_BASE + 0xb8) +#define WSA884X_GAIN_RAMPING_MIN (WSA884X_DIG_CTRL0_BASE + 0xb9) +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK 0x1f +#define WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_SHIFT 0 +#define WSA884X_TAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc0) +#define WSA884X_TAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc1) +#define WSA884X_TAGC_FORCE_VAL (WSA884X_DIG_CTRL0_BASE + 0xc2) +#define WSA884X_VAGC_CTL (WSA884X_DIG_CTRL0_BASE + 0xc8) +#define WSA884X_VAGC_TIME (WSA884X_DIG_CTRL0_BASE + 0xc9) +#define WSA884X_VAGC_ATTN_LVL_1 (WSA884X_DIG_CTRL0_BASE + 0xca) +#define WSA884X_VAGC_ATTN_LVL_2 (WSA884X_DIG_CTRL0_BASE + 0xcb) +#define WSA884X_VAGC_ATTN_LVL_3 (WSA884X_DIG_CTRL0_BASE + 0xcc) +#define WSA884X_CLSH_CTL_0 (WSA884X_DIG_CTRL0_BASE + 0xd0) +#define WSA884X_CLSH_CTL_0_CSR_GAIN_EN_SHIFT 7 +#define WSA884X_CLSH_CTL_0_DLY_CODE_MASK 0x70 +#define WSA884X_CLSH_CTL_0_DLY_CODE_SHIFT 4 +#define WSA884X_CLSH_CTL_0_DLY_RST_SHIFT 3 +#define WSA884X_CLSH_CTL_0_DLY_EN_SHIFT 2 +#define WSA884X_CLSH_CTL_0_INPUT_EN_SHIFT 1 +#define WSA884X_CLSH_CTL_0_CLSH_EN_SHIFT 0 +#define WSA884X_CLSH_CTL_1 (WSA884X_DIG_CTRL0_BASE + 0xd1) +#define WSA884X_CLSH_V_HD_PA (WSA884X_DIG_CTRL0_BASE + 0xd2) +#define WSA884X_CLSH_V_PA_MIN (WSA884X_DIG_CTRL0_BASE + 0xd3) +#define WSA884X_CLSH_OVRD_VAL (WSA884X_DIG_CTRL0_BASE + 0xd4) +#define WSA884X_CLSH_HARD_MAX (WSA884X_DIG_CTRL0_BASE + 0xd5) +#define WSA884X_CLSH_SOFT_MAX (WSA884X_DIG_CTRL0_BASE + 0xd6) +#define WSA884X_CLSH_SIG_DP (WSA884X_DIG_CTRL0_BASE + 0xd7) +#define WSA884X_PBR_DELAY_CTL (WSA884X_DIG_CTRL0_BASE + 0xd8) +#define WSA884X_CLSH_SRL_MAX_PBR (WSA884X_DIG_CTRL0_BASE + 0xe0) +#define WSA884X_PBR_MAX_VOLTAGE 20 +#define WSA884X_PBR_MAX_CODE 255 +#define WSA884X_VTH_TO_REG(vth) \ + ((vth) != 0 ? (((vth) - 150) * WSA884X_PBR_MAX_CODE / (WSA884X_PBR_MAX_VOLTAGE * 100) + 1) : 0) +#define WSA884X_CLSH_VTH1 (WSA884X_DIG_CTRL0_BASE + 0xe1) +#define WSA884X_CLSH_VTH2 (WSA884X_DIG_CTRL0_BASE + 0xe2) +#define WSA884X_CLSH_VTH3 (WSA884X_DIG_CTRL0_BASE + 0xe3) +#define WSA884X_CLSH_VTH4 (WSA884X_DIG_CTRL0_BASE + 0xe4) +#define WSA884X_CLSH_VTH5 (WSA884X_DIG_CTRL0_BASE + 0xe5) +#define WSA884X_CLSH_VTH6 (WSA884X_DIG_CTRL0_BASE + 0xe6) +#define WSA884X_CLSH_VTH7 (WSA884X_DIG_CTRL0_BASE + 0xe7) +#define WSA884X_CLSH_VTH8 (WSA884X_DIG_CTRL0_BASE + 0xe8) +#define WSA884X_CLSH_VTH9 (WSA884X_DIG_CTRL0_BASE + 0xe9) +#define WSA884X_CLSH_VTH10 (WSA884X_DIG_CTRL0_BASE + 0xea) +#define WSA884X_CLSH_VTH11 (WSA884X_DIG_CTRL0_BASE + 0xeb) +#define WSA884X_CLSH_VTH12 (WSA884X_DIG_CTRL0_BASE + 0xec) +#define WSA884X_CLSH_VTH13 (WSA884X_DIG_CTRL0_BASE + 0xed) +#define WSA884X_CLSH_VTH14 (WSA884X_DIG_CTRL0_BASE + 0xee) +#define WSA884X_CLSH_VTH15 (WSA884X_DIG_CTRL0_BASE + 0xef) + +#define WSA884X_DIG_CTRL1_BASE (WSA884X_BASE + 0x0500) +#define WSA884X_DIG_CTRL1_PAGE (WSA884X_DIG_CTRL1_BASE + 0x00) +#define WSA884X_VPHX_SYS_EN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x01) +#define WSA884X_ANA_WO_CTL_0 (WSA884X_DIG_CTRL1_BASE + 0x04) +#define WSA884X_ANA_WO_CTL_0_MODE_SHIFT 0 +#define WSA884X_ANA_WO_CTL_0_VPHX_SYS_EN_MASK 0xc0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_DISABLE 0x0 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB 0xa +#define WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB 0x7 +#define WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK 0x3c +#define WSA884X_ANA_WO_CTL_0_PA_MIN_GAIN_BYP_MASK 0x02 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER 0x1 +#define WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK 0x01 +#define WSA884X_ANA_WO_CTL_1 (WSA884X_DIG_CTRL1_BASE + 0x05) +#define WSA884X_PIN_CTL (WSA884X_DIG_CTRL1_BASE + 0x10) +#define WSA884X_PIN_CTL_OE (WSA884X_DIG_CTRL1_BASE + 0x11) +#define WSA884X_PIN_WDATA_IOPAD (WSA884X_DIG_CTRL1_BASE + 0x12) +#define WSA884X_PIN_STATUS (WSA884X_DIG_CTRL1_BASE + 0x13) +#define WSA884X_I2C_SLAVE_CTL (WSA884X_DIG_CTRL1_BASE + 0x14) +#define WSA884X_SPMI_PAD_CTL0 (WSA884X_DIG_CTRL1_BASE + 0x15) +#define WSA884X_SPMI_PAD_CTL1 (WSA884X_DIG_CTRL1_BASE + 0x16) +#define WSA884X_SPMI_PAD_CTL2 (WSA884X_DIG_CTRL1_BASE + 0x17) +#define WSA884X_MEM_CTL (WSA884X_DIG_CTRL1_BASE + 0x18) +#define WSA884X_SWR_HM_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x19) +#define WSA884X_SWR_HM_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x1a) +#define WSA884X_OTP_CTRL0 (WSA884X_DIG_CTRL1_BASE + 0x30) +#define WSA884X_OTP_CTRL1 (WSA884X_DIG_CTRL1_BASE + 0x31) +#define WSA884X_OTP_CTRL2 (WSA884X_DIG_CTRL1_BASE + 0x32) +#define WSA884X_OTP_STAT (WSA884X_DIG_CTRL1_BASE + 0x33) +#define WSA884X_OTP_PRG_TCSP0 (WSA884X_DIG_CTRL1_BASE + 0x34) +#define WSA884X_OTP_PRG_TCSP1 (WSA884X_DIG_CTRL1_BASE + 0x35) +#define WSA884X_OTP_PRG_TPPS (WSA884X_DIG_CTRL1_BASE + 0x36) +#define WSA884X_OTP_PRG_TVPS (WSA884X_DIG_CTRL1_BASE + 0x37) +#define WSA884X_OTP_PRG_TVPH (WSA884X_DIG_CTRL1_BASE + 0x38) +#define WSA884X_OTP_PRG_TPPR0 (WSA884X_DIG_CTRL1_BASE + 0x39) +#define WSA884X_OTP_PRG_TPPR1 (WSA884X_DIG_CTRL1_BASE + 0x3a) +#define WSA884X_OTP_PRG_TPPH (WSA884X_DIG_CTRL1_BASE + 0x3b) +#define WSA884X_OTP_PRG_END (WSA884X_DIG_CTRL1_BASE + 0x3c) +#define WSA884X_WAVG_PLAY (WSA884X_DIG_CTRL1_BASE + 0x40) +#define WSA884X_WAVG_CTL (WSA884X_DIG_CTRL1_BASE + 0x41) +#define WSA884X_WAVG_LRA_PER_0 (WSA884X_DIG_CTRL1_BASE + 0x43) +#define WSA884X_WAVG_LRA_PER_1 (WSA884X_DIG_CTRL1_BASE + 0x44) +#define WSA884X_WAVG_DELTA_THETA_0 (WSA884X_DIG_CTRL1_BASE + 0x45) +#define WSA884X_WAVG_DELTA_THETA_1 (WSA884X_DIG_CTRL1_BASE + 0x46) +#define WSA884X_WAVG_DIRECT_AMP_0 (WSA884X_DIG_CTRL1_BASE + 0x47) +#define WSA884X_WAVG_DIRECT_AMP_1 (WSA884X_DIG_CTRL1_BASE + 0x48) +#define WSA884X_WAVG_PTRN_AMP0_0 (WSA884X_DIG_CTRL1_BASE + 0x49) +#define WSA884X_WAVG_PTRN_AMP0_1 (WSA884X_DIG_CTRL1_BASE + 0x4a) +#define WSA884X_WAVG_PTRN_AMP1_0 (WSA884X_DIG_CTRL1_BASE + 0x4b) +#define WSA884X_WAVG_PTRN_AMP1_1 (WSA884X_DIG_CTRL1_BASE + 0x4c) +#define WSA884X_WAVG_PTRN_AMP2_0 (WSA884X_DIG_CTRL1_BASE + 0x4d) +#define WSA884X_WAVG_PTRN_AMP2_1 (WSA884X_DIG_CTRL1_BASE + 0x4e) +#define WSA884X_WAVG_PTRN_AMP3_0 (WSA884X_DIG_CTRL1_BASE + 0x4f) +#define WSA884X_WAVG_PTRN_AMP3_1 (WSA884X_DIG_CTRL1_BASE + 0x50) +#define WSA884X_WAVG_PTRN_AMP4_0 (WSA884X_DIG_CTRL1_BASE + 0x51) +#define WSA884X_WAVG_PTRN_AMP4_1 (WSA884X_DIG_CTRL1_BASE + 0x52) +#define WSA884X_WAVG_PTRN_AMP5_0 (WSA884X_DIG_CTRL1_BASE + 0x53) +#define WSA884X_WAVG_PTRN_AMP5_1 (WSA884X_DIG_CTRL1_BASE + 0x54) +#define WSA884X_WAVG_PTRN_AMP6_0 (WSA884X_DIG_CTRL1_BASE + 0x55) +#define WSA884X_WAVG_PTRN_AMP6_1 (WSA884X_DIG_CTRL1_BASE + 0x56) +#define WSA884X_WAVG_PTRN_AMP7_0 (WSA884X_DIG_CTRL1_BASE + 0x57) +#define WSA884X_WAVG_PTRN_AMP7_1 (WSA884X_DIG_CTRL1_BASE + 0x58) +#define WSA884X_WAVG_PER_0_1 (WSA884X_DIG_CTRL1_BASE + 0x59) +#define WSA884X_WAVG_PER_2_3 (WSA884X_DIG_CTRL1_BASE + 0x5a) +#define WSA884X_WAVG_PER_4_5 (WSA884X_DIG_CTRL1_BASE + 0x5b) +#define WSA884X_WAVG_PER_6_7 (WSA884X_DIG_CTRL1_BASE + 0x5c) +#define WSA884X_WAVG_STA (WSA884X_DIG_CTRL1_BASE + 0x5d) +#define WSA884X_INTR_MODE (WSA884X_DIG_CTRL1_BASE + 0x80) +#define WSA884X_INTR_MASK0 (WSA884X_DIG_CTRL1_BASE + 0x81) +#define WSA884X_INTR_MASK1 (WSA884X_DIG_CTRL1_BASE + 0x82) +#define WSA884X_INTR_STATUS0 (WSA884X_DIG_CTRL1_BASE + 0x83) +#define WSA884X_INTR_STATUS1 (WSA884X_DIG_CTRL1_BASE + 0x84) +#define WSA884X_INTR_CLEAR0 (WSA884X_DIG_CTRL1_BASE + 0x85) +#define WSA884X_INTR_CLEAR1 (WSA884X_DIG_CTRL1_BASE + 0x86) +#define WSA884X_INTR_LEVEL0 (WSA884X_DIG_CTRL1_BASE + 0x87) +#define WSA884X_INTR_LEVEL1 (WSA884X_DIG_CTRL1_BASE + 0x88) +#define WSA884X_INTR_SET0 (WSA884X_DIG_CTRL1_BASE + 0x89) +#define WSA884X_INTR_SET1 (WSA884X_DIG_CTRL1_BASE + 0x8a) +#define WSA884X_INTR_TEST0 (WSA884X_DIG_CTRL1_BASE + 0x8b) +#define WSA884X_INTR_TEST1 (WSA884X_DIG_CTRL1_BASE + 0x8c) +#define WSA884X_PDM_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc0) +#define WSA884X_ATE_TEST_MODE (WSA884X_DIG_CTRL1_BASE + 0xc1) +#define WSA884X_PA_FSM_DBG (WSA884X_DIG_CTRL1_BASE + 0xc2) +#define WSA884X_DIG_DEBUG_MODE (WSA884X_DIG_CTRL1_BASE + 0xc3) +#define WSA884X_DIG_DEBUG_SEL (WSA884X_DIG_CTRL1_BASE + 0xc4) +#define WSA884X_DIG_DEBUG_EN (WSA884X_DIG_CTRL1_BASE + 0xc5) +#define WSA884X_TADC_DETECT_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xc9) +#define WSA884X_TADC_DEBUG_MSB (WSA884X_DIG_CTRL1_BASE + 0xca) +#define WSA884X_TADC_DEBUG_LSB (WSA884X_DIG_CTRL1_BASE + 0xcb) +#define WSA884X_SAMPLE_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcc) +#define WSA884X_SWR_EDGE_SEL (WSA884X_DIG_CTRL1_BASE + 0xcd) +#define WSA884X_TEST_MODE_CTL (WSA884X_DIG_CTRL1_BASE + 0xce) +#define WSA884X_IOPAD_CTL (WSA884X_DIG_CTRL1_BASE + 0xcf) +#define WSA884X_ANA_CSR_DBG_ADD (WSA884X_DIG_CTRL1_BASE + 0xd0) +#define WSA884X_ANA_CSR_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd1) +#define WSA884X_CLK_DBG_CTL (WSA884X_DIG_CTRL1_BASE + 0xd2) +#define WSA884X_SPARE_R (WSA884X_DIG_CTRL1_BASE + 0xf0) +#define WSA884X_SPARE_0 (WSA884X_DIG_CTRL1_BASE + 0xf1) +#define WSA884X_SPARE_1 (WSA884X_DIG_CTRL1_BASE + 0xf2) +#define WSA884X_SPARE_2 (WSA884X_DIG_CTRL1_BASE + 0xf3) +#define WSA884X_SCODE (WSA884X_DIG_CTRL1_BASE + 0xff) + +#define WSA884X_DIG_TRIM_BASE (WSA884X_BASE + 0x0800) +#define WSA884X_DIG_TRIM_PAGE (WSA884X_DIG_TRIM_BASE + 0x00) +#define WSA884X_OTP_REG_0 (WSA884X_DIG_TRIM_BASE + 0x80) +#define WSA884X_OTP_ID_WSA8840 0x0 +#define WSA884X_OTP_ID_WSA8845 0x5 +#define WSA884X_OTP_ID_WSA8845H 0xc +#define WSA884X_OTP_REG_0_ID_MASK 0x0f +#define WSA884X_OTP_REG_1 (WSA884X_DIG_TRIM_BASE + 0x81) +#define WSA884X_OTP_REG_2 (WSA884X_DIG_TRIM_BASE + 0x82) +#define WSA884X_OTP_REG_3 (WSA884X_DIG_TRIM_BASE + 0x83) +#define WSA884X_OTP_REG_4 (WSA884X_DIG_TRIM_BASE + 0x84) +#define WSA884X_OTP_REG_5 (WSA884X_DIG_TRIM_BASE + 0x85) +#define WSA884X_OTP_REG_6 (WSA884X_DIG_TRIM_BASE + 0x86) +#define WSA884X_OTP_REG_7 (WSA884X_DIG_TRIM_BASE + 0x87) +#define WSA884X_OTP_REG_8 (WSA884X_DIG_TRIM_BASE + 0x88) +#define WSA884X_OTP_REG_9 (WSA884X_DIG_TRIM_BASE + 0x89) +#define WSA884X_OTP_REG_10 (WSA884X_DIG_TRIM_BASE + 0x8a) +#define WSA884X_OTP_REG_11 (WSA884X_DIG_TRIM_BASE + 0x8b) +#define WSA884X_OTP_REG_12 (WSA884X_DIG_TRIM_BASE + 0x8c) +#define WSA884X_OTP_REG_13 (WSA884X_DIG_TRIM_BASE + 0x8d) +#define WSA884X_OTP_REG_14 (WSA884X_DIG_TRIM_BASE + 0x8e) +#define WSA884X_OTP_REG_15 (WSA884X_DIG_TRIM_BASE + 0x8f) +#define WSA884X_OTP_REG_16 (WSA884X_DIG_TRIM_BASE + 0x90) +#define WSA884X_OTP_REG_17 (WSA884X_DIG_TRIM_BASE + 0x91) +#define WSA884X_OTP_REG_18 (WSA884X_DIG_TRIM_BASE + 0x92) +#define WSA884X_OTP_REG_19 (WSA884X_DIG_TRIM_BASE + 0x93) +#define WSA884X_OTP_REG_20 (WSA884X_DIG_TRIM_BASE + 0x94) +#define WSA884X_OTP_REG_21 (WSA884X_DIG_TRIM_BASE + 0x95) +#define WSA884X_OTP_REG_22 (WSA884X_DIG_TRIM_BASE + 0x96) +#define WSA884X_OTP_REG_23 (WSA884X_DIG_TRIM_BASE + 0x97) +#define WSA884X_OTP_REG_24 (WSA884X_DIG_TRIM_BASE + 0x98) +#define WSA884X_OTP_REG_25 (WSA884X_DIG_TRIM_BASE + 0x99) +#define WSA884X_OTP_REG_26 (WSA884X_DIG_TRIM_BASE + 0x9a) +#define WSA884X_OTP_REG_27 (WSA884X_DIG_TRIM_BASE + 0x9b) +#define WSA884X_OTP_REG_28 (WSA884X_DIG_TRIM_BASE + 0x9c) +#define WSA884X_OTP_REG_29 (WSA884X_DIG_TRIM_BASE + 0x9d) +#define WSA884X_OTP_REG_30 (WSA884X_DIG_TRIM_BASE + 0x9e) +#define WSA884X_OTP_REG_31 (WSA884X_DIG_TRIM_BASE + 0x9f) +#define WSA884X_OTP_REG_32 (WSA884X_DIG_TRIM_BASE + 0xa0) +#define WSA884X_OTP_REG_33 (WSA884X_DIG_TRIM_BASE + 0xa1) +#define WSA884X_OTP_REG_34 (WSA884X_DIG_TRIM_BASE + 0xa2) +#define WSA884X_OTP_REG_35 (WSA884X_DIG_TRIM_BASE + 0xa3) +#define WSA884X_OTP_REG_36 (WSA884X_DIG_TRIM_BASE + 0xa4) +#define WSA884X_OTP_REG_37 (WSA884X_DIG_TRIM_BASE + 0xa5) +#define WSA884X_OTP_REG_38 (WSA884X_DIG_TRIM_BASE + 0xa6) +#define WSA884X_OTP_REG_38_RESERVER_MASK 0xf0 +#define WSA884X_OTP_REG_38_RESERVER_SHIFT 4 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_MASK 0x08 +#define WSA884X_OTP_REG_38_BST_CFG_SEL_SHIFT 3 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_MASK 0x07 +#define WSA884X_OTP_REG_38_BOOST_ILIM_TUNE_SHIFT 0 +#define WSA884X_OTP_REG_39 (WSA884X_DIG_TRIM_BASE + 0xa7) +#define WSA884X_OTP_REG_40 (WSA884X_DIG_TRIM_BASE + 0xa8) +#define WSA884X_OTP_REG_40_SPARE_TYPE2_MASK 0xc0 +#define WSA884X_OTP_REG_40_SPARE_TYPE2_SHIFT 6 +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK 0x3c +#define WSA884X_OTP_REG_40_ISENSE_RESCAL_SHIFT 2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_MASK 0x2 +#define WSA884X_OTP_REG_40_ATE_BOOST_RDSON_TEST_SHIFT 1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_MASK 0x1 +#define WSA884X_OTP_REG_40_ATE_CLASSD_RDSON_TEST_SHIFT 0 +#define WSA884X_OTP_REG_41 (WSA884X_DIG_TRIM_BASE + 0xa9) +#define WSA884X_OTP_REG_63 (WSA884X_DIG_TRIM_BASE + 0xbf) + +#define WSA884X_DIG_EMEM_BASE (WSA884X_BASE + 0x08C0) +#define WSA884X_EMEM_0 (WSA884X_DIG_EMEM_BASE + 0x00) +#define WSA884X_EMEM_1 (WSA884X_DIG_EMEM_BASE + 0x01) +#define WSA884X_EMEM_2 (WSA884X_DIG_EMEM_BASE + 0x02) +#define WSA884X_EMEM_3 (WSA884X_DIG_EMEM_BASE + 0x03) +#define WSA884X_EMEM_4 (WSA884X_DIG_EMEM_BASE + 0x04) +#define WSA884X_EMEM_5 (WSA884X_DIG_EMEM_BASE + 0x05) +#define WSA884X_EMEM_6 (WSA884X_DIG_EMEM_BASE + 0x06) +#define WSA884X_EMEM_7 (WSA884X_DIG_EMEM_BASE + 0x07) +#define WSA884X_EMEM_8 (WSA884X_DIG_EMEM_BASE + 0x08) +#define WSA884X_EMEM_9 (WSA884X_DIG_EMEM_BASE + 0x09) +#define WSA884X_EMEM_10 (WSA884X_DIG_EMEM_BASE + 0x0a) +#define WSA884X_EMEM_11 (WSA884X_DIG_EMEM_BASE + 0x0b) +#define WSA884X_EMEM_12 (WSA884X_DIG_EMEM_BASE + 0x0c) +#define WSA884X_EMEM_13 (WSA884X_DIG_EMEM_BASE + 0x0d) +#define WSA884X_EMEM_14 (WSA884X_DIG_EMEM_BASE + 0x0e) +#define WSA884X_EMEM_15 (WSA884X_DIG_EMEM_BASE + 0x0f) +#define WSA884X_EMEM_16 (WSA884X_DIG_EMEM_BASE + 0x10) +#define WSA884X_EMEM_17 (WSA884X_DIG_EMEM_BASE + 0x11) +#define WSA884X_EMEM_18 (WSA884X_DIG_EMEM_BASE + 0x12) +#define WSA884X_EMEM_19 (WSA884X_DIG_EMEM_BASE + 0x13) +#define WSA884X_EMEM_20 (WSA884X_DIG_EMEM_BASE + 0x14) +#define WSA884X_EMEM_21 (WSA884X_DIG_EMEM_BASE + 0x15) +#define WSA884X_EMEM_22 (WSA884X_DIG_EMEM_BASE + 0x16) +#define WSA884X_EMEM_23 (WSA884X_DIG_EMEM_BASE + 0x17) +#define WSA884X_EMEM_24 (WSA884X_DIG_EMEM_BASE + 0x18) +#define WSA884X_EMEM_25 (WSA884X_DIG_EMEM_BASE + 0x19) +#define WSA884X_EMEM_26 (WSA884X_DIG_EMEM_BASE + 0x1a) +#define WSA884X_EMEM_27 (WSA884X_DIG_EMEM_BASE + 0x1b) +#define WSA884X_EMEM_28 (WSA884X_DIG_EMEM_BASE + 0x1c) +#define WSA884X_EMEM_29 (WSA884X_DIG_EMEM_BASE + 0x1d) +#define WSA884X_EMEM_30 (WSA884X_DIG_EMEM_BASE + 0x1e) +#define WSA884X_EMEM_31 (WSA884X_DIG_EMEM_BASE + 0x1f) +#define WSA884X_EMEM_32 (WSA884X_DIG_EMEM_BASE + 0x20) +#define WSA884X_EMEM_33 (WSA884X_DIG_EMEM_BASE + 0x21) +#define WSA884X_EMEM_34 (WSA884X_DIG_EMEM_BASE + 0x22) +#define WSA884X_EMEM_35 (WSA884X_DIG_EMEM_BASE + 0x23) +#define WSA884X_EMEM_36 (WSA884X_DIG_EMEM_BASE + 0x24) +#define WSA884X_EMEM_37 (WSA884X_DIG_EMEM_BASE + 0x25) +#define WSA884X_EMEM_38 (WSA884X_DIG_EMEM_BASE + 0x26) +#define WSA884X_EMEM_39 (WSA884X_DIG_EMEM_BASE + 0x27) +#define WSA884X_EMEM_40 (WSA884X_DIG_EMEM_BASE + 0x28) +#define WSA884X_EMEM_41 (WSA884X_DIG_EMEM_BASE + 0x29) +#define WSA884X_EMEM_42 (WSA884X_DIG_EMEM_BASE + 0x2a) +#define WSA884X_EMEM_43 (WSA884X_DIG_EMEM_BASE + 0x2b) +#define WSA884X_EMEM_44 (WSA884X_DIG_EMEM_BASE + 0x2c) +#define WSA884X_EMEM_45 (WSA884X_DIG_EMEM_BASE + 0x2d) +#define WSA884X_EMEM_46 (WSA884X_DIG_EMEM_BASE + 0x2e) +#define WSA884X_EMEM_47 (WSA884X_DIG_EMEM_BASE + 0x2f) +#define WSA884X_EMEM_48 (WSA884X_DIG_EMEM_BASE + 0x30) +#define WSA884X_EMEM_49 (WSA884X_DIG_EMEM_BASE + 0x31) +#define WSA884X_EMEM_50 (WSA884X_DIG_EMEM_BASE + 0x32) +#define WSA884X_EMEM_51 (WSA884X_DIG_EMEM_BASE + 0x33) +#define WSA884X_EMEM_52 (WSA884X_DIG_EMEM_BASE + 0x34) +#define WSA884X_EMEM_53 (WSA884X_DIG_EMEM_BASE + 0x35) +#define WSA884X_EMEM_54 (WSA884X_DIG_EMEM_BASE + 0x36) +#define WSA884X_EMEM_55 (WSA884X_DIG_EMEM_BASE + 0x37) +#define WSA884X_EMEM_56 (WSA884X_DIG_EMEM_BASE + 0x38) +#define WSA884X_EMEM_57 (WSA884X_DIG_EMEM_BASE + 0x39) +#define WSA884X_EMEM_58 (WSA884X_DIG_EMEM_BASE + 0x3a) +#define WSA884X_EMEM_59 (WSA884X_DIG_EMEM_BASE + 0x3b) +#define WSA884X_EMEM_60 (WSA884X_DIG_EMEM_BASE + 0x3c) +#define WSA884X_EMEM_61 (WSA884X_DIG_EMEM_BASE + 0x3d) +#define WSA884X_EMEM_62 (WSA884X_DIG_EMEM_BASE + 0x3e) +#define WSA884X_EMEM_63 (WSA884X_DIG_EMEM_BASE + 0x3f) + +#define WSA884X_NUM_REGISTERS (WSA884X_EMEM_63 + 1) +#define WSA884X_MAX_REGISTER (WSA884X_NUM_REGISTERS - 1) + +#define WSA884X_SUPPLIES_NUM 2 +#define WSA884X_MAX_SWR_PORTS 6 +#define WSA884X_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\ + SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000 |\ + SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000 |\ + SNDRV_PCM_RATE_384000) +/* Fractional Rates */ +#define WSA884X_FRAC_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 |\ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_352800) + +#define WSA884X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE |\ + SNDRV_PCM_FMTBIT_S24_LE |\ + SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE) + +struct wsa884x_priv { + struct regmap *regmap; + struct device *dev; + struct regulator_bulk_data supplies[WSA884X_SUPPLIES_NUM]; + struct sdw_slave *slave; + struct sdw_stream_config sconfig; + struct sdw_stream_runtime *sruntime; + struct sdw_port_config port_config[WSA884X_MAX_SWR_PORTS]; + struct gpio_desc *sd_n; + bool port_prepared[WSA884X_MAX_SWR_PORTS]; + bool port_enable[WSA884X_MAX_SWR_PORTS]; + unsigned int variant; + int active_ports; + int dev_mode; + bool hw_init; +}; + +enum { + COMP_OFFSET0, + COMP_OFFSET1, + COMP_OFFSET2, + COMP_OFFSET3, + COMP_OFFSET4, +}; + +enum wsa884x_gain { + G_21_DB = 0, + G_19P5_DB, + G_18_DB, + G_16P5_DB, + G_15_DB, + G_13P5_DB, + G_12_DB, + G_10P5_DB, + G_9_DB, + G_7P5_DB, + G_6_DB, + G_4P5_DB, + G_3_DB, + G_1P5_DB, + G_0_DB, + G_M1P5_DB, + G_M3_DB, + G_M4P5_DB, + G_M6_DB, + G_MAX_DB, +}; + +enum wsa884x_isense { + ISENSE_6_DB = 0, + ISENSE_12_DB, + ISENSE_15_DB, + ISENSE_18_DB, +}; + +enum wsa884x_vsense { + VSENSE_M12_DB = 0, + VSENSE_M15_DB, + VSENSE_M18_DB, + VSENSE_M21_DB, + VSENSE_M24_DB, +}; + +enum wsa884x_port_ids { + WSA884X_PORT_DAC, + WSA884X_PORT_COMP, + WSA884X_PORT_BOOST, + WSA884X_PORT_PBR, + WSA884X_PORT_VISENSE, + WSA884X_PORT_CPS, +}; + +static const char * const wsa884x_supply_name[] = { + "vdd-io", + "vdd-1p8", +}; + +static const char * const wsa884x_dev_mode_text[] = { + "Speaker", "Receiver" +}; + +enum wsa884x_mode { + WSA884X_SPEAKER, + WSA884X_RECEIVER, +}; + +static const struct soc_enum wsa884x_dev_mode_enum = + SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(wsa884x_dev_mode_text), wsa884x_dev_mode_text); + +static struct sdw_dpn_prop wsa884x_sink_dpn_prop[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_COMP + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_BOOST + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_PBR + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + }, { + .num = WSA884X_PORT_CPS + 1, + .type = SDW_DPN_SIMPLE, + .min_ch = 1, + .max_ch = 1, + .simple_ch_prep_sm = true, + .read_only_wordlength = true, + } +}; + +static const struct sdw_port_config wsa884x_pconfig[WSA884X_MAX_SWR_PORTS] = { + { + .num = WSA884X_PORT_DAC + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_COMP + 1, + .ch_mask = 0xf, + }, { + .num = WSA884X_PORT_BOOST + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_PBR + 1, + .ch_mask = 0x1, + }, { + .num = WSA884X_PORT_VISENSE + 1, + .ch_mask = 0x3, + }, { + .num = WSA884X_PORT_CPS + 1, + .ch_mask = 0x3, + }, +}; + +static struct reg_default wsa884x_defaults[] = { + { WSA884X_BG_CTRL, 0xa5 }, + { WSA884X_ADC_CTRL, 0x00 }, + { WSA884X_BOP1_PROG, 0x22 }, + { WSA884X_BOP2_PROG, 0x44 }, + { WSA884X_UVLO_PROG, 0x99 }, + { WSA884X_UVLO_PROG1, 0x70 }, + { WSA884X_SPARE_CTRL_0, 0x00 }, + { WSA884X_SPARE_CTRL_1, 0x00 }, + { WSA884X_SPARE_CTRL_2, 0x00 }, + { WSA884X_SPARE_CTRL_3, 0x00 }, + { WSA884X_REF_CTRL, 0xd2 }, + { WSA884X_BG_TEST_CTL, 0x06 }, + { WSA884X_BG_BIAS, 0xd7 }, + { WSA884X_ADC_PROG, 0x08 }, + { WSA884X_ADC_IREF_CTL, 0x57 }, + { WSA884X_ADC_ISENS_CTL, 0x47 }, + { WSA884X_ADC_CLK_CTL, 0x87 }, + { WSA884X_ADC_TEST_CTL, 0x00 }, + { WSA884X_ADC_BIAS, 0x51 }, + { WSA884X_VBAT_SNS, 0xa0 }, + { WSA884X_BOP_ATEST_SEL, 0x00 }, + { WSA884X_MISC0, 0x04 }, + { WSA884X_MISC1, 0x75 }, + { WSA884X_MISC2, 0x00 }, + { WSA884X_MISC3, 0x10 }, + { WSA884X_SPARE_TSBG_0, 0x00 }, + { WSA884X_SPARE_TUNE_0, 0x00 }, + { WSA884X_SPARE_TUNE_1, 0x00 }, + { WSA884X_VSENSE1, 0xe7 }, + { WSA884X_ISENSE2, 0x27 }, + { WSA884X_SPARE_CTL_1, 0x00 }, + { WSA884X_SPARE_CTL_2, 0x00 }, + { WSA884X_SPARE_CTL_3, 0x00 }, + { WSA884X_SPARE_CTL_4, 0x00 }, + { WSA884X_EN, 0x10 }, + { WSA884X_OVERRIDE1, 0x00 }, + { WSA884X_OVERRIDE2, 0x08 }, + { WSA884X_ISENSE1, 0xd4 }, + { WSA884X_ISENSE_CAL, 0x00 }, + { WSA884X_MISC, 0x00 }, + { WSA884X_ADC_0, 0x00 }, + { WSA884X_ADC_1, 0x00 }, + { WSA884X_ADC_2, 0x40 }, + { WSA884X_ADC_3, 0x80 }, + { WSA884X_ADC_4, 0x25 }, + { WSA884X_ADC_5, 0x24 }, + { WSA884X_ADC_6, 0x0a }, + { WSA884X_ADC_7, 0x81 }, + { WSA884X_IVSENSE_SPARE_TUNE_1, 0x00 }, + { WSA884X_SPARE_TUNE_2, 0x00 }, + { WSA884X_SPARE_TUNE_3, 0x00 }, + { WSA884X_SPARE_TUNE_4, 0x00 }, + { WSA884X_TOP_CTRL1, 0xd3 }, + { WSA884X_CLIP_DET_CTRL1, 0x7e }, + { WSA884X_CLIP_DET_CTRL2, 0x4c }, + { WSA884X_DAC_CTRL1, 0xa4 }, + { WSA884X_DAC_VCM_CTRL_REG1, 0x02 }, + { WSA884X_DAC_VCM_CTRL_REG2, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG3, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG4, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG5, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG6, 0x00 }, + { WSA884X_PWM_CLK_CTL, 0x20 }, + { WSA884X_DRV_LF_LDO_SEL, 0xaa }, + { WSA884X_OCP_CTL, 0xc6 }, + { WSA884X_PDRV_HS_CTL, 0x52 }, + { WSA884X_PDRV_LS_CTL, 0x4a }, + { WSA884X_SPK_TOP_SPARE_CTL_1, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_2, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_3, 0x00 }, + { WSA884X_SPK_TOP_SPARE_CTL_4, 0x00 }, + { WSA884X_SPARE_CTL_5, 0x00 }, + { WSA884X_DAC_EN_DEBUG_REG, 0x00 }, + { WSA884X_DAC_OPAMP_BIAS1_REG, 0x48 }, + { WSA884X_DAC_OPAMP_BIAS2_REG, 0x48 }, + { WSA884X_DAC_TUNE1, 0x02 }, + { WSA884X_DAC_VOLTAGE_CTRL_REG, 0x05 }, + { WSA884X_ATEST1_REG, 0x00 }, + { WSA884X_ATEST2_REG, 0x00 }, + { WSA884X_TOP_BIAS_REG1, 0x6a }, + { WSA884X_TOP_BIAS_REG2, 0x65 }, + { WSA884X_TOP_BIAS_REG3, 0x55 }, + { WSA884X_TOP_BIAS_REG4, 0xa9 }, + { WSA884X_PWRSTG_DBG2, 0x21 }, + { WSA884X_DRV_LF_BLK_EN, 0x0f }, + { WSA884X_DRV_LF_EN, 0x0a }, + { WSA884X_DRV_LF_MASK_DCC_CTL, 0x08 }, + { WSA884X_DRV_LF_MISC_CTL1, 0x30 }, + { WSA884X_DRV_LF_REG_GAIN, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL, 0x00 }, + { WSA884X_DRV_OS_CAL_CTL1, 0x90 }, + { WSA884X_PWRSTG_DBG, 0x08 }, + { WSA884X_BBM_CTL, 0x92 }, + { WSA884X_TOP_MISC1, 0x00 }, + { WSA884X_DAC_VCM_CTRL_REG7, 0x00 }, + { WSA884X_TOP_BIAS_REG5, 0x15 }, + { WSA884X_DRV_LF_MISC_CTL2, 0x00 }, + { WSA884X_STB_CTRL1, 0x42 }, + { WSA884X_CURRENT_LIMIT, 0x54 }, + { WSA884X_BYP_CTRL1, 0x01 }, + { WSA884X_SPARE_CTL_0, 0x00 }, + { WSA884X_BOOST_SPARE_CTL_1, 0x00 }, + { WSA884X_IBIAS1, 0x00 }, + { WSA884X_IBIAS2, 0x00 }, + { WSA884X_IBIAS3, 0x00 }, + { WSA884X_EN_CTRL, 0x42 }, + { WSA884X_STB_CTRL2, 0x03 }, + { WSA884X_STB_CTRL3, 0x3c }, + { WSA884X_STB_CTRL4, 0x30 }, + { WSA884X_BYP_CTRL2, 0x97 }, + { WSA884X_BYP_CTRL3, 0x11 }, + { WSA884X_ZX_CTRL1, 0xf0 }, + { WSA884X_ZX_CTRL2, 0x04 }, + { WSA884X_BLEEDER_CTRL, 0x04 }, + { WSA884X_BOOST_MISC, 0x62 }, + { WSA884X_PWRSTAGE_CTRL1, 0x00 }, + { WSA884X_PWRSTAGE_CTRL2, 0x31 }, + { WSA884X_PWRSTAGE_CTRL3, 0x81 }, + { WSA884X_PWRSTAGE_CTRL4, 0x5f }, + { WSA884X_MAXD_REG1, 0x00 }, + { WSA884X_MAXD_REG2, 0x5b }, + { WSA884X_ILIM_CTRL1, 0xe2 }, + { WSA884X_ILIM_CTRL2, 0x90 }, + { WSA884X_TEST_CTRL1, 0x00 }, + { WSA884X_TEST_CTRL2, 0x00 }, + { WSA884X_SPARE1, 0x00 }, + { WSA884X_BOOT_CAP_CHECK, 0x01 }, + { WSA884X_PON_CTL_0, 0x12 }, + { WSA884X_PWRSAV_CTL, 0xaa }, + { WSA884X_PON_LDOL_SPARE_CTL_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_CTL_3, 0x00 }, + { WSA884X_PON_CLT_1, 0xe1 }, + { WSA884X_PON_CTL_2, 0x00 }, + { WSA884X_PON_CTL_3, 0x70 }, + { WSA884X_CKWD_CTL_0, 0x14 }, + { WSA884X_CKWD_CTL_1, 0x3b }, + { WSA884X_CKWD_CTL_2, 0x00 }, + { WSA884X_CKSK_CTL_0, 0x00 }, + { WSA884X_PADSW_CTL_0, 0x00 }, + { WSA884X_TEST_0, 0x00 }, + { WSA884X_TEST_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_0, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_1, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_2, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_3, 0x00 }, + { WSA884X_PON_LDOL_SPARE_TUNE_4, 0x00 }, + { WSA884X_DIG_CTRL0_PAGE, 0x00 }, + { WSA884X_CDC_RST_CTL, 0x01 }, + { WSA884X_SWR_RESET_EN, 0x00 }, + { WSA884X_TOP_CLK_CFG, 0x00 }, + { WSA884X_SWR_CLK_RATE, 0x00 }, + { WSA884X_CDC_PATH_MODE, 0x00 }, + { WSA884X_CDC_CLK_CTL, 0x1f }, + { WSA884X_PA_FSM_EN, 0x00 }, + { WSA884X_PA_FSM_CTL0, 0x00 }, + { WSA884X_PA_FSM_CTL1, 0xfe }, + { WSA884X_PA_FSM_TIMER0, 0x80 }, + { WSA884X_PA_FSM_TIMER1, 0x80 }, + { WSA884X_PA_FSM_ERR_CTL, 0x00 }, + { WSA884X_PA_FSM_MSK0, 0x00 }, + { WSA884X_PA_FSM_MSK1, 0x00 }, + { WSA884X_PA_FSM_BYP_CTL, 0x00 }, + { WSA884X_PA_FSM_BYP0, 0x00 }, + { WSA884X_PA_FSM_BYP1, 0x00 }, + { WSA884X_TADC_VALUE_CTL, 0x03 }, + { WSA884X_TEMP_DETECT_CTL, 0x01 }, + { WSA884X_TEMP_CONFIG0, 0x00 }, + { WSA884X_TEMP_CONFIG1, 0x00 }, + { WSA884X_VBAT_THRM_FLT_CTL, 0x7f }, + { WSA884X_VBAT_CAL_CTL, 0x01 }, + { WSA884X_UVLO_DEGLITCH_CTL, 0x05 }, + { WSA884X_BOP_DEGLITCH_CTL, 0x05 }, + { WSA884X_VBAT_ZONE_DETC_CTL, 0x31 }, + { WSA884X_CPS_CTL, 0x00 }, + { WSA884X_CDC_RX_CTL, 0xfe }, + { WSA884X_CDC_SPK_DSM_A1_0, 0x00 }, + { WSA884X_CDC_SPK_DSM_A1_1, 0x01 }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x96 }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x09 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xab }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x05 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x1c }, + { WSA884X_CDC_SPK_DSM_A4_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x17 }, + { WSA884X_CDC_SPK_DSM_A5_1, 0x02 }, + { WSA884X_CDC_SPK_DSM_A6_0, 0xaa }, + { WSA884X_CDC_SPK_DSM_A7_0, 0xe3 }, + { WSA884X_CDC_SPK_DSM_C_0, 0x69 }, + { WSA884X_CDC_SPK_DSM_C_1, 0x54 }, + { WSA884X_CDC_SPK_DSM_C_2, 0x02 }, + { WSA884X_CDC_SPK_DSM_C_3, 0x15 }, + { WSA884X_CDC_SPK_DSM_R1, 0xa4 }, + { WSA884X_CDC_SPK_DSM_R2, 0xb5 }, + { WSA884X_CDC_SPK_DSM_R3, 0x86 }, + { WSA884X_CDC_SPK_DSM_R4, 0x85 }, + { WSA884X_CDC_SPK_DSM_R5, 0xaa }, + { WSA884X_CDC_SPK_DSM_R6, 0xe2 }, + { WSA884X_CDC_SPK_DSM_R7, 0x62 }, + { WSA884X_CDC_SPK_GAIN_PDM_0, 0x00 }, + { WSA884X_CDC_SPK_GAIN_PDM_1, 0xfc }, + { WSA884X_CDC_SPK_GAIN_PDM_2, 0x05 }, + { WSA884X_PDM_WD_CTL, 0x00 }, + { WSA884X_DEM_BYPASS_DATA0, 0x00 }, + { WSA884X_DEM_BYPASS_DATA1, 0x00 }, + { WSA884X_DEM_BYPASS_DATA2, 0x00 }, + { WSA884X_DEM_BYPASS_DATA3, 0x00 }, + { WSA884X_DRE_CTL_0, 0x70 }, + { WSA884X_DRE_CTL_1, 0x04 }, + { WSA884X_DRE_IDLE_DET_CTL, 0x2f }, + { WSA884X_GAIN_RAMPING_CTL, 0x50 }, + { WSA884X_GAIN_RAMPING_MIN, 0x12 }, + { WSA884X_TAGC_CTL, 0x15 }, + { WSA884X_TAGC_TIME, 0xbc }, + { WSA884X_TAGC_FORCE_VAL, 0x00 }, + { WSA884X_VAGC_CTL, 0x01 }, + { WSA884X_VAGC_TIME, 0x0f }, + { WSA884X_VAGC_ATTN_LVL_1, 0x03 }, + { WSA884X_VAGC_ATTN_LVL_2, 0x06 }, + { WSA884X_VAGC_ATTN_LVL_3, 0x09 }, + { WSA884X_CLSH_CTL_0, 0x37 }, + { WSA884X_CLSH_CTL_1, 0x81 }, + { WSA884X_CLSH_V_HD_PA, 0x0c }, + { WSA884X_CLSH_V_PA_MIN, 0x00 }, + { WSA884X_CLSH_OVRD_VAL, 0x00 }, + { WSA884X_CLSH_HARD_MAX, 0xff }, + { WSA884X_CLSH_SOFT_MAX, 0xf5 }, + { WSA884X_CLSH_SIG_DP, 0x00 }, + { WSA884X_PBR_DELAY_CTL, 0x07 }, + { WSA884X_CLSH_SRL_MAX_PBR, 0x02 }, + { WSA884X_CLSH_VTH1, 0x00 }, + { WSA884X_CLSH_VTH2, 0x00 }, + { WSA884X_CLSH_VTH3, 0x00 }, + { WSA884X_CLSH_VTH4, 0x00 }, + { WSA884X_CLSH_VTH5, 0x00 }, + { WSA884X_CLSH_VTH6, 0x00 }, + { WSA884X_CLSH_VTH7, 0x00 }, + { WSA884X_CLSH_VTH8, 0x00 }, + { WSA884X_CLSH_VTH9, 0x00 }, + { WSA884X_CLSH_VTH10, 0x00 }, + { WSA884X_CLSH_VTH11, 0x00 }, + { WSA884X_CLSH_VTH12, 0x00 }, + { WSA884X_CLSH_VTH13, 0x00 }, + { WSA884X_CLSH_VTH14, 0x00 }, + { WSA884X_CLSH_VTH15, 0x00 }, + { WSA884X_DIG_CTRL1_PAGE, 0x00 }, + { WSA884X_PIN_CTL, 0x04 }, + { WSA884X_PIN_CTL_OE, 0x00 }, + { WSA884X_PIN_WDATA_IOPAD, 0x00 }, + { WSA884X_I2C_SLAVE_CTL, 0x00 }, + { WSA884X_SPMI_PAD_CTL0, 0x2f }, + { WSA884X_SPMI_PAD_CTL1, 0x2f }, + { WSA884X_SPMI_PAD_CTL2, 0x2f }, + { WSA884X_MEM_CTL, 0x00 }, + { WSA884X_SWR_HM_TEST0, 0x08 }, + { WSA884X_OTP_CTRL0, 0x00 }, + { WSA884X_OTP_CTRL2, 0x00 }, + { WSA884X_OTP_PRG_TCSP0, 0x77 }, + { WSA884X_OTP_PRG_TCSP1, 0x00 }, + { WSA884X_OTP_PRG_TPPS, 0x47 }, + { WSA884X_OTP_PRG_TVPS, 0x3b }, + { WSA884X_OTP_PRG_TVPH, 0x47 }, + { WSA884X_OTP_PRG_TPPR0, 0x47 }, + { WSA884X_OTP_PRG_TPPR1, 0x00 }, + { WSA884X_OTP_PRG_TPPH, 0x47 }, + { WSA884X_OTP_PRG_END, 0x47 }, + { WSA884X_WAVG_PLAY, 0x00 }, + { WSA884X_WAVG_CTL, 0x06 }, + { WSA884X_WAVG_LRA_PER_0, 0xd1 }, + { WSA884X_WAVG_LRA_PER_1, 0x00 }, + { WSA884X_WAVG_DELTA_THETA_0, 0xe6 }, + { WSA884X_WAVG_DELTA_THETA_1, 0x04 }, + { WSA884X_WAVG_DIRECT_AMP_0, 0x50 }, + { WSA884X_WAVG_DIRECT_AMP_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP0_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP0_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP1_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP1_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP2_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP2_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP3_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP3_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP4_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP4_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP5_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP5_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP6_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP6_1, 0x00 }, + { WSA884X_WAVG_PTRN_AMP7_0, 0x50 }, + { WSA884X_WAVG_PTRN_AMP7_1, 0x00 }, + { WSA884X_WAVG_PER_0_1, 0x88 }, + { WSA884X_WAVG_PER_2_3, 0x88 }, + { WSA884X_WAVG_PER_4_5, 0x88 }, + { WSA884X_WAVG_PER_6_7, 0x88 }, + { WSA884X_INTR_MODE, 0x00 }, + { WSA884X_INTR_MASK0, 0x90 }, + { WSA884X_INTR_MASK1, 0x00 }, + { WSA884X_INTR_CLEAR0, 0x00 }, + { WSA884X_INTR_CLEAR1, 0x00 }, + { WSA884X_INTR_LEVEL0, 0x04 }, + { WSA884X_INTR_LEVEL1, 0x00 }, + { WSA884X_INTR_SET0, 0x00 }, + { WSA884X_INTR_SET1, 0x00 }, + { WSA884X_INTR_TEST0, 0x00 }, + { WSA884X_INTR_TEST1, 0x00 }, + { WSA884X_PDM_TEST_MODE, 0x00 }, + { WSA884X_PA_FSM_DBG, 0x00 }, + { WSA884X_DIG_DEBUG_MODE, 0x00 }, + { WSA884X_DIG_DEBUG_SEL, 0x00 }, + { WSA884X_DIG_DEBUG_EN, 0x00 }, + { WSA884X_TADC_DETECT_DBG_CTL, 0x00 }, + { WSA884X_TADC_DEBUG_MSB, 0x00 }, + { WSA884X_TADC_DEBUG_LSB, 0x00 }, + { WSA884X_SAMPLE_EDGE_SEL, 0x7f }, + { WSA884X_SWR_EDGE_SEL, 0x00 }, + { WSA884X_TEST_MODE_CTL, 0x05 }, + { WSA884X_IOPAD_CTL, 0x00 }, + { WSA884X_ANA_CSR_DBG_ADD, 0x00 }, + { WSA884X_ANA_CSR_DBG_CTL, 0x12 }, + { WSA884X_CLK_DBG_CTL, 0x00 }, + { WSA884X_SPARE_0, 0x00 }, + { WSA884X_SPARE_1, 0x00 }, + { WSA884X_SPARE_2, 0x00 }, + { WSA884X_SCODE, 0x00 }, + { WSA884X_DIG_TRIM_PAGE, 0x00 }, + { WSA884X_EMEM_0, 0x00 }, + { WSA884X_EMEM_1, 0x00 }, + { WSA884X_EMEM_2, 0x00 }, + { WSA884X_EMEM_3, 0x00 }, + { WSA884X_EMEM_4, 0x00 }, + { WSA884X_EMEM_5, 0x00 }, + { WSA884X_EMEM_6, 0x00 }, + { WSA884X_EMEM_7, 0x00 }, + { WSA884X_EMEM_8, 0x00 }, + { WSA884X_EMEM_9, 0x00 }, + { WSA884X_EMEM_10, 0x00 }, + { WSA884X_EMEM_11, 0x00 }, + { WSA884X_EMEM_12, 0x00 }, + { WSA884X_EMEM_13, 0x00 }, + { WSA884X_EMEM_14, 0x00 }, + { WSA884X_EMEM_15, 0x00 }, + { WSA884X_EMEM_16, 0x00 }, + { WSA884X_EMEM_17, 0x00 }, + { WSA884X_EMEM_18, 0x00 }, + { WSA884X_EMEM_19, 0x00 }, + { WSA884X_EMEM_20, 0x00 }, + { WSA884X_EMEM_21, 0x00 }, + { WSA884X_EMEM_22, 0x00 }, + { WSA884X_EMEM_23, 0x00 }, + { WSA884X_EMEM_24, 0x00 }, + { WSA884X_EMEM_25, 0x00 }, + { WSA884X_EMEM_26, 0x00 }, + { WSA884X_EMEM_27, 0x00 }, + { WSA884X_EMEM_28, 0x00 }, + { WSA884X_EMEM_29, 0x00 }, + { WSA884X_EMEM_30, 0x00 }, + { WSA884X_EMEM_31, 0x00 }, + { WSA884X_EMEM_32, 0x00 }, + { WSA884X_EMEM_33, 0x00 }, + { WSA884X_EMEM_34, 0x00 }, + { WSA884X_EMEM_35, 0x00 }, + { WSA884X_EMEM_36, 0x00 }, + { WSA884X_EMEM_37, 0x00 }, + { WSA884X_EMEM_38, 0x00 }, + { WSA884X_EMEM_39, 0x00 }, + { WSA884X_EMEM_40, 0x00 }, + { WSA884X_EMEM_41, 0x00 }, + { WSA884X_EMEM_42, 0x00 }, + { WSA884X_EMEM_43, 0x00 }, + { WSA884X_EMEM_44, 0x00 }, + { WSA884X_EMEM_45, 0x00 }, + { WSA884X_EMEM_46, 0x00 }, + { WSA884X_EMEM_47, 0x00 }, + { WSA884X_EMEM_48, 0x00 }, + { WSA884X_EMEM_49, 0x00 }, + { WSA884X_EMEM_50, 0x00 }, + { WSA884X_EMEM_51, 0x00 }, + { WSA884X_EMEM_52, 0x00 }, + { WSA884X_EMEM_53, 0x00 }, + { WSA884X_EMEM_54, 0x00 }, + { WSA884X_EMEM_55, 0x00 }, + { WSA884X_EMEM_56, 0x00 }, + { WSA884X_EMEM_57, 0x00 }, + { WSA884X_EMEM_58, 0x00 }, + { WSA884X_EMEM_59, 0x00 }, + { WSA884X_EMEM_60, 0x00 }, + { WSA884X_EMEM_61, 0x00 }, + { WSA884X_EMEM_62, 0x00 }, + { WSA884X_EMEM_63, 0x00 }, +}; + +static bool wsa884x_readonly_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_DOUT_MSB: + case WSA884X_DOUT_LSB: + case WSA884X_STATUS: + case WSA884X_SPK_TOP_SPARE_TUNE_2: + case WSA884X_SPK_TOP_SPARE_TUNE_3: + case WSA884X_SPK_TOP_SPARE_TUNE_4: + case WSA884X_SPARE_TUNE_5: + case WSA884X_SPARE_TUNE_6: + case WSA884X_SPARE_TUNE_7: + case WSA884X_SPARE_TUNE_8: + case WSA884X_SPARE_TUNE_9: + case WSA884X_SPARE_TUNE_10: + case WSA884X_PA_STATUS0: + case WSA884X_PA_STATUS1: + case WSA884X_PA_STATUS2: + case WSA884X_PA_STATUS3: + case WSA884X_PA_STATUS4: + case WSA884X_PA_STATUS5: + case WSA884X_SPARE_RO_1: + case WSA884X_SPARE_RO_2: + case WSA884X_SPARE_RO_3: + case WSA884X_SPARE_RO_0: + case WSA884X_BOOST_SPARE_RO_1: + case WSA884X_STATUS_0: + case WSA884X_STATUS_1: + case WSA884X_CHIP_ID0: + case WSA884X_CHIP_ID1: + case WSA884X_CHIP_ID2: + case WSA884X_CHIP_ID3: + case WSA884X_BUS_ID: + case WSA884X_PA_FSM_STA0: + case WSA884X_PA_FSM_STA1: + case WSA884X_PA_FSM_ERR_COND0: + case WSA884X_PA_FSM_ERR_COND1: + case WSA884X_TEMP_DIN_MSB: + case WSA884X_TEMP_DIN_LSB: + case WSA884X_TEMP_DOUT_MSB: + case WSA884X_TEMP_DOUT_LSB: + case WSA884X_VBAT_DIN_MSB: + case WSA884X_VBAT_DIN_LSB: + case WSA884X_VBAT_DOUT_MSB: + case WSA884X_VBAT_DOUT_LSB: + case WSA884X_VBAT_CAL_MSB: + case WSA884X_VBAT_CAL_LSB: + case WSA884X_VPHX_SYS_EN_STATUS: + case WSA884X_PIN_STATUS: + case WSA884X_SWR_HM_TEST1: + case WSA884X_OTP_CTRL1: + case WSA884X_OTP_STAT: + case WSA884X_WAVG_STA: + case WSA884X_INTR_STATUS0: + case WSA884X_INTR_STATUS1: + case WSA884X_ATE_TEST_MODE: + case WSA884X_SPARE_R: + return true; + } + return false; +} + +static bool wsa884x_writeable_register(struct device *dev, unsigned int reg) +{ + return !wsa884x_readonly_register(dev, reg); +} + +static bool wsa884x_volatile_register(struct device *dev, unsigned int reg) +{ + switch (reg) { + case WSA884X_ANA_WO_CTL_0: + case WSA884X_ANA_WO_CTL_1: + return true; + } + return wsa884x_readonly_register(dev, reg); +} + +static struct regmap_config wsa884x_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .cache_type = REGCACHE_MAPLE, + .reg_defaults = wsa884x_defaults, + .max_register = WSA884X_MAX_REGISTER, + .num_reg_defaults = ARRAY_SIZE(wsa884x_defaults), + .volatile_reg = wsa884x_volatile_register, + .writeable_reg = wsa884x_writeable_register, + .reg_format_endian = REGMAP_ENDIAN_NATIVE, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .use_single_read = true, +}; + +static const struct reg_sequence wsa884x_reg_init[] = { + { WSA884X_BOP2_PROG, FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_VTH_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_BOP2_PROG_BOP2_HYST_MASK, 0x6) }, + { WSA884X_REF_CTRL, (0xd2 & ~WSA884X_REF_CTRL_BG_RDY_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_REF_CTRL_BG_RDY_SEL_MASK, 0x1) }, + /* + * Downstream suggests for batteries different than 1-Stacked (1S): + * { WSA884X_TOP_CTRL1, 0xd3 & ~WSA884X_TOP_CTRL1_OCP_LOWVBAT_ITH_EN_MASK }, + */ + { WSA884X_STB_CTRL1, (0x42 & ~WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK) | + FIELD_PREP_CONST(WSA884X_STB_CTRL1_SLOPE_COMP_CURRENT_MASK, 0xd) }, + { WSA884X_CURRENT_LIMIT, (0x54 & ~WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK) | + FIELD_PREP_CONST(WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, 0x9) }, + { WSA884X_ZX_CTRL1, (0xf0 & ~WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_ZX_CTRL1_ZX_DET_SW_SEL_MASK, 0x3) }, + { WSA884X_ILIM_CTRL1, (0xe2 & ~WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK) | + FIELD_PREP_CONST(WSA884X_ILIM_CTRL1_ILIM_OFFSET_PB_MASK, 0x3) }, + { WSA884X_CKWD_CTL_1, FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_VPP_SW_CTL_MASK, 0x0) | + FIELD_PREP_CONST(WSA884X_CKWD_CTL_1_CKWD_VCOMP_VREF_SEL_MASK, 0x13) }, + { WSA884X_PA_FSM_CTL1, (0xfe & ~WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK) | + FIELD_PREP_CONST(WSA884X_PA_FSM_CTL1_NOISE_GATE_BLOCK_MASK, 0x4) }, /* == 0xfe */ + { WSA884X_VBAT_THRM_FLT_CTL, (0x7f & ~WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK) | + FIELD_PREP_CONST(WSA884X_VBAT_THRM_FLT_CTL_VBAT_COEF_SEL_MASK, 0x4) }, + { WSA884X_VBAT_CAL_CTL, FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_RESERVE_MASK, 0x2) | + FIELD_PREP_CONST(WSA884X_VBAT_CAL_CTL_VBAT_CAL_EN_MASK, 0x1) }, + { WSA884X_BOP_DEGLITCH_CTL, FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_SETTING_MASK, 0x8) | + FIELD_PREP_CONST(WSA884X_BOP_DEGLITCH_CTL_BOP_DEGLITCH_EN_MASK, 0x1) }, + { WSA884X_CDC_SPK_DSM_A2_0, 0x0a }, + { WSA884X_CDC_SPK_DSM_A2_1, 0x08 }, + { WSA884X_CDC_SPK_DSM_A3_0, 0xf3 }, + { WSA884X_CDC_SPK_DSM_A3_1, 0x07 }, + { WSA884X_CDC_SPK_DSM_A4_0, 0x79 }, + { WSA884X_CDC_SPK_DSM_A5_0, 0x0b }, + { WSA884X_CDC_SPK_DSM_A6_0, 0x8a }, + { WSA884X_CDC_SPK_DSM_A7_0, 0x9b }, + { WSA884X_CDC_SPK_DSM_C_0, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C3_MASK, 0x6) | + FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_0_COEF_C2_MASK, 0x8) }, + { WSA884X_CDC_SPK_DSM_C_2, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_2_COEF_C7_MASK, 0xf) }, + { WSA884X_CDC_SPK_DSM_C_3, FIELD_PREP_CONST(WSA884X_CDC_SPK_DSM_C_3_COEF_C7_MASK, 0x20) }, + { WSA884X_CDC_SPK_DSM_R1, 0x83 }, + { WSA884X_CDC_SPK_DSM_R2, 0x7f }, + { WSA884X_CDC_SPK_DSM_R3, 0x9d }, + { WSA884X_CDC_SPK_DSM_R4, 0x82 }, + { WSA884X_CDC_SPK_DSM_R5, 0x8b }, + { WSA884X_CDC_SPK_DSM_R6, 0x9b }, + { WSA884X_CDC_SPK_DSM_R7, 0x3f }, + /* Speaker mode by default */ + { WSA884X_DRE_CTL_0, FIELD_PREP_CONST(WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x7) }, + { WSA884X_CLSH_CTL_0, (0x37 & ~WSA884X_CLSH_CTL_0_DLY_CODE_MASK) | + FIELD_PREP_CONST(WSA884X_CLSH_CTL_0_DLY_CODE_MASK, 0x6) }, + /* + * WSA884X_CLSH_VTH values for speaker mode with G_21_DB system gain, + * battery 1S and rload 8 Ohms. + */ + { WSA884X_CLSH_VTH1, WSA884X_VTH_TO_REG(863), }, + { WSA884X_CLSH_VTH2, WSA884X_VTH_TO_REG(918), }, + { WSA884X_CLSH_VTH3, WSA884X_VTH_TO_REG(980), }, + { WSA884X_CLSH_VTH4, WSA884X_VTH_TO_REG(1043), }, + { WSA884X_CLSH_VTH5, WSA884X_VTH_TO_REG(1098), }, + { WSA884X_CLSH_VTH6, WSA884X_VTH_TO_REG(1137), }, + { WSA884X_CLSH_VTH7, WSA884X_VTH_TO_REG(1184), }, + { WSA884X_CLSH_VTH8, WSA884X_VTH_TO_REG(1239), }, + { WSA884X_CLSH_VTH9, WSA884X_VTH_TO_REG(1278), }, + { WSA884X_CLSH_VTH10, WSA884X_VTH_TO_REG(1380), }, + { WSA884X_CLSH_VTH11, WSA884X_VTH_TO_REG(1482), }, + { WSA884X_CLSH_VTH12, WSA884X_VTH_TO_REG(1584), }, + { WSA884X_CLSH_VTH13, WSA884X_VTH_TO_REG(1663), }, + { WSA884X_CLSH_VTH14, WSA884X_VTH_TO_REG(1780), }, + { WSA884X_CLSH_VTH15, WSA884X_VTH_TO_REG(2000), }, + { WSA884X_ANA_WO_CTL_1, 0x00 }, + { WSA884X_OTP_REG_38, 0x00 }, + { WSA884X_OTP_REG_40, FIELD_PREP_CONST(WSA884X_OTP_REG_40_ISENSE_RESCAL_MASK, 0x8) }, +}; + +static void wsa884x_set_gain_parameters(struct wsa884x_priv *wsa884x) +{ + struct regmap *regmap = wsa884x->regmap; + unsigned int min_gain, igain, vgain, comp_offset; + + /* + * Downstream sets gain parameters customized per boards per use-case. + * Choose here some sane values matching knowon users, like QRD8550 + * board:. + * + * Values match here downstream: + * For WSA884X_RECEIVER - G_7P5_DB system gain + * For WSA884X_SPEAKER - G_21_DB system gain + */ + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + comp_offset = COMP_OFFSET4; + min_gain = G_M6_DB; + igain = ISENSE_18_DB; + vgain = VSENSE_M12_DB; + } else { + /* WSA884X_SPEAKER */ + comp_offset = COMP_OFFSET0; + min_gain = G_0_DB; + igain = ISENSE_12_DB; + vgain = VSENSE_M24_DB; + } + + regmap_update_bits(regmap, WSA884X_ISENSE2, + WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, + FIELD_PREP(WSA884X_ISENSE2_ISENSE_GAIN_CTL_MASK, igain)); + regmap_update_bits(regmap, WSA884X_VSENSE1, + WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, + FIELD_PREP(WSA884X_VSENSE1_GAIN_VSENSE_FE_MASK, vgain)); + regmap_update_bits(regmap, WSA884X_GAIN_RAMPING_MIN, + WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, + FIELD_PREP(WSA884X_GAIN_RAMPING_MIN_MIN_GAIN_MASK, min_gain)); + + if (wsa884x->port_enable[WSA884X_PORT_COMP]) { + regmap_update_bits(regmap, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_OFFSET_MASK, + FIELD_PREP(WSA884X_DRE_CTL_0_OFFSET_MASK, comp_offset)); + + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x0)); + } else { + regmap_update_bits(regmap, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + FIELD_PREP(WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, 0x1)); + } +} + +static void wsa884x_init(struct wsa884x_priv *wsa884x) +{ + unsigned int wo_ctl_0; + unsigned int variant = 0; + + if (!regmap_read(wsa884x->regmap, WSA884X_OTP_REG_0, &variant)) + wsa884x->variant = variant & WSA884X_OTP_REG_0_ID_MASK; + + regmap_multi_reg_write(wsa884x->regmap, wsa884x_reg_init, + ARRAY_SIZE(wsa884x_reg_init)); + + wo_ctl_0 = 0xc; + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MASK, + WSA884X_ANA_WO_CTL_0_DAC_CM_CLAMP_EN_MODE_SPEAKER); + /* Assume that compander is enabled by default unless it is haptics sku */ + if (wsa884x->variant == WSA884X_OTP_ID_WSA8845H) + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_18_DB); + else + wo_ctl_0 |= FIELD_PREP(WSA884X_ANA_WO_CTL_0_PA_AUX_GAIN_MASK, + WSA884X_ANA_WO_CTL_0_PA_AUX_0_DB); + regmap_write(wsa884x->regmap, WSA884X_ANA_WO_CTL_0, wo_ctl_0); + + wsa884x_set_gain_parameters(wsa884x); + + wsa884x->hw_init = false; +} + +static int wsa884x_update_status(struct sdw_slave *slave, + enum sdw_slave_status status) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + int ret; + + if (status == SDW_SLAVE_UNATTACHED) { + wsa884x->hw_init = false; + regcache_cache_only(wsa884x->regmap, true); + regcache_mark_dirty(wsa884x->regmap); + return 0; + } + + if (wsa884x->hw_init || status != SDW_SLAVE_ATTACHED) + return 0; + + regcache_cache_only(wsa884x->regmap, false); + ret = regcache_sync(wsa884x->regmap); + if (ret < 0) { + dev_err(&slave->dev, "Cannot sync regmap cache\n"); + return ret; + } + + wsa884x_init(wsa884x); + + return 0; +} + +static int wsa884x_port_prep(struct sdw_slave *slave, + struct sdw_prepare_ch *prepare_ch, + enum sdw_port_prep_ops state) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(&slave->dev); + + if (state == SDW_OPS_PORT_POST_PREP) + wsa884x->port_prepared[prepare_ch->num - 1] = true; + else + wsa884x->port_prepared[prepare_ch->num - 1] = false; + + return 0; +} + +static const struct sdw_slave_ops wsa884x_slave_ops = { + .update_status = wsa884x_update_status, + .port_prep = wsa884x_port_prep, +}; + +static int wsa884x_dev_mode_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + ucontrol->value.enumerated.item[0] = wsa884x->dev_mode; + + return 0; +} + +static int wsa884x_dev_mode_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + if (wsa884x->dev_mode == ucontrol->value.enumerated.item[0]) + return 0; + + wsa884x->dev_mode = ucontrol->value.enumerated.item[0]; + + return 1; +} + +static int wsa884x_get_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + ucontrol->value.integer.value[0] = wsa884x->port_enable[portidx]; + + return 0; +} + +static int wsa884x_set_swr_port(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *comp = snd_soc_kcontrol_component(kcontrol); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + struct soc_mixer_control *mixer = (struct soc_mixer_control *)kcontrol->private_value; + int portidx = mixer->reg; + + if (ucontrol->value.integer.value[0]) { + if (wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = true; + } else { + if (!wsa884x->port_enable[portidx]) + return 0; + + wsa884x->port_enable[portidx] = false; + } + + return 1; +} + +static int wsa884x_codec_probe(struct snd_soc_component *comp) +{ + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(comp); + + snd_soc_component_init_regmap(comp, wsa884x->regmap); + + return 0; +} + +static void wsa884x_spkr_post_pmu(struct snd_soc_component *component, + struct wsa884x_priv *wsa884x) +{ + unsigned int curr_limit, curr_ovrd_en; + + wsa884x_set_gain_parameters(wsa884x); + if (wsa884x->dev_mode == WSA884X_RECEIVER) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0x3); + snd_soc_component_write_field(component, WSA884X_CDC_PATH_MODE, + WSA884X_CDC_PATH_MODE_RXD_MODE_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PWM_CLK_CTL, + WSA884X_PWM_CLK_CTL_PWM_CLK_FREQ_SEL_MASK, + 0x1); + } else { + /* WSA884X_SPEAKER */ + snd_soc_component_write_field(component, WSA884X_DRE_CTL_0, + WSA884X_DRE_CTL_0_PROG_DELAY_MASK, 0xf); + } + + if (wsa884x->port_enable[WSA884X_PORT_PBR]) { + curr_ovrd_en = 0x0; + curr_limit = 0x15; + } else { + curr_ovrd_en = 0x1; + if (wsa884x->dev_mode == WSA884X_RECEIVER) + curr_limit = 0x9; + else + curr_limit = 0x15; + } + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_OVRD_EN_MASK, + curr_ovrd_en); + snd_soc_component_write_field(component, WSA884X_CURRENT_LIMIT, + WSA884X_CURRENT_LIMIT_CURRENT_LIMIT_MASK, + curr_limit); +} + +static int wsa884x_spkr_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm); + struct wsa884x_priv *wsa884x = snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + wsa884x_spkr_post_pmu(component, wsa884x); + + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + + break; + case SND_SOC_DAPM_PRE_PMD: + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PDM_WD_CTL, + WSA884X_PDM_WD_CTL_PDM_WD_EN_MASK, + 0x0); + break; + } + + return 0; +} + +static const struct snd_soc_dapm_widget wsa884x_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("IN"), + SND_SOC_DAPM_SPK("SPKR", wsa884x_spkr_event), +}; + +static const DECLARE_TLV_DB_SCALE(pa_gain, -900, 150, -900); + +static const struct snd_kcontrol_new wsa884x_snd_controls[] = { + SOC_SINGLE_RANGE_TLV("PA Volume", WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_SHIFT, + 0x0, 0x1f, 1, pa_gain), + SOC_ENUM_EXT("WSA MODE", wsa884x_dev_mode_enum, + wsa884x_dev_mode_get, wsa884x_dev_mode_put), + SOC_SINGLE_EXT("DAC Switch", WSA884X_PORT_DAC, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("COMP Switch", WSA884X_PORT_COMP, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("BOOST Switch", WSA884X_PORT_BOOST, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("PBR Switch", WSA884X_PORT_PBR, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("VISENSE Switch", WSA884X_PORT_VISENSE, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), + SOC_SINGLE_EXT("CPS Switch", WSA884X_PORT_CPS, 0, 1, 0, + wsa884x_get_swr_port, wsa884x_set_swr_port), +}; + +static const struct snd_soc_dapm_route wsa884x_audio_map[] = { + {"SPKR", NULL, "IN"}, +}; + +static const struct snd_soc_component_driver wsa884x_component_drv = { + .name = "WSA884x", + .probe = wsa884x_codec_probe, + .controls = wsa884x_snd_controls, + .num_controls = ARRAY_SIZE(wsa884x_snd_controls), + .dapm_widgets = wsa884x_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(wsa884x_dapm_widgets), + .dapm_routes = wsa884x_audio_map, + .num_dapm_routes = ARRAY_SIZE(wsa884x_audio_map), +}; + +static int wsa884x_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + int i; + + wsa884x->active_ports = 0; + for (i = 0; i < WSA884X_MAX_SWR_PORTS; i++) { + if (!wsa884x->port_enable[i]) + continue; + + wsa884x->port_config[wsa884x->active_ports] = wsa884x_pconfig[i]; + wsa884x->active_ports++; + } + + wsa884x->sconfig.frame_rate = params_rate(params); + + return sdw_stream_add_slave(wsa884x->slave, &wsa884x->sconfig, + wsa884x->port_config, wsa884x->active_ports, + wsa884x->sruntime); +} + +static int wsa884x_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + sdw_stream_remove_slave(wsa884x->slave, wsa884x->sruntime); + + return 0; +} + +static int wsa884x_mute_stream(struct snd_soc_dai *dai, int mute, int stream) +{ + struct snd_soc_component *component = dai->component; + + if (mute) { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x0); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x0); + + } else { + snd_soc_component_write_field(component, WSA884X_DRE_CTL_1, + WSA884X_DRE_CTL_1_CSR_GAIN_EN_MASK, + 0x1); + snd_soc_component_write_field(component, WSA884X_PA_FSM_EN, + WSA884X_PA_FSM_EN_GLOBAL_PA_EN_MASK, + 0x1); + } + + return 0; +} + +static int wsa884x_set_stream(struct snd_soc_dai *dai, + void *stream, int direction) +{ + struct wsa884x_priv *wsa884x = dev_get_drvdata(dai->dev); + + wsa884x->sruntime = stream; + + return 0; +} + +static const struct snd_soc_dai_ops wsa884x_dai_ops = { + .hw_params = wsa884x_hw_params, + .hw_free = wsa884x_hw_free, + .mute_stream = wsa884x_mute_stream, + .set_stream = wsa884x_set_stream, +}; + +static struct snd_soc_dai_driver wsa884x_dais[] = { + { + .name = "SPKR", + .playback = { + .stream_name = "SPKR Playback", + .rates = WSA884X_RATES | WSA884X_FRAC_RATES, + .formats = WSA884X_FORMATS, + .rate_min = 8000, + .rate_max = 384000, + .channels_min = 1, + .channels_max = 1, + }, + .ops = &wsa884x_dai_ops, + }, +}; + +static void wsa884x_gpio_powerdown(void *data) +{ + gpiod_direction_output(data, 1); +} + +static void wsa884x_regulator_disable(void *data) +{ + regulator_bulk_disable(WSA884X_SUPPLIES_NUM, data); +} + +static int wsa884x_probe(struct sdw_slave *pdev, + const struct sdw_device_id *id) +{ + struct device *dev = &pdev->dev; + struct wsa884x_priv *wsa884x; + unsigned int i; + int ret; + + wsa884x = devm_kzalloc(dev, sizeof(*wsa884x), GFP_KERNEL); + if (!wsa884x) + return -ENOMEM; + + for (i = 0; i < WSA884X_SUPPLIES_NUM; i++) + wsa884x->supplies[i].supply = wsa884x_supply_name[i]; + + ret = devm_regulator_bulk_get(dev, WSA884X_SUPPLIES_NUM, + wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(WSA884X_SUPPLIES_NUM, wsa884x->supplies); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, wsa884x_regulator_disable, + wsa884x->supplies); + if (ret) + return ret; + + wsa884x->sd_n = devm_gpiod_get_optional(dev, "powerdown", + GPIOD_OUT_HIGH); + if (IS_ERR(wsa884x->sd_n)) + return dev_err_probe(dev, PTR_ERR(wsa884x->sd_n), + "Shutdown Control GPIO not found\n"); + + dev_set_drvdata(dev, wsa884x); + wsa884x->slave = pdev; + wsa884x->dev = dev; + wsa884x->dev_mode = WSA884X_SPEAKER; + wsa884x->sconfig.ch_count = 1; + wsa884x->sconfig.bps = 1; + wsa884x->sconfig.direction = SDW_DATA_DIR_RX; + wsa884x->sconfig.type = SDW_STREAM_PDM; + + pdev->prop.sink_ports = GENMASK(WSA884X_MAX_SWR_PORTS, 0); + pdev->prop.simple_clk_stop_capable = true; + pdev->prop.sink_dpn_prop = wsa884x_sink_dpn_prop; + pdev->prop.scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY; + + /* Bring out of reset */ + gpiod_direction_output(wsa884x->sd_n, 0); + ret = devm_add_action_or_reset(dev, wsa884x_gpio_powerdown, wsa884x->sd_n); + if (ret) + return ret; + + wsa884x->regmap = devm_regmap_init_sdw(pdev, &wsa884x_regmap_config); + if (IS_ERR(wsa884x->regmap)) + return dev_err_probe(dev, PTR_ERR(wsa884x->regmap), + "regmap_init failed\n"); + + /* Start in cache-only until device is enumerated */ + regcache_cache_only(wsa884x->regmap, true); + wsa884x->hw_init = true; + + pm_runtime_set_autosuspend_delay(dev, 3000); + pm_runtime_use_autosuspend(dev); + pm_runtime_mark_last_busy(dev); + pm_runtime_set_active(dev); + pm_runtime_enable(dev); + + return devm_snd_soc_register_component(dev, + &wsa884x_component_drv, + wsa884x_dais, + ARRAY_SIZE(wsa884x_dais)); +} + +static int __maybe_unused wsa884x_runtime_suspend(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, true); + regcache_mark_dirty(regmap); + + return 0; +} + +static int __maybe_unused wsa884x_runtime_resume(struct device *dev) +{ + struct regmap *regmap = dev_get_regmap(dev, NULL); + + regcache_cache_only(regmap, false); + regcache_sync(regmap); + + return 0; +} + +static const struct dev_pm_ops wsa884x_pm_ops = { + SET_RUNTIME_PM_OPS(wsa884x_runtime_suspend, wsa884x_runtime_resume, NULL) +}; + +static const struct sdw_device_id wsa884x_swr_id[] = { + SDW_SLAVE_ENTRY(0x0217, 0x204, 0), + {}, +}; +MODULE_DEVICE_TABLE(sdw, wsa884x_swr_id); + +static struct sdw_driver wsa884x_codec_driver = { + .driver = { + .name = "wsa884x-codec", + .pm = &wsa884x_pm_ops, + }, + .probe = wsa884x_probe, + .ops = &wsa884x_slave_ops, + .id_table = wsa884x_swr_id, +}; +module_sdw_driver(wsa884x_codec_driver); + +MODULE_AUTHOR("Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>"); +MODULE_DESCRIPTION("WSA884x codec driver"); +MODULE_LICENSE("GPL"); |