summaryrefslogtreecommitdiff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig62
-rw-r--r--sound/soc/codecs/Makefile14
-rw-r--r--sound/soc/codecs/adau1977-spi.c2
-rw-r--r--sound/soc/codecs/cs35l41-lib.c73
-rw-r--r--sound/soc/codecs/cs35l41.c144
-rw-r--r--sound/soc/codecs/cs35l41.h1
-rw-r--r--sound/soc/codecs/cs35l45-i2c.c3
-rw-r--r--sound/soc/codecs/cs35l45-spi.c5
-rw-r--r--sound/soc/codecs/cs35l45-tables.c135
-rw-r--r--sound/soc/codecs/cs35l45.c630
-rw-r--r--sound/soc/codecs/cs35l45.h267
-rw-r--r--sound/soc/codecs/cs35l56-i2c.c82
-rw-r--r--sound/soc/codecs/cs35l56-sdw.c566
-rw-r--r--sound/soc/codecs/cs35l56-shared.c362
-rw-r--r--sound/soc/codecs/cs35l56-spi.c79
-rw-r--r--sound/soc/codecs/cs35l56.c1601
-rw-r--r--sound/soc/codecs/cs35l56.h81
-rw-r--r--sound/soc/codecs/cs4271-i2c.c1
-rw-r--r--sound/soc/codecs/cs4271-spi.c1
-rw-r--r--sound/soc/codecs/cs4271.c4
-rw-r--r--sound/soc/codecs/cs42l42-sdw.c3
-rw-r--r--sound/soc/codecs/cs47l15.c6
-rw-r--r--sound/soc/codecs/cs47l24.c6
-rw-r--r--sound/soc/codecs/cs47l35.c6
-rw-r--r--sound/soc/codecs/cs47l85.c6
-rw-r--r--sound/soc/codecs/cs47l90.c6
-rw-r--r--sound/soc/codecs/cs47l92.c6
-rw-r--r--sound/soc/codecs/da7218.c10
-rw-r--r--sound/soc/codecs/da7219-aad.c60
-rw-r--r--sound/soc/codecs/da7219-aad.h5
-rw-r--r--sound/soc/codecs/es8316.c33
-rw-r--r--sound/soc/codecs/inno_rk3036.c6
-rw-r--r--sound/soc/codecs/lpass-macro-common.c2
-rw-r--r--sound/soc/codecs/lpass-macro-common.h3
-rw-r--r--sound/soc/codecs/lpass-rx-macro.c42
-rw-r--r--sound/soc/codecs/lpass-tx-macro.c41
-rw-r--r--sound/soc/codecs/lpass-va-macro.c6
-rw-r--r--sound/soc/codecs/lpass-wsa-macro.c43
-rw-r--r--sound/soc/codecs/max98363.c464
-rw-r--r--sound/soc/codecs/max98363.h36
-rw-r--r--sound/soc/codecs/max98373-sdw.c34
-rw-r--r--sound/soc/codecs/max98373.c4
-rw-r--r--sound/soc/codecs/max9867.c19
-rw-r--r--sound/soc/codecs/msm8916-wcd-analog.c6
-rw-r--r--sound/soc/codecs/msm8916-wcd-digital.c6
-rw-r--r--sound/soc/codecs/mt6358.c2
-rw-r--r--sound/soc/codecs/mt6359.c2
-rw-r--r--sound/soc/codecs/nau8821.c96
-rw-r--r--sound/soc/codecs/nau8821.h23
-rw-r--r--sound/soc/codecs/nau8825.c8
-rw-r--r--sound/soc/codecs/nau8825.h1
-rw-r--r--sound/soc/codecs/pcm179x-spi.c2
-rw-r--r--sound/soc/codecs/rk817_codec.c6
-rw-r--r--sound/soc/codecs/rt1019.c2
-rw-r--r--sound/soc/codecs/rt1308-sdw.c30
-rw-r--r--sound/soc/codecs/rt1308-sdw.h4
-rw-r--r--sound/soc/codecs/rt1316-sdw.c30
-rw-r--r--sound/soc/codecs/rt1316-sdw.h4
-rw-r--r--sound/soc/codecs/rt1318-sdw.c30
-rw-r--r--sound/soc/codecs/rt1318-sdw.h4
-rw-r--r--sound/soc/codecs/rt5682-sdw.c34
-rw-r--r--sound/soc/codecs/rt700.c30
-rw-r--r--sound/soc/codecs/rt700.h4
-rw-r--r--sound/soc/codecs/rt711-sdca.c30
-rw-r--r--sound/soc/codecs/rt711-sdca.h4
-rw-r--r--sound/soc/codecs/rt711.c30
-rw-r--r--sound/soc/codecs/rt711.h4
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c983
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.h108
-rw-r--r--sound/soc/codecs/rt712-sdca.c30
-rw-r--r--sound/soc/codecs/rt712-sdca.h4
-rw-r--r--sound/soc/codecs/rt715-sdca.c30
-rw-r--r--sound/soc/codecs/rt715-sdca.h4
-rw-r--r--sound/soc/codecs/rt715.c30
-rw-r--r--sound/soc/codecs/rt715.h4
-rw-r--r--sound/soc/codecs/sdw-mockup.c34
-rw-r--r--sound/soc/codecs/sma1303.c2
-rw-r--r--sound/soc/codecs/src4xxx-i2c.c2
-rw-r--r--sound/soc/codecs/ssm2602.c15
-rw-r--r--sound/soc/codecs/sta32x.c39
-rw-r--r--sound/soc/codecs/sta350.c63
-rw-r--r--sound/soc/codecs/tas5086.c2
-rw-r--r--sound/soc/codecs/tas571x.c59
-rw-r--r--sound/soc/codecs/tas5720.c3
-rw-r--r--sound/soc/codecs/wcd9335.c27
-rw-r--r--sound/soc/codecs/wcd934x.c25
-rw-r--r--sound/soc/codecs/wcd938x.c33
-rw-r--r--sound/soc/codecs/wm5102.c6
-rw-r--r--sound/soc/codecs/wm5110.c6
-rw-r--r--sound/soc/codecs/wm8903.c1
-rw-r--r--sound/soc/codecs/wm8994.c6
-rw-r--r--sound/soc/codecs/wm8997.c6
-rw-r--r--sound/soc/codecs/wm8998.c6
-rw-r--r--sound/soc/codecs/wm_adsp.c63
-rw-r--r--sound/soc/codecs/wm_adsp.h3
-rw-r--r--sound/soc/codecs/zl38060.c2
96 files changed, 6235 insertions, 713 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 07747565c3b5..79d2362ad055 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -68,6 +68,9 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS35L41_I2C
imply SND_SOC_CS35L45_I2C
imply SND_SOC_CS35L45_SPI
+ imply SND_SOC_CS35L56_I2C
+ imply SND_SOC_CS35L56_SPI
+ imply SND_SOC_CS35L56_SDW
imply SND_SOC_CS42L42
imply SND_SOC_CS42L42_SDW
imply SND_SOC_CS42L51_I2C
@@ -130,6 +133,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_MAX98925
imply SND_SOC_MAX98926
imply SND_SOC_MAX98927
+ imply SND_SOC_MAX98363
imply SND_SOC_MAX98373_I2C
imply SND_SOC_MAX98373_SDW
imply SND_SOC_MAX98390
@@ -200,6 +204,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT711_SDW
imply SND_SOC_RT711_SDCA_SDW
imply SND_SOC_RT712_SDCA_SDW
+ imply SND_SOC_RT712_SDCA_DMIC_SDW
imply SND_SOC_RT715_SDW
imply SND_SOC_RT715_SDCA_SDW
imply SND_SOC_RT1308_SDW
@@ -364,6 +369,9 @@ config SND_SOC_WM_ADSP
default y if SND_SOC_WM2200=y
default y if SND_SOC_CS35L41_SPI=y
default y if SND_SOC_CS35L41_I2C=y
+ default y if SND_SOC_CS35L45_SPI=y
+ default y if SND_SOC_CS35L45_I2C=y
+ default y if SND_SOC_CS35L56=y
default m if SND_SOC_MADERA=m
default m if SND_SOC_CS47L24=m
default m if SND_SOC_WM5102=m
@@ -371,6 +379,9 @@ config SND_SOC_WM_ADSP
default m if SND_SOC_WM2200=m
default m if SND_SOC_CS35L41_SPI=m
default m if SND_SOC_CS35L41_I2C=m
+ default m if SND_SOC_CS35L45_SPI=m
+ default m if SND_SOC_CS35L45_I2C=m
+ default m if SND_SOC_CS35L56=m
config SND_SOC_AB8500_CODEC
tristate
@@ -711,6 +722,41 @@ config SND_SOC_CS35L45_I2C
Enable support for Cirrus Logic CS35L45 smart speaker amplifier
with I2C control.
+config SND_SOC_CS35L56
+ tristate
+
+config SND_SOC_CS35L56_SHARED
+ tristate
+
+config SND_SOC_CS35L56_I2C
+ tristate "Cirrus Logic CS35L56 CODEC (I2C)"
+ depends on I2C
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select REGMAP_I2C
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with I2C control
+
+config SND_SOC_CS35L56_SPI
+ tristate "Cirrus Logic CS35L56 CODEC (SPI)"
+ depends on SPI_MASTER
+ depends on SOUNDWIRE || !SOUNDWIRE
+ select REGMAP_SPI
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with SPI control
+
+config SND_SOC_CS35L56_SDW
+ tristate "Cirrus Logic CS35L56 CODEC (SDW)"
+ depends on SOUNDWIRE
+ select REGMAP
+ select SND_SOC_CS35L56
+ select SND_SOC_CS35L56_SHARED
+ help
+ Enable support for Cirrus Logic CS35L56 boosted amplifier with SoundWire control
+
config SND_SOC_CS42L42_CORE
tristate
@@ -1089,6 +1135,16 @@ config SND_SOC_MAX98520
To compile this driver as a module, choose M here.
+config SND_SOC_MAX98363
+ tristate "Analog Devices MAX98363 Soundwire Speaker Amplifier"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ help
+ Enable support for Analog Devices MAX98363 Soundwire
+ amplifier. MAX98363 supports the MIPI SoundWire v1.2
+ compatible interface for audio and control data.
+ This amplifier does not support I2C and I2S.
+
config SND_SOC_MAX98373
tristate
@@ -1477,6 +1533,12 @@ config SND_SOC_RT712_SDCA_SDW
select REGMAP_SOUNDWIRE
select REGMAP_SOUNDWIRE_MBQ
+config SND_SOC_RT712_SDCA_DMIC_SDW
+ tristate "Realtek RT712 SDCA DMIC Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+ select REGMAP_SOUNDWIRE_MBQ
+
config SND_SOC_RT715
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index f1ca18f7946c..5cdbae88e6e3 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -66,6 +66,11 @@ snd-soc-cs35l41-i2c-objs := cs35l41-i2c.o
snd-soc-cs35l45-objs := cs35l45.o cs35l45-tables.o
snd-soc-cs35l45-spi-objs := cs35l45-spi.o
snd-soc-cs35l45-i2c-objs := cs35l45-i2c.o
+snd-soc-cs35l56-objs := cs35l56.o
+snd-soc-cs35l56-shared-objs := cs35l56-shared.o
+snd-soc-cs35l56-i2c-objs := cs35l56-i2c.o
+snd-soc-cs35l56-spi-objs := cs35l56-spi.o
+snd-soc-cs35l56-sdw-objs := cs35l56-sdw.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l42-i2c-objs := cs42l42-i2c.o
snd-soc-cs42l42-sdw-objs := cs42l42-sdw.o
@@ -143,6 +148,7 @@ snd-soc-max98925-objs := max98925.o
snd-soc-max98926-objs := max98926.o
snd-soc-max98927-objs := max98927.o
snd-soc-max98520-objs := max98520.o
+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
@@ -228,6 +234,7 @@ snd-soc-rt700-objs := rt700.o rt700-sdw.o
snd-soc-rt711-objs := rt711.o rt711-sdw.o
snd-soc-rt711-sdca-objs := rt711-sdca.o rt711-sdca-sdw.o
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-rt9120-objs := rt9120.o
@@ -433,6 +440,11 @@ obj-$(CONFIG_SND_SOC_CS35L41_I2C) += snd-soc-cs35l41-i2c.o
obj-$(CONFIG_SND_SOC_CS35L45) += snd-soc-cs35l45.o
obj-$(CONFIG_SND_SOC_CS35L45_SPI) += snd-soc-cs35l45-spi.o
obj-$(CONFIG_SND_SOC_CS35L45_I2C) += snd-soc-cs35l45-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56) += snd-soc-cs35l56.o
+obj-$(CONFIG_SND_SOC_CS35L56_SHARED) += snd-soc-cs35l56-shared.o
+obj-$(CONFIG_SND_SOC_CS35L56_I2C) += snd-soc-cs35l56-i2c.o
+obj-$(CONFIG_SND_SOC_CS35L56_SPI) += snd-soc-cs35l56-spi.o
+obj-$(CONFIG_SND_SOC_CS35L56_SDW) += snd-soc-cs35l56-sdw.o
obj-$(CONFIG_SND_SOC_CS42L42_CORE) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42-i2c.o
obj-$(CONFIG_SND_SOC_CS42L42_SDW) += snd-soc-cs42l42-sdw.o
@@ -505,6 +517,7 @@ obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o
obj-$(CONFIG_SND_SOC_MAX98520) += snd-soc-max98520.o
+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
@@ -591,6 +604,7 @@ obj-$(CONFIG_SND_SOC_RT700) += snd-soc-rt700.o
obj-$(CONFIG_SND_SOC_RT711) += snd-soc-rt711.o
obj-$(CONFIG_SND_SOC_RT711_SDCA_SDW) += snd-soc-rt711-sdca.o
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_RT9120) += snd-soc-rt9120.o
diff --git a/sound/soc/codecs/adau1977-spi.c b/sound/soc/codecs/adau1977-spi.c
index 8370bec27a9c..207c5c95f35a 100644
--- a/sound/soc/codecs/adau1977-spi.c
+++ b/sound/soc/codecs/adau1977-spi.c
@@ -55,7 +55,7 @@ static const struct spi_device_id adau1977_spi_ids[] = {
};
MODULE_DEVICE_TABLE(spi, adau1977_spi_ids);
-static const struct of_device_id adau1977_spi_of_match[] = {
+static const struct of_device_id adau1977_spi_of_match[] __maybe_unused = {
{ .compatible = "adi,adau1977" },
{ .compatible = "adi,adau1978" },
{ .compatible = "adi,adau1979" },
diff --git a/sound/soc/codecs/cs35l41-lib.c b/sound/soc/codecs/cs35l41-lib.c
index 04be71435491..8538e2871c5f 100644
--- a/sound/soc/codecs/cs35l41-lib.c
+++ b/sound/soc/codecs/cs35l41-lib.c
@@ -1114,12 +1114,31 @@ static const struct reg_sequence cs35l41_reset_to_safe[] = {
{ 0x00000040, 0x00000033 },
};
+static const struct reg_sequence cs35l41_actv_seq[] = {
+ /* SYNC_BST_CTL_RX_EN = 1; SYNC_BST_CTL_TX_EN = 1 */
+ {CS35L41_MDSYNC_EN, 0x00003000},
+ /* BST_CTL_SEL = MDSYNC */
+ {CS35L41_BSTCVRT_VCTRL2, 0x00000002},
+};
+
+static const struct reg_sequence cs35l41_pass_seq[] = {
+ /* SYNC_BST_CTL_RX_EN = 0; SYNC_BST_CTL_TX_EN = 1 */
+ {CS35L41_MDSYNC_EN, 0x00001000},
+ /* BST_EN = 0 */
+ {CS35L41_PWR_CTRL2, 0x00003300},
+ /* BST_CTL_SEL = MDSYNC */
+ {CS35L41_BSTCVRT_VCTRL2, 0x00000002},
+};
+
int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
struct cs35l41_hw_cfg *hw_cfg)
{
int ret;
switch (hw_cfg->bst_type) {
+ case CS35L41_SHD_BOOST_ACTV:
+ regmap_multi_reg_write(regmap, cs35l41_actv_seq, ARRAY_SIZE(cs35l41_actv_seq));
+ fallthrough;
case CS35L41_INT_BOOST:
ret = cs35l41_boost_config(dev, regmap, hw_cfg->bst_ind,
hw_cfg->bst_cap, hw_cfg->bst_ipk);
@@ -1138,6 +1157,10 @@ int cs35l41_init_boost(struct device *dev, struct regmap *regmap,
ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
CS35L41_BST_DIS_FET_OFF << CS35L41_BST_EN_SHIFT);
break;
+ case CS35L41_SHD_BOOST_PASS:
+ ret = regmap_multi_reg_write(regmap, cs35l41_pass_seq,
+ ARRAY_SIZE(cs35l41_pass_seq));
+ break;
default:
dev_err(dev, "Boost type %d not supported\n", hw_cfg->bst_type);
ret = -EINVAL;
@@ -1165,11 +1188,59 @@ bool cs35l41_safe_reset(struct regmap *regmap, enum cs35l41_boost_type b_type)
}
EXPORT_SYMBOL_GPL(cs35l41_safe_reset);
-int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable)
+int cs35l41_global_enable(struct regmap *regmap, enum cs35l41_boost_type b_type, int enable,
+ struct completion *pll_lock)
{
int ret;
+ unsigned int gpio1_func, pad_control, pwr_ctrl1, pwr_ctrl3;
+ struct reg_sequence cs35l41_mdsync_down_seq[] = {
+ {CS35L41_PWR_CTRL3, 0},
+ {CS35L41_GPIO_PAD_CONTROL, 0},
+ {CS35L41_PWR_CTRL1, 0, 3000},
+ };
+ struct reg_sequence cs35l41_mdsync_up_seq[] = {
+ {CS35L41_PWR_CTRL3, 0},
+ {CS35L41_PWR_CTRL1, 0x00000000, 3000},
+ {CS35L41_PWR_CTRL1, 0x00000001, 3000},
+ };
switch (b_type) {
+ case CS35L41_SHD_BOOST_ACTV:
+ case CS35L41_SHD_BOOST_PASS:
+ regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
+ regmap_read(regmap, CS35L41_GPIO_PAD_CONTROL, &pad_control);
+
+ pwr_ctrl3 &= ~CS35L41_SYNC_EN_MASK;
+ pwr_ctrl1 = enable << CS35L41_GLOBAL_EN_SHIFT;
+
+ gpio1_func = enable ? CS35L41_GPIO1_MDSYNC : CS35L41_GPIO1_HIZ;
+ gpio1_func <<= CS35L41_GPIO1_CTRL_SHIFT;
+
+ pad_control &= ~CS35L41_GPIO1_CTRL_MASK;
+ pad_control |= gpio1_func & CS35L41_GPIO1_CTRL_MASK;
+
+ cs35l41_mdsync_down_seq[0].def = pwr_ctrl3;
+ cs35l41_mdsync_down_seq[1].def = pad_control;
+ cs35l41_mdsync_down_seq[2].def = pwr_ctrl1;
+ ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_down_seq,
+ ARRAY_SIZE(cs35l41_mdsync_down_seq));
+ if (!enable)
+ break;
+
+ if (!pll_lock)
+ return -EINVAL;
+
+ ret = wait_for_completion_timeout(pll_lock, msecs_to_jiffies(1000));
+ if (ret == 0) {
+ ret = -ETIMEDOUT;
+ } else {
+ regmap_read(regmap, CS35L41_PWR_CTRL3, &pwr_ctrl3);
+ pwr_ctrl3 |= CS35L41_SYNC_EN_MASK;
+ cs35l41_mdsync_up_seq[0].def = pwr_ctrl3;
+ ret = regmap_multi_reg_write(regmap, cs35l41_mdsync_up_seq,
+ ARRAY_SIZE(cs35l41_mdsync_up_seq));
+ }
+ break;
case CS35L41_INT_BOOST:
ret = regmap_update_bits(regmap, CS35L41_PWR_CTRL1, CS35L41_GLOBAL_EN_MASK,
enable << CS35L41_GLOBAL_EN_SHIFT);
diff --git a/sound/soc/codecs/cs35l41.c b/sound/soc/codecs/cs35l41.c
index c223d83e02cf..6ac501f008ec 100644
--- a/sound/soc/codecs/cs35l41.c
+++ b/sound/soc/codecs/cs35l41.c
@@ -150,6 +150,7 @@ static const struct cs35l41_fs_mon_config cs35l41_fs_mon[] = {
{ 5644800, 16, 24 },
{ 6000000, 16, 24 },
{ 6144000, 16, 24 },
+ { 12288000, 0, 0 },
};
static int cs35l41_get_fs_mon_config_index(int freq)
@@ -356,6 +357,30 @@ static const struct snd_kcontrol_new cs35l41_aud_controls[] = {
WM_ADSP_FW_CONTROL("DSP1", 0),
};
+static void cs35l41_boost_enable(struct cs35l41_private *cs35l41, unsigned int enable)
+{
+ switch (cs35l41->hw_cfg.bst_type) {
+ case CS35L41_INT_BOOST:
+ case CS35L41_SHD_BOOST_ACTV:
+ enable = enable ? CS35L41_BST_EN_DEFAULT : CS35L41_BST_DIS_FET_OFF;
+ regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2, CS35L41_BST_EN_MASK,
+ enable << CS35L41_BST_EN_SHIFT);
+ break;
+ default:
+ break;
+ }
+}
+
+
+static void cs35l41_error_release(struct cs35l41_private *cs35l41, unsigned int irq_err_bit,
+ unsigned int rel_err_bit)
+{
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1, irq_err_bit);
+ regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, rel_err_bit);
+ regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, rel_err_bit, 0);
+}
+
static irqreturn_t cs35l41_irq(int irq, void *data)
{
struct cs35l41_private *cs35l41 = data;
@@ -392,94 +417,49 @@ static irqreturn_t cs35l41_irq(int irq, void *data)
*/
if (status[0] & CS35L41_AMP_SHORT_ERR) {
dev_crit_ratelimited(cs35l41->dev, "Amp short error\n");
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_AMP_SHORT_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_AMP_SHORT_ERR_RLS,
- CS35L41_AMP_SHORT_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_AMP_SHORT_ERR_RLS, 0);
+ cs35l41_error_release(cs35l41, CS35L41_AMP_SHORT_ERR, CS35L41_AMP_SHORT_ERR_RLS);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_TEMP_WARN) {
dev_crit_ratelimited(cs35l41->dev, "Over temperature warning\n");
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_TEMP_WARN);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_WARN_ERR_RLS,
- CS35L41_TEMP_WARN_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_WARN_ERR_RLS, 0);
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_WARN, CS35L41_TEMP_WARN_ERR_RLS);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_TEMP_ERR) {
dev_crit_ratelimited(cs35l41->dev, "Over temperature error\n");
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_TEMP_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_ERR_RLS,
- CS35L41_TEMP_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_TEMP_ERR_RLS, 0);
+ cs35l41_error_release(cs35l41, CS35L41_TEMP_ERR, CS35L41_TEMP_ERR_RLS);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_BST_OVP_ERR) {
dev_crit_ratelimited(cs35l41->dev, "VBST Over Voltage error\n");
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK, 0);
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_BST_OVP_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_OVP_ERR_RLS,
- CS35L41_BST_OVP_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_OVP_ERR_RLS, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK,
- CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_OVP_ERR, CS35L41_BST_OVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_BST_DCM_UVP_ERR) {
dev_crit_ratelimited(cs35l41->dev, "DCM VBST Under Voltage Error\n");
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK, 0);
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_BST_DCM_UVP_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_UVP_ERR_RLS,
- CS35L41_BST_UVP_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_UVP_ERR_RLS, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK,
- CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_DCM_UVP_ERR, CS35L41_BST_UVP_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
ret = IRQ_HANDLED;
}
if (status[0] & CS35L41_BST_SHORT_ERR) {
dev_crit_ratelimited(cs35l41->dev, "LBST error: powering off!\n");
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK, 0);
- regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
- CS35L41_BST_SHORT_ERR);
- regmap_write(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_SHORT_ERR_RLS,
- CS35L41_BST_SHORT_ERR_RLS);
- regmap_update_bits(cs35l41->regmap, CS35L41_PROTECT_REL_ERR_IGN,
- CS35L41_BST_SHORT_ERR_RLS, 0);
- regmap_update_bits(cs35l41->regmap, CS35L41_PWR_CTRL2,
- CS35L41_BST_EN_MASK,
- CS35L41_BST_EN_DEFAULT << CS35L41_BST_EN_SHIFT);
+ cs35l41_boost_enable(cs35l41, 0);
+ cs35l41_error_release(cs35l41, CS35L41_BST_SHORT_ERR, CS35L41_BST_SHORT_ERR_RLS);
+ cs35l41_boost_enable(cs35l41, 1);
+ ret = IRQ_HANDLED;
+ }
+
+ if (status[2] & CS35L41_PLL_LOCK) {
+ regmap_write(cs35l41->regmap, CS35L41_IRQ1_STATUS3, CS35L41_PLL_LOCK);
+ complete(&cs35l41->pll_lock);
ret = IRQ_HANDLED;
}
@@ -520,10 +500,12 @@ static int cs35l41_main_amp_event(struct snd_soc_dapm_widget *w,
cs35l41_pup_patch,
ARRAY_SIZE(cs35l41_pup_patch));
- cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1);
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 1,
+ &cs35l41->pll_lock);
break;
case SND_SOC_DAPM_POST_PMD:
- cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0);
+ cs35l41_global_enable(cs35l41->regmap, cs35l41->hw_cfg.bst_type, 0,
+ &cs35l41->pll_lock);
ret = regmap_read_poll_timeout(cs35l41->regmap, CS35L41_IRQ1_STATUS1,
val, val & CS35L41_PDN_DONE_MASK,
@@ -830,6 +812,10 @@ static const struct snd_pcm_hw_constraint_list cs35l41_constraints = {
static int cs35l41_pcm_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
+ struct cs35l41_private *cs35l41 = snd_soc_component_get_drvdata(dai->component);
+
+ reinit_completion(&cs35l41->pll_lock);
+
if (substream->runtime)
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
@@ -1037,9 +1023,21 @@ static int cs35l41_handle_pdata(struct device *dev, struct cs35l41_hw_cfg *hw_cf
unsigned int val;
int ret;
- ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
- if (ret >= 0)
- hw_cfg->bst_type = val;
+ /* Some ACPI systems received the Shared Boost feature before the upstream driver,
+ * leaving those systems with deprecated _DSD properties.
+ * To correctly configure those systems add shared-boost-active and shared-boost-passive
+ * properties mapped to the correct value in boost-type.
+ * These two are not DT properties and should not be used in new systems designs.
+ */
+ if (device_property_read_bool(dev, "cirrus,shared-boost-active")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_ACTV;
+ } else if (device_property_read_bool(dev, "cirrus,shared-boost-passive")) {
+ hw_cfg->bst_type = CS35L41_SHD_BOOST_PASS;
+ } else {
+ ret = device_property_read_u32(dev, "cirrus,boost-type", &val);
+ if (ret >= 0)
+ hw_cfg->bst_type = val;
+ }
ret = device_property_read_u32(dev, "cirrus,boost-peak-milliamp", &val);
if (ret >= 0)
@@ -1280,6 +1278,10 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
/* Set interrupt masks for critical errors */
regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1,
CS35L41_INT1_MASK_DEFAULT);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 0 << CS35L41_INT3_PLL_LOCK_SHIFT);
ret = devm_request_threaded_irq(cs35l41->dev, cs35l41->irq, NULL, cs35l41_irq,
IRQF_ONESHOT | IRQF_SHARED | irq_pol,
@@ -1303,6 +1305,8 @@ int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *
if (ret < 0)
goto err;
+ init_completion(&cs35l41->pll_lock);
+
pm_runtime_set_autosuspend_delay(cs35l41->dev, 3000);
pm_runtime_use_autosuspend(cs35l41->dev);
pm_runtime_mark_last_busy(cs35l41->dev);
@@ -1345,6 +1349,10 @@ void cs35l41_remove(struct cs35l41_private *cs35l41)
pm_runtime_disable(cs35l41->dev);
regmap_write(cs35l41->regmap, CS35L41_IRQ1_MASK1, 0xFFFFFFFF);
+ if (cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_PASS ||
+ cs35l41->hw_cfg.bst_type == CS35L41_SHD_BOOST_ACTV)
+ regmap_update_bits(cs35l41->regmap, CS35L41_IRQ1_MASK3, CS35L41_INT3_PLL_LOCK_MASK,
+ 1 << CS35L41_INT3_PLL_LOCK_SHIFT);
kfree(cs35l41->dsp.system_name);
wm_adsp2_remove(&cs35l41->dsp);
cs35l41_safe_reset(cs35l41->regmap, cs35l41->hw_cfg.bst_type);
diff --git a/sound/soc/codecs/cs35l41.h b/sound/soc/codecs/cs35l41.h
index c85cbc1dd333..34d967d4372b 100644
--- a/sound/soc/codecs/cs35l41.h
+++ b/sound/soc/codecs/cs35l41.h
@@ -33,6 +33,7 @@ struct cs35l41_private {
int irq;
/* GPIO for /RST */
struct gpio_desc *reset_gpio;
+ struct completion pll_lock;
};
int cs35l41_probe(struct cs35l41_private *cs35l41, const struct cs35l41_hw_cfg *hw_cfg);
diff --git a/sound/soc/codecs/cs35l45-i2c.c b/sound/soc/codecs/cs35l45-i2c.c
index 1117df4b2f11..562f73df7afa 100644
--- a/sound/soc/codecs/cs35l45-i2c.c
+++ b/sound/soc/codecs/cs35l45-i2c.c
@@ -32,6 +32,9 @@ static int cs35l45_i2c_probe(struct i2c_client *client)
}
cs35l45->dev = dev;
+ cs35l45->irq = client->irq;
+ cs35l45->bus_type = CONTROL_BUS_I2C;
+ cs35l45->i2c_addr = client->addr;
return cs35l45_probe(cs35l45);
}
diff --git a/sound/soc/codecs/cs35l45-spi.c b/sound/soc/codecs/cs35l45-spi.c
index ffaca07fb267..a00b23b4180c 100644
--- a/sound/soc/codecs/cs35l45-spi.c
+++ b/sound/soc/codecs/cs35l45-spi.c
@@ -23,6 +23,9 @@ static int cs35l45_spi_probe(struct spi_device *spi)
if (cs35l45 == NULL)
return -ENOMEM;
+ spi->max_speed_hz = CS35L45_SPI_MAX_FREQ;
+ spi_setup(spi);
+
spi_set_drvdata(spi, cs35l45);
cs35l45->regmap = devm_regmap_init_spi(spi, &cs35l45_spi_regmap);
if (IS_ERR(cs35l45->regmap)) {
@@ -32,6 +35,8 @@ static int cs35l45_spi_probe(struct spi_device *spi)
}
cs35l45->dev = dev;
+ cs35l45->irq = spi->irq;
+ cs35l45->bus_type = CONTROL_BUS_SPI;
return cs35l45_probe(cs35l45);
}
diff --git a/sound/soc/codecs/cs35l45-tables.c b/sound/soc/codecs/cs35l45-tables.c
index 4b1320a2e6e9..46610e64e818 100644
--- a/sound/soc/codecs/cs35l45-tables.c
+++ b/sound/soc/codecs/cs35l45-tables.c
@@ -43,6 +43,12 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_apply_patch, SND_SOC_CS35L45);
static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_BLOCK_ENABLES, 0x00003323 },
{ CS35L45_BLOCK_ENABLES2, 0x00000010 },
+ { CS35L45_SYNC_GPIO1, 0x00000007 },
+ { CS35L45_INTB_GPIO2_MCLK_REF, 0x00000005 },
+ { CS35L45_GPIO3, 0x00000005 },
+ { CS35L45_PWRMGT_CTL, 0x00000000 },
+ { CS35L45_WAKESRC_CTL, 0x00000008 },
+ { CS35L45_WKI2C_CTL, 0x00000030 },
{ CS35L45_REFCLK_INPUT, 0x00000510 },
{ CS35L45_GLOBAL_SAMPLE_RATE, 0x00000003 },
{ CS35L45_ASP_ENABLES1, 0x00000000 },
@@ -60,7 +66,53 @@ static const struct reg_default cs35l45_defaults[] = {
{ CS35L45_ASPTX3_INPUT, 0x00000020 },
{ CS35L45_ASPTX4_INPUT, 0x00000028 },
{ CS35L45_ASPTX5_INPUT, 0x00000048 },
+ { CS35L45_DSP1_RX1_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX2_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX3_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX4_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX5_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX6_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX7_RATE, 0x00000001 },
+ { CS35L45_DSP1_RX8_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX1_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX2_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX3_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX4_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX5_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX6_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX7_RATE, 0x00000001 },
+ { CS35L45_DSP1_TX8_RATE, 0x00000001 },
+ { CS35L45_DSP1RX1_INPUT, 0x00000008 },
+ { CS35L45_DSP1RX2_INPUT, 0x00000009 },
+ { CS35L45_DSP1RX3_INPUT, 0x00000018 },
+ { CS35L45_DSP1RX4_INPUT, 0x00000019 },
+ { CS35L45_DSP1RX5_INPUT, 0x00000020 },
+ { CS35L45_DSP1RX6_INPUT, 0x00000028 },
+ { CS35L45_DSP1RX7_INPUT, 0x0000003A },
+ { CS35L45_DSP1RX8_INPUT, 0x00000028 },
{ CS35L45_AMP_PCM_CONTROL, 0x00100000 },
+ { CS35L45_IRQ1_CFG, 0x00000000 },
+ { CS35L45_IRQ1_MASK_1, 0xBFEFFFBF },
+ { CS35L45_IRQ1_MASK_2, 0xFFFFFFFF },
+ { CS35L45_IRQ1_MASK_3, 0xFFFF87FF },
+ { CS35L45_IRQ1_MASK_4, 0xF8FFFFFF },
+ { CS35L45_IRQ1_MASK_5, 0x0EF80000 },
+ { CS35L45_IRQ1_MASK_6, 0x00000000 },
+ { CS35L45_IRQ1_MASK_7, 0xFFFFFF78 },
+ { CS35L45_IRQ1_MASK_8, 0x00003FFF },
+ { CS35L45_IRQ1_MASK_9, 0x00000000 },
+ { CS35L45_IRQ1_MASK_10, 0x00000000 },
+ { CS35L45_IRQ1_MASK_11, 0x00000000 },
+ { CS35L45_IRQ1_MASK_12, 0x00000000 },
+ { CS35L45_IRQ1_MASK_13, 0x00000000 },
+ { CS35L45_IRQ1_MASK_14, 0x00000001 },
+ { CS35L45_IRQ1_MASK_15, 0x00000000 },
+ { CS35L45_IRQ1_MASK_16, 0x00000000 },
+ { CS35L45_IRQ1_MASK_17, 0x00000000 },
+ { CS35L45_IRQ1_MASK_18, 0x3FE5D0FF },
+ { CS35L45_GPIO1_CTRL1, 0x81000001 },
+ { CS35L45_GPIO2_CTRL1, 0x81000001 },
+ { CS35L45_GPIO3_CTRL1, 0x81000001 },
};
static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
@@ -72,6 +124,13 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_BLOCK_ENABLES:
case CS35L45_BLOCK_ENABLES2:
case CS35L45_ERROR_RELEASE:
+ case CS35L45_SYNC_GPIO1:
+ case CS35L45_INTB_GPIO2_MCLK_REF:
+ case CS35L45_GPIO3:
+ case CS35L45_PWRMGT_CTL:
+ case CS35L45_WAKESRC_CTL:
+ case CS35L45_WKI2C_CTL:
+ case CS35L45_PWRMGT_STS:
case CS35L45_REFCLK_INPUT:
case CS35L45_GLOBAL_SAMPLE_RATE:
case CS35L45_ASP_ENABLES1:
@@ -89,9 +148,59 @@ static bool cs35l45_readable_reg(struct device *dev, unsigned int reg)
case CS35L45_ASPTX3_INPUT:
case CS35L45_ASPTX4_INPUT:
case CS35L45_ASPTX5_INPUT:
+ case CS35L45_DSP1RX1_INPUT:
+ case CS35L45_DSP1RX2_INPUT:
+ case CS35L45_DSP1RX3_INPUT:
+ case CS35L45_DSP1RX4_INPUT:
+ case CS35L45_DSP1RX5_INPUT:
+ case CS35L45_DSP1RX6_INPUT:
+ case CS35L45_DSP1RX7_INPUT:
+ case CS35L45_DSP1RX8_INPUT:
case CS35L45_AMP_PCM_CONTROL:
case CS35L45_AMP_PCM_HPF_TST:
- case CS35L45_IRQ1_EINT_4:
+ case CS35L45_IRQ1_CFG:
+ case CS35L45_IRQ1_STATUS:
+ case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+ case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+ case CS35L45_IRQ1_MASK_1 ... CS35L45_IRQ1_MASK_18:
+ case CS35L45_GPIO_STATUS1:
+ case CS35L45_GPIO1_CTRL1:
+ case CS35L45_GPIO2_CTRL1:
+ case CS35L45_GPIO3_CTRL1:
+ case CS35L45_DSP_MBOX_1:
+ case CS35L45_DSP_MBOX_2:
+ case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+ case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+ case CS35L45_DSP1_SYS_ID:
+ case CS35L45_DSP1_CLOCK_FREQ:
+ case CS35L45_DSP1_RX1_RATE:
+ case CS35L45_DSP1_RX2_RATE:
+ case CS35L45_DSP1_RX3_RATE:
+ case CS35L45_DSP1_RX4_RATE:
+ case CS35L45_DSP1_RX5_RATE:
+ case CS35L45_DSP1_RX6_RATE:
+ case CS35L45_DSP1_RX7_RATE:
+ case CS35L45_DSP1_RX8_RATE:
+ case CS35L45_DSP1_TX1_RATE:
+ case CS35L45_DSP1_TX2_RATE:
+ case CS35L45_DSP1_TX3_RATE:
+ case CS35L45_DSP1_TX4_RATE:
+ case CS35L45_DSP1_TX5_RATE:
+ case CS35L45_DSP1_TX6_RATE:
+ case CS35L45_DSP1_TX7_RATE:
+ case CS35L45_DSP1_TX8_RATE:
+ case CS35L45_DSP1_SCRATCH1:
+ case CS35L45_DSP1_SCRATCH2:
+ case CS35L45_DSP1_SCRATCH3:
+ case CS35L45_DSP1_SCRATCH4:
+ case CS35L45_DSP1_CCM_CORE_CONTROL:
+ case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+ case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+ case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+ case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+ case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+ case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+ case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
return true;
default:
return false;
@@ -106,7 +215,29 @@ static bool cs35l45_volatile_reg(struct device *dev, unsigned int reg)
case CS35L45_GLOBAL_ENABLES:
case CS35L45_ERROR_RELEASE:
case CS35L45_AMP_PCM_HPF_TST: /* not cachable */
- case CS35L45_IRQ1_EINT_4:
+ case CS35L45_PWRMGT_STS:
+ case CS35L45_IRQ1_STATUS:
+ case CS35L45_IRQ1_EINT_1 ... CS35L45_IRQ1_EINT_18:
+ case CS35L45_IRQ1_STS_1 ... CS35L45_IRQ1_STS_18:
+ case CS35L45_GPIO_STATUS1:
+ case CS35L45_DSP_MBOX_1:
+ case CS35L45_DSP_MBOX_2:
+ case CS35L45_DSP_VIRT1_MBOX_1 ... CS35L45_DSP_VIRT1_MBOX_4:
+ case CS35L45_DSP_VIRT2_MBOX_1 ... CS35L45_DSP_VIRT2_MBOX_4:
+ case CS35L45_DSP1_SYS_ID:
+ case CS35L45_DSP1_CLOCK_FREQ:
+ case CS35L45_DSP1_SCRATCH1:
+ case CS35L45_DSP1_SCRATCH2:
+ case CS35L45_DSP1_SCRATCH3:
+ case CS35L45_DSP1_SCRATCH4:
+ case CS35L45_DSP1_CCM_CORE_CONTROL:
+ case CS35L45_DSP1_XMEM_PACK_0 ... CS35L45_DSP1_XMEM_PACK_4607:
+ case CS35L45_DSP1_XMEM_UNPACK32_0 ... CS35L45_DSP1_XMEM_UNPACK32_3071:
+ case CS35L45_DSP1_XMEM_UNPACK24_0 ... CS35L45_DSP1_XMEM_UNPACK24_6143:
+ case CS35L45_DSP1_YMEM_PACK_0 ... CS35L45_DSP1_YMEM_PACK_1532:
+ case CS35L45_DSP1_YMEM_UNPACK32_0 ... CS35L45_DSP1_YMEM_UNPACK32_1022:
+ case CS35L45_DSP1_YMEM_UNPACK24_0 ... CS35L45_DSP1_YMEM_UNPACK24_2043:
+ case CS35L45_DSP1_PMEM_0 ... CS35L45_DSP1_PMEM_3834:
return true;
default:
return false;
diff --git a/sound/soc/codecs/cs35l45.c b/sound/soc/codecs/cs35l45.c
index 855d9f13e6ff..c31597f6bfae 100644
--- a/sound/soc/codecs/cs35l45.c
+++ b/sound/soc/codecs/cs35l45.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/property.h>
+#include <linux/firmware.h>
#include <linux/regulator/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -19,6 +20,71 @@
#include "cs35l45.h"
+static bool cs35l45_check_cspl_mbox_sts(const enum cs35l45_cspl_mboxcmd cmd,
+ enum cs35l45_cspl_mboxstate sts)
+{
+ switch (cmd) {
+ case CSPL_MBOX_CMD_NONE:
+ case CSPL_MBOX_CMD_UNKNOWN_CMD:
+ return true;
+ case CSPL_MBOX_CMD_PAUSE:
+ case CSPL_MBOX_CMD_OUT_OF_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_PAUSED);
+ case CSPL_MBOX_CMD_RESUME:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_REINIT:
+ return (sts == CSPL_MBOX_STS_RUNNING);
+ case CSPL_MBOX_CMD_STOP_PRE_REINIT:
+ return (sts == CSPL_MBOX_STS_RDY_FOR_REINIT);
+ case CSPL_MBOX_CMD_HIBERNATE:
+ return (sts == CSPL_MBOX_STS_HIBERNATE);
+ default:
+ return false;
+ }
+}
+
+static int cs35l45_set_cspl_mbox_cmd(struct cs35l45_private *cs35l45,
+ struct regmap *regmap,
+ const enum cs35l45_cspl_mboxcmd cmd)
+{
+ unsigned int sts = 0, i;
+ int ret;
+
+ if (!cs35l45->dsp.cs_dsp.running) {
+ dev_err(cs35l45->dev, "DSP not running\n");
+ return -EPERM;
+ }
+
+ // Set mailbox cmd
+ ret = regmap_write(regmap, CS35L45_DSP_VIRT1_MBOX_1, cmd);
+ if (ret < 0) {
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(cs35l45->dev, "Failed to write MBOX: %d\n", ret);
+ return ret;
+ }
+
+ // Read mailbox status and verify it is appropriate for the given cmd
+ for (i = 0; i < 5; i++) {
+ usleep_range(1000, 1100);
+
+ ret = regmap_read(regmap, CS35L45_DSP_MBOX_2, &sts);
+ if (ret < 0) {
+ dev_err(cs35l45->dev, "Failed to read MBOX STS: %d\n", ret);
+ continue;
+ }
+
+ if (!cs35l45_check_cspl_mbox_sts(cmd, sts))
+ dev_dbg(cs35l45->dev, "[%u] cmd %u returned invalid sts %u", i, cmd, sts);
+ else
+ return 0;
+ }
+
+ if (cmd != CSPL_MBOX_CMD_OUT_OF_HIBERNATE)
+ dev_err(cs35l45->dev, "Failed to set mailbox cmd %u (status %u)\n", cmd, sts);
+
+ return -ENOMSG;
+}
+
static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
@@ -46,10 +112,68 @@ static int cs35l45_global_en_ev(struct snd_soc_dapm_widget *w,
return 0;
}
+static int cs35l45_dsp_preload_ev(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 cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l45->dsp.cs_dsp.booted)
+ return 0;
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ case SND_SOC_DAPM_POST_PMU:
+ if (cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_PWRMGT_CTL,
+ CS35L45_MEM_RDY_MASK);
+
+ return wm_adsp_event(w, kcontrol, event);
+ case SND_SOC_DAPM_PRE_PMD:
+ if (cs35l45->dsp.preloaded)
+ return 0;
+
+ if (cs35l45->dsp.cs_dsp.running) {
+ ret = wm_adsp_event(w, kcontrol, event);
+ if (ret)
+ return ret;
+ }
+
+ return wm_adsp_early_event(w, kcontrol, event);
+ default:
+ return 0;
+ }
+}
+
+static int cs35l45_dsp_audio_ev(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 cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_RESUME);
+ case SND_SOC_DAPM_PRE_PMD:
+ return cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_PAUSE);
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
static const char * const cs35l45_asp_tx_txt[] = {
"Zero", "ASP_RX1", "ASP_RX2",
"VMON", "IMON", "ERR_VOL",
"VDD_BATTMON", "VDD_BSTMON",
+ "DSP_TX1", "DSP_TX2",
"Interpolator", "IL_TARGET",
};
@@ -57,6 +181,7 @@ static const unsigned int cs35l45_asp_tx_val[] = {
CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
CS35L45_PCM_SRC_VDD_BATTMON, CS35L45_PCM_SRC_VDD_BSTMON,
+ CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2,
CS35L45_PCM_SRC_INTERPOLATOR, CS35L45_PCM_SRC_IL_TARGET,
};
@@ -78,12 +203,54 @@ static const struct soc_enum cs35l45_asp_tx_enums[] = {
cs35l45_asp_tx_val),
};
+static const char * const cs35l45_dsp_rx_txt[] = {
+ "Zero", "ASP_RX1", "ASP_RX2",
+ "VMON", "IMON", "ERR_VOL",
+ "CLASSH_TGT", "VDD_BATTMON",
+ "VDD_BSTMON", "TEMPMON",
+};
+
+static const unsigned int cs35l45_dsp_rx_val[] = {
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_VMON, CS35L45_PCM_SRC_IMON, CS35L45_PCM_SRC_ERR_VOL,
+ CS35L45_PCM_SRC_CLASSH_TGT, CS35L45_PCM_SRC_VDD_BATTMON,
+ CS35L45_PCM_SRC_VDD_BSTMON, CS35L45_PCM_SRC_TEMPMON,
+};
+
+static const struct soc_enum cs35l45_dsp_rx_enums[] = {
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX1_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX2_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX3_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX4_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX5_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX6_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX7_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+ SOC_VALUE_ENUM_SINGLE(CS35L45_DSP1RX8_INPUT, 0, CS35L45_PCM_SRC_MASK,
+ ARRAY_SIZE(cs35l45_dsp_rx_txt), cs35l45_dsp_rx_txt,
+ cs35l45_dsp_rx_val),
+};
+
static const char * const cs35l45_dac_txt[] = {
- "Zero", "ASP_RX1", "ASP_RX2"
+ "Zero", "ASP_RX1", "ASP_RX2", "DSP_TX1", "DSP_TX2"
};
static const unsigned int cs35l45_dac_val[] = {
- CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2
+ CS35L45_PCM_SRC_ZERO, CS35L45_PCM_SRC_ASP_RX1, CS35L45_PCM_SRC_ASP_RX2,
+ CS35L45_PCM_SRC_DSP_TX1, CS35L45_PCM_SRC_DSP_TX2
};
static const struct soc_enum cs35l45_dacpcm_enums[] = {
@@ -100,11 +267,29 @@ static const struct snd_kcontrol_new cs35l45_asp_muxes[] = {
SOC_DAPM_ENUM("ASP_TX5 Source", cs35l45_asp_tx_enums[4]),
};
+static const struct snd_kcontrol_new cs35l45_dsp_muxes[] = {
+ SOC_DAPM_ENUM("DSP_RX1 Source", cs35l45_dsp_rx_enums[0]),
+ SOC_DAPM_ENUM("DSP_RX2 Source", cs35l45_dsp_rx_enums[1]),
+ SOC_DAPM_ENUM("DSP_RX3 Source", cs35l45_dsp_rx_enums[2]),
+ SOC_DAPM_ENUM("DSP_RX4 Source", cs35l45_dsp_rx_enums[3]),
+ SOC_DAPM_ENUM("DSP_RX5 Source", cs35l45_dsp_rx_enums[4]),
+ SOC_DAPM_ENUM("DSP_RX6 Source", cs35l45_dsp_rx_enums[5]),
+ SOC_DAPM_ENUM("DSP_RX7 Source", cs35l45_dsp_rx_enums[6]),
+ SOC_DAPM_ENUM("DSP_RX8 Source", cs35l45_dsp_rx_enums[7]),
+};
+
static const struct snd_kcontrol_new cs35l45_dac_muxes[] = {
SOC_DAPM_ENUM("DACPCM1 Source", cs35l45_dacpcm_enums[0]),
};
static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
+ SND_SOC_DAPM_SPK("DSP1 Preload", NULL),
+ SND_SOC_DAPM_SUPPLY_S("DSP1 Preloader", 100, SND_SOC_NOPM, 0, 0,
+ cs35l45_dsp_preload_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_OUT_DRV_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0,
+ cs35l45_dsp_audio_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_SUPPLY("GLOBAL_EN", SND_SOC_NOPM, 0, 0,
cs35l45_global_en_ev,
SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
@@ -139,6 +324,15 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
SND_SOC_DAPM_MUX("ASP_TX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[3]),
SND_SOC_DAPM_MUX("ASP_TX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_asp_muxes[4]),
+ SND_SOC_DAPM_MUX("DSP_RX1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[0]),
+ SND_SOC_DAPM_MUX("DSP_RX2 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[1]),
+ SND_SOC_DAPM_MUX("DSP_RX3 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[2]),
+ SND_SOC_DAPM_MUX("DSP_RX4 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[3]),
+ SND_SOC_DAPM_MUX("DSP_RX5 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[4]),
+ SND_SOC_DAPM_MUX("DSP_RX6 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[5]),
+ SND_SOC_DAPM_MUX("DSP_RX7 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[6]),
+ SND_SOC_DAPM_MUX("DSP_RX8 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dsp_muxes[7]),
+
SND_SOC_DAPM_MUX("DACPCM1 Source", SND_SOC_NOPM, 0, 0, &cs35l45_dac_muxes[0]),
SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -149,6 +343,8 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
#define CS35L45_ASP_MUX_ROUTE(name) \
{ name" Source", "ASP_RX1", "ASP_RX1" }, \
{ name" Source", "ASP_RX2", "ASP_RX2" }, \
+ { name" Source", "DSP_TX1", "DSP1" }, \
+ { name" Source", "DSP_TX2", "DSP1" }, \
{ name" Source", "VMON", "VMON" }, \
{ name" Source", "IMON", "IMON" }, \
{ name" Source", "ERR_VOL", "ERR_VOL" }, \
@@ -157,10 +353,16 @@ static const struct snd_soc_dapm_widget cs35l45_dapm_widgets[] = {
{ name" Source", "Interpolator", "AMP_INTP" }, \
{ name" Source", "IL_TARGET", "IL_TARGET" }
-#define CS35L45_DAC_MUX_ROUTE(name) \
+#define CS35L45_DSP_MUX_ROUTE(name) \
{ name" Source", "ASP_RX1", "ASP_RX1" }, \
{ name" Source", "ASP_RX2", "ASP_RX2" }
+#define CS35L45_DAC_MUX_ROUTE(name) \
+ { name" Source", "ASP_RX1", "ASP_RX1" }, \
+ { name" Source", "ASP_RX2", "ASP_RX2" }, \
+ { name" Source", "DSP_TX1", "DSP1" }, \
+ { name" Source", "DSP_TX2", "DSP1" }
+
static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
/* Feedback */
{ "VMON", NULL, "VMON_SRC" },
@@ -204,6 +406,27 @@ static const struct snd_soc_dapm_route cs35l45_dapm_routes[] = {
{ "AMP", NULL, "DACPCM1 Source"},
{ "AMP", NULL, "GLOBAL_EN"},
+ CS35L45_DSP_MUX_ROUTE("DSP_RX1"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX2"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX3"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX4"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX5"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX6"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX7"),
+ CS35L45_DSP_MUX_ROUTE("DSP_RX8"),
+
+ {"DSP1", NULL, "DSP_RX1 Source"},
+ {"DSP1", NULL, "DSP_RX2 Source"},
+ {"DSP1", NULL, "DSP_RX3 Source"},
+ {"DSP1", NULL, "DSP_RX4 Source"},
+ {"DSP1", NULL, "DSP_RX5 Source"},
+ {"DSP1", NULL, "DSP_RX6 Source"},
+ {"DSP1", NULL, "DSP_RX7 Source"},
+ {"DSP1", NULL, "DSP_RX8 Source"},
+
+ {"DSP1 Preload", NULL, "DSP1 Preloader"},
+ {"DSP1", NULL, "DSP1 Preloader"},
+
CS35L45_DAC_MUX_ROUTE("DACPCM1"),
{ "SPK", NULL, "AMP"},
@@ -219,6 +442,8 @@ static const struct snd_kcontrol_new cs35l45_controls[] = {
-409, 48,
(CS35L45_AMP_VOL_PCM_WIDTH - 1) - 1,
0, cs35l45_dig_pcm_vol_tlv),
+ WM_ADSP2_PRELOAD_SWITCH("DSP1", 1),
+ WM_ADSP_FW_CONTROL("DSP1", 0),
};
static int cs35l45_set_pll(struct cs35l45_private *cs35l45, unsigned int freq)
@@ -489,7 +714,24 @@ static struct snd_soc_dai_driver cs35l45_dai[] = {
},
};
+static int cs35l45_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ return wm_adsp2_component_probe(&cs35l45->dsp, component);
+}
+
+static void cs35l45_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l45_private *cs35l45 = snd_soc_component_get_drvdata(component);
+
+ wm_adsp2_component_remove(&cs35l45->dsp, component);
+}
+
static const struct snd_soc_component_driver cs35l45_component = {
+ .probe = cs35l45_component_probe,
+ .remove = cs35l45_component_remove,
+
.dapm_widgets = cs35l45_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(cs35l45_dapm_widgets),
@@ -504,11 +746,81 @@ static const struct snd_soc_component_driver cs35l45_component = {
.endianness = 1,
};
+static void cs35l45_setup_hibernate(struct cs35l45_private *cs35l45)
+{
+ unsigned int wksrc;
+
+ if (cs35l45->bus_type == CONTROL_BUS_I2C)
+ wksrc = CS35L45_WKSRC_I2C;
+ else
+ wksrc = CS35L45_WKSRC_SPI;
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_WKSRC_EN_MASK,
+ wksrc << CS35L45_WKSRC_EN_SHIFT);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WAKESRC_CTL,
+ CS35L45_UPDT_WKCTL_MASK);
+
+ regmap_update_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_WKI2C_ADDR_MASK, cs35l45->i2c_addr);
+
+ regmap_set_bits(cs35l45->regmap, CS35L45_WKI2C_CTL,
+ CS35L45_UPDT_WKI2C_MASK);
+}
+
+static int cs35l45_enter_hibernate(struct cs35l45_private *cs35l45)
+{
+ dev_dbg(cs35l45->dev, "Enter hibernate\n");
+
+ cs35l45_setup_hibernate(cs35l45);
+
+ // Don't wait for ACK since bus activity would wake the device
+ regmap_write(cs35l45->regmap, CS35L45_DSP_VIRT1_MBOX_1, CSPL_MBOX_CMD_HIBERNATE);
+
+ return 0;
+}
+
+static int cs35l45_exit_hibernate(struct cs35l45_private *cs35l45)
+{
+ const int wake_retries = 20;
+ const int sleep_retries = 5;
+ int ret, i, j;
+
+ for (i = 0; i < sleep_retries; i++) {
+ dev_dbg(cs35l45->dev, "Exit hibernate\n");
+
+ for (j = 0; j < wake_retries; j++) {
+ ret = cs35l45_set_cspl_mbox_cmd(cs35l45, cs35l45->regmap,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE);
+ if (!ret) {
+ dev_dbg(cs35l45->dev, "Wake success at cycle: %d\n", j);
+ return 0;
+ }
+ usleep_range(100, 200);
+ }
+
+ dev_err(cs35l45->dev, "Wake failed, re-enter hibernate: %d\n", ret);
+
+ cs35l45_setup_hibernate(cs35l45);
+ }
+
+ dev_err(cs35l45->dev, "Timed out waking device\n");
+
+ return -ETIMEDOUT;
+}
+
static int __maybe_unused cs35l45_runtime_suspend(struct device *dev)
{
struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
+ cs35l45_enter_hibernate(cs35l45);
+
regcache_cache_only(cs35l45->regmap, true);
+ regcache_mark_dirty(cs35l45->regmap);
dev_dbg(cs35l45->dev, "Runtime suspended\n");
@@ -520,9 +832,17 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
struct cs35l45_private *cs35l45 = dev_get_drvdata(dev);
int ret;
+ if (!cs35l45->dsp.preloaded || !cs35l45->dsp.cs_dsp.running)
+ return 0;
+
dev_dbg(cs35l45->dev, "Runtime resume\n");
regcache_cache_only(cs35l45->regmap, false);
+
+ ret = cs35l45_exit_hibernate(cs35l45);
+ if (ret)
+ return ret;
+
ret = regcache_sync(cs35l45->regmap);
if (ret != 0)
dev_warn(cs35l45->dev, "regcache_sync failed: %d\n", ret);
@@ -536,7 +856,66 @@ static int __maybe_unused cs35l45_runtime_resume(struct device *dev)
static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
{
+ struct device_node *node = cs35l45->dev->of_node;
+ unsigned int gpio_regs[] = {CS35L45_GPIO1_CTRL1, CS35L45_GPIO2_CTRL1,
+ CS35L45_GPIO3_CTRL1};
+ unsigned int pad_regs[] = {CS35L45_SYNC_GPIO1,
+ CS35L45_INTB_GPIO2_MCLK_REF, CS35L45_GPIO3};
+ struct device_node *child;
unsigned int val;
+ char of_name[32];
+ int ret, i;
+
+ if (!node)
+ return 0;
+
+ for (i = 0; i < CS35L45_NUM_GPIOS; i++) {
+ sprintf(of_name, "cirrus,gpio-ctrl%d", i + 1);
+ child = of_get_child_by_name(node, of_name);
+ if (!child)
+ continue;
+
+ ret = of_property_read_u32(child, "gpio-dir", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_DIR_MASK,
+ val << CS35L45_GPIO_DIR_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-lvl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_LVL_MASK,
+ val << CS35L45_GPIO_LVL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-op-cfg", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_OP_CFG_MASK,
+ val << CS35L45_GPIO_OP_CFG_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-pol", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, gpio_regs[i],
+ CS35L45_GPIO_POL_MASK,
+ val << CS35L45_GPIO_POL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-ctrl", &val);
+ if (!ret)
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_CTRL_MASK,
+ val << CS35L45_GPIO_CTRL_SHIFT);
+
+ ret = of_property_read_u32(child, "gpio-invert", &val);
+ if (!ret) {
+ regmap_update_bits(cs35l45->regmap, pad_regs[i],
+ CS35L45_GPIO_INVERT_MASK,
+ val << CS35L45_GPIO_INVERT_SHIFT);
+ if (i == 1)
+ cs35l45->irq_invert = val;
+ }
+
+ of_node_put(child);
+ }
if (device_property_read_u32(cs35l45->dev,
"cirrus,asp-sdout-hiz-ctrl", &val) == 0) {
@@ -548,6 +927,134 @@ static int cs35l45_apply_property_config(struct cs35l45_private *cs35l45)
return 0;
}
+static int cs35l45_dsp_virt2_mbox3_irq_handle(struct cs35l45_private *cs35l45,
+ const unsigned int cmd,
+ unsigned int data)
+{
+ static char *speak_status = "Unknown";
+
+ switch (cmd) {
+ case EVENT_SPEAKER_STATUS:
+ switch (data) {
+ case 1:
+ speak_status = "All Clear";
+ break;
+ case 2:
+ speak_status = "Open Circuit";
+ break;
+ case 4:
+ speak_status = "Short Circuit";
+ break;
+ }
+
+ dev_info(cs35l45->dev, "MBOX event (SPEAKER_STATUS): %s\n",
+ speak_status);
+ break;
+ case EVENT_BOOT_DONE:
+ dev_dbg(cs35l45->dev, "MBOX event (BOOT_DONE)\n");
+ break;
+ default:
+ dev_err(cs35l45->dev, "MBOX event not supported %u\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static irqreturn_t cs35l45_dsp_virt2_mbox_cb(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+ unsigned int mbox_val;
+ int ret = 0;
+
+ ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_3, &mbox_val);
+ if (!ret && mbox_val)
+ ret = cs35l45_dsp_virt2_mbox3_irq_handle(cs35l45, mbox_val & CS35L45_MBOX3_CMD_MASK,
+ (mbox_val & CS35L45_MBOX3_DATA_MASK) >> CS35L45_MBOX3_DATA_SHIFT);
+
+ /* Handle DSP trace log IRQ */
+ ret = regmap_read(cs35l45->regmap, CS35L45_DSP_VIRT2_MBOX_4, &mbox_val);
+ if (!ret && mbox_val != 0) {
+ dev_err(cs35l45->dev, "Spurious DSP MBOX4 IRQ\n");
+ }
+
+ return IRQ_RETVAL(ret);
+}
+
+static irqreturn_t cs35l45_pll_unlock(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+
+ dev_dbg(cs35l45->dev, "PLL unlock detected!");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_pll_lock(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+
+ dev_dbg(cs35l45->dev, "PLL lock detected!");
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data);
+
+static const struct cs35l45_irq cs35l45_irqs[] = {
+ CS35L45_IRQ(AMP_SHORT_ERR, "Amplifier short error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(UVLO_VDDBATT_ERR, "VDDBATT undervoltage error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(BST_SHORT_ERR, "Boost inductor error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(BST_UVP_ERR, "Boost undervoltage error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(TEMP_ERR, "Overtemperature error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(AMP_CAL_ERR, "Amplifier calibration error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(UVLO_VDDLV_ERR, "LV threshold detector error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(GLOBAL_ERROR, "Global error", cs35l45_spk_safe_err),
+ CS35L45_IRQ(DSP_WDT_EXPIRE, "DSP Watchdog Timer", cs35l45_spk_safe_err),
+ CS35L45_IRQ(PLL_UNLOCK_FLAG_RISE, "PLL unlock", cs35l45_pll_unlock),
+ CS35L45_IRQ(PLL_LOCK_FLAG, "PLL lock", cs35l45_pll_lock),
+ CS35L45_IRQ(DSP_VIRT2_MBOX, "DSP virtual MBOX 2 write flag", cs35l45_dsp_virt2_mbox_cb),
+};
+
+static irqreturn_t cs35l45_spk_safe_err(int irq, void *data)
+{
+ struct cs35l45_private *cs35l45 = data;
+ int i;
+
+ i = irq - regmap_irq_get_virq(cs35l45->irq_data, 0);
+
+ dev_err(cs35l45->dev, "%s condition detected!\n", cs35l45_irqs[i].name);
+
+ return IRQ_HANDLED;
+}
+
+static const struct regmap_irq cs35l45_reg_irqs[] = {
+ CS35L45_REG_IRQ(IRQ1_EINT_1, AMP_SHORT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, UVLO_VDDBATT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, BST_SHORT_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, BST_UVP_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_1, TEMP_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, AMP_CAL_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_18, UVLO_VDDLV_ERR),
+ CS35L45_REG_IRQ(IRQ1_EINT_18, GLOBAL_ERROR),
+ CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_WDT_EXPIRE),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_UNLOCK_FLAG_RISE),
+ CS35L45_REG_IRQ(IRQ1_EINT_3, PLL_LOCK_FLAG),
+ CS35L45_REG_IRQ(IRQ1_EINT_2, DSP_VIRT2_MBOX),
+};
+
+static const struct regmap_irq_chip cs35l45_regmap_irq_chip = {
+ .name = "cs35l45 IRQ1 Controller",
+ .main_status = CS35L45_IRQ1_STATUS,
+ .status_base = CS35L45_IRQ1_EINT_1,
+ .mask_base = CS35L45_IRQ1_MASK_1,
+ .ack_base = CS35L45_IRQ1_EINT_1,
+ .num_regs = 18,
+ .irqs = cs35l45_reg_irqs,
+ .num_irqs = ARRAY_SIZE(cs35l45_reg_irqs),
+ .runtime_pm = true,
+};
+
static int cs35l45_initialize(struct cs35l45_private *cs35l45)
{
struct device *dev = cs35l45->dev;
@@ -593,18 +1100,68 @@ static int cs35l45_initialize(struct cs35l45_private *cs35l45)
if (ret < 0)
return ret;
- pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
- pm_runtime_use_autosuspend(cs35l45->dev);
- pm_runtime_set_active(cs35l45->dev);
- pm_runtime_enable(cs35l45->dev);
-
return 0;
}
+static const struct reg_sequence cs35l45_fs_errata_patch[] = {
+ {0x02B80080, 0x00000001},
+ {0x02B80088, 0x00000001},
+ {0x02B80090, 0x00000001},
+ {0x02B80098, 0x00000001},
+ {0x02B800A0, 0x00000001},
+ {0x02B800A8, 0x00000001},
+ {0x02B800B0, 0x00000001},
+ {0x02B800B8, 0x00000001},
+ {0x02B80280, 0x00000001},
+ {0x02B80288, 0x00000001},
+ {0x02B80290, 0x00000001},
+ {0x02B80298, 0x00000001},
+ {0x02B802A0, 0x00000001},
+ {0x02B802A8, 0x00000001},
+ {0x02B802B0, 0x00000001},
+ {0x02B802B8, 0x00000001},
+};
+
+static const struct cs_dsp_region cs35l45_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L45_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L45_DSP1_XMEM_PACK_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L45_DSP1_YMEM_PACK_0 },
+ {. type = WMFW_ADSP2_XM, .base = CS35L45_DSP1_XMEM_UNPACK24_0},
+ {. type = WMFW_ADSP2_YM, .base = CS35L45_DSP1_YMEM_UNPACK24_0},
+};
+
+static int cs35l45_dsp_init(struct cs35l45_private *cs35l45)
+{
+ struct wm_adsp *dsp = &cs35l45->dsp;
+ int ret;
+
+ dsp->part = "cs35l45";
+ dsp->fw = 9; /* 9 is WM_ADSP_FW_SPK_PROT in wm_adsp.c */
+ dsp->toggle_preload = true;
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->cs_dsp.dev = cs35l45->dev;
+ dsp->cs_dsp.regmap = cs35l45->regmap;
+ dsp->cs_dsp.base = CS35L45_DSP1_CLOCK_FREQ;
+ dsp->cs_dsp.base_sysinfo = CS35L45_DSP1_SYS_ID;
+ dsp->cs_dsp.mem = cs35l45_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l45_dsp1_regions);
+ dsp->cs_dsp.lock_regions = 0xFFFFFFFF;
+
+ ret = wm_halo_init(dsp);
+
+ regmap_multi_reg_write(cs35l45->regmap, cs35l45_fs_errata_patch,
+ ARRAY_SIZE(cs35l45_fs_errata_patch));
+
+ return ret;
+}
+
int cs35l45_probe(struct cs35l45_private *cs35l45)
{
struct device *dev = cs35l45->dev;
- int ret;
+ unsigned long irq_pol = IRQF_ONESHOT | IRQF_SHARED;
+ int ret, i, irq;
cs35l45->vdd_batt = devm_regulator_get(dev, "vdd-batt");
if (IS_ERR(cs35l45->vdd_batt))
@@ -649,14 +1206,63 @@ int cs35l45_probe(struct cs35l45_private *cs35l45)
if (ret < 0)
goto err_reset;
+ ret = cs35l45_dsp_init(cs35l45);
+ if (ret < 0)
+ goto err_reset;
+
+ pm_runtime_set_autosuspend_delay(cs35l45->dev, 3000);
+ pm_runtime_use_autosuspend(cs35l45->dev);
+ pm_runtime_mark_last_busy(cs35l45->dev);
+ pm_runtime_set_active(cs35l45->dev);
+ pm_runtime_get_noresume(cs35l45->dev);
+ pm_runtime_enable(cs35l45->dev);
+
+ if (cs35l45->irq) {
+ if (cs35l45->irq_invert)
+ irq_pol |= IRQF_TRIGGER_HIGH;
+ else
+ irq_pol |= IRQF_TRIGGER_LOW;
+
+ ret = devm_regmap_add_irq_chip(dev, cs35l45->regmap, cs35l45->irq, irq_pol, 0,
+ &cs35l45_regmap_irq_chip, &cs35l45->irq_data);
+ if (ret) {
+ dev_err(dev, "Failed to register IRQ chip: %d\n", ret);
+ goto err_dsp;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l45_irqs); i++) {
+ irq = regmap_irq_get_virq(cs35l45->irq_data, cs35l45_irqs[i].irq);
+ if (irq < 0) {
+ dev_err(dev, "Failed to get %s\n", cs35l45_irqs[i].name);
+ ret = irq;
+ goto err_dsp;
+ }
+
+ ret = devm_request_threaded_irq(dev, irq, NULL, cs35l45_irqs[i].handler,
+ irq_pol, cs35l45_irqs[i].name, cs35l45);
+ if (ret) {
+ dev_err(dev, "Failed to request IRQ %s: %d\n",
+ cs35l45_irqs[i].name, ret);
+ goto err_dsp;
+ }
+ }
+ }
+
ret = devm_snd_soc_register_component(dev, &cs35l45_component,
cs35l45_dai,
ARRAY_SIZE(cs35l45_dai));
if (ret < 0)
- goto err_reset;
+ goto err_dsp;
+
+ pm_runtime_put_autosuspend(cs35l45->dev);
return 0;
+err_dsp:
+ pm_runtime_disable(cs35l45->dev);
+ pm_runtime_put_noidle(cs35l45->dev);
+ wm_adsp2_remove(&cs35l45->dsp);
+
err_reset:
gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
err:
@@ -669,9 +1275,13 @@ EXPORT_SYMBOL_NS_GPL(cs35l45_probe, SND_SOC_CS35L45);
void cs35l45_remove(struct cs35l45_private *cs35l45)
{
+ pm_runtime_get_sync(cs35l45->dev);
pm_runtime_disable(cs35l45->dev);
+ wm_adsp2_remove(&cs35l45->dsp);
gpiod_set_value_cansleep(cs35l45->reset_gpio, 0);
+
+ pm_runtime_put_noidle(cs35l45->dev);
regulator_disable(cs35l45->vdd_a);
/* VDD_BATT must be the last to power-off */
regulator_disable(cs35l45->vdd_batt);
diff --git a/sound/soc/codecs/cs35l45.h b/sound/soc/codecs/cs35l45.h
index 53fe9d2b7b15..0da28439f628 100644
--- a/sound/soc/codecs/cs35l45.h
+++ b/sound/soc/codecs/cs35l45.h
@@ -14,6 +14,8 @@
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <dt-bindings/sound/cs35l45.h>
+#include "wm_adsp.h"
#define CS35L45_DEVID 0x00000000
#define CS35L45_REVID 0x00000004
@@ -24,6 +26,13 @@
#define CS35L45_BLOCK_ENABLES 0x00002018
#define CS35L45_BLOCK_ENABLES2 0x0000201C
#define CS35L45_ERROR_RELEASE 0x00002034
+#define CS35L45_SYNC_GPIO1 0x00002430
+#define CS35L45_INTB_GPIO2_MCLK_REF 0x00002434
+#define CS35L45_GPIO3 0x00002438
+#define CS35L45_PWRMGT_CTL 0x00002900
+#define CS35L45_WAKESRC_CTL 0x00002904
+#define CS35L45_WKI2C_CTL 0x00002908
+#define CS35L45_PWRMGT_STS 0x0000290C
#define CS35L45_REFCLK_INPUT 0x00002C04
#define CS35L45_GLOBAL_SAMPLE_RATE 0x00002C0C
#define CS35L45_BOOST_CCM_CFG 0x00003808
@@ -44,11 +53,105 @@
#define CS35L45_ASPTX3_INPUT 0x00004C28
#define CS35L45_ASPTX4_INPUT 0x00004C2C
#define CS35L45_ASPTX5_INPUT 0x00004C30
+#define CS35L45_DSP1RX1_INPUT 0x00004C40
+#define CS35L45_DSP1RX2_INPUT 0x00004C44
+#define CS35L45_DSP1RX3_INPUT 0x00004C48
+#define CS35L45_DSP1RX4_INPUT 0x00004C4C
+#define CS35L45_DSP1RX5_INPUT 0x00004C50
+#define CS35L45_DSP1RX6_INPUT 0x00004C54
+#define CS35L45_DSP1RX7_INPUT 0x00004C58
+#define CS35L45_DSP1RX8_INPUT 0x00004C5C
#define CS35L45_LDPM_CONFIG 0x00006404
#define CS35L45_AMP_PCM_CONTROL 0x00007000
#define CS35L45_AMP_PCM_HPF_TST 0x00007004
+#define CS35L45_IRQ1_CFG 0x0000E000
+#define CS35L45_IRQ1_STATUS 0x0000E004
+#define CS35L45_IRQ1_EINT_1 0x0000E010
+#define CS35L45_IRQ1_EINT_2 0x0000E014
+#define CS35L45_IRQ1_EINT_3 0x0000E018
#define CS35L45_IRQ1_EINT_4 0x0000E01C
-#define CS35L45_LASTREG 0x0000E01C
+#define CS35L45_IRQ1_EINT_5 0x0000E020
+#define CS35L45_IRQ1_EINT_7 0x0000E028
+#define CS35L45_IRQ1_EINT_8 0x0000E02C
+#define CS35L45_IRQ1_EINT_18 0x0000E054
+#define CS35L45_IRQ1_STS_1 0x0000E090
+#define CS35L45_IRQ1_STS_2 0x0000E094
+#define CS35L45_IRQ1_STS_3 0x0000E098
+#define CS35L45_IRQ1_STS_4 0x0000E09C
+#define CS35L45_IRQ1_STS_5 0x0000E0A0
+#define CS35L45_IRQ1_STS_7 0x0000E0A8
+#define CS35L45_IRQ1_STS_8 0x0000E0AC
+#define CS35L45_IRQ1_STS_18 0x0000E0D4
+#define CS35L45_IRQ1_MASK_1 0x0000E110
+#define CS35L45_IRQ1_MASK_2 0x0000E114
+#define CS35L45_IRQ1_MASK_3 0x0000E118
+#define CS35L45_IRQ1_MASK_4 0x0000E11C
+#define CS35L45_IRQ1_MASK_5 0x0000E120
+#define CS35L45_IRQ1_MASK_6 0x0000E124
+#define CS35L45_IRQ1_MASK_7 0x0000E128
+#define CS35L45_IRQ1_MASK_8 0x0000E12C
+#define CS35L45_IRQ1_MASK_9 0x0000E130
+#define CS35L45_IRQ1_MASK_10 0x0000E134
+#define CS35L45_IRQ1_MASK_11 0x0000E138
+#define CS35L45_IRQ1_MASK_12 0x0000E13C
+#define CS35L45_IRQ1_MASK_13 0x0000E140
+#define CS35L45_IRQ1_MASK_14 0x0000E144
+#define CS35L45_IRQ1_MASK_15 0x0000E148
+#define CS35L45_IRQ1_MASK_16 0x0000E14C
+#define CS35L45_IRQ1_MASK_17 0x0000E150
+#define CS35L45_IRQ1_MASK_18 0x0000E154
+#define CS35L45_GPIO_STATUS1 0x0000F000
+#define CS35L45_GPIO1_CTRL1 0x0000F008
+#define CS35L45_GPIO2_CTRL1 0x0000F00C
+#define CS35L45_GPIO3_CTRL1 0x0000F010
+#define CS35L45_DSP_MBOX_1 0x00011000
+#define CS35L45_DSP_MBOX_2 0x00011004
+#define CS35L45_DSP_VIRT1_MBOX_1 0x00011020
+#define CS35L45_DSP_VIRT1_MBOX_2 0x00011024
+#define CS35L45_DSP_VIRT1_MBOX_3 0x00011028
+#define CS35L45_DSP_VIRT1_MBOX_4 0x0001102C
+#define CS35L45_DSP_VIRT2_MBOX_1 0x00011040
+#define CS35L45_DSP_VIRT2_MBOX_2 0x00011044
+#define CS35L45_DSP_VIRT2_MBOX_3 0x00011048
+#define CS35L45_DSP_VIRT2_MBOX_4 0x0001104C
+#define CS35L45_DSP1_XMEM_PACK_0 0x02000000
+#define CS35L45_DSP1_XMEM_PACK_4607 0x020047FC
+#define CS35L45_DSP1_XMEM_UNPACK32_0 0x02400000
+#define CS35L45_DSP1_XMEM_UNPACK32_3071 0x02402FFC
+#define CS35L45_DSP1_SYS_ID 0x025E0000
+#define CS35L45_DSP1_XMEM_UNPACK24_0 0x02800000
+#define CS35L45_DSP1_XMEM_UNPACK24_6143 0x02805FFC
+#define CS35L45_DSP1_CLOCK_FREQ 0x02B80000
+#define CS35L45_DSP1_RX1_RATE 0x02B80080
+#define CS35L45_DSP1_RX2_RATE 0x02B80088
+#define CS35L45_DSP1_RX3_RATE 0x02B80090
+#define CS35L45_DSP1_RX4_RATE 0x02B80098
+#define CS35L45_DSP1_RX5_RATE 0x02B800A0
+#define CS35L45_DSP1_RX6_RATE 0x02B800A8
+#define CS35L45_DSP1_RX7_RATE 0x02B800B0
+#define CS35L45_DSP1_RX8_RATE 0x02B800B8
+#define CS35L45_DSP1_TX1_RATE 0x02B80280
+#define CS35L45_DSP1_TX2_RATE 0x02B80288
+#define CS35L45_DSP1_TX3_RATE 0x02B80290
+#define CS35L45_DSP1_TX4_RATE 0x02B80298
+#define CS35L45_DSP1_TX5_RATE 0x02B802A0
+#define CS35L45_DSP1_TX6_RATE 0x02B802A8
+#define CS35L45_DSP1_TX7_RATE 0x02B802B0
+#define CS35L45_DSP1_TX8_RATE 0x02B802B8
+#define CS35L45_DSP1_SCRATCH1 0x02B805C0
+#define CS35L45_DSP1_SCRATCH2 0x02B805C8
+#define CS35L45_DSP1_SCRATCH3 0x02B805D0
+#define CS35L45_DSP1_SCRATCH4 0x02B805D8
+#define CS35L45_DSP1_CCM_CORE_CONTROL 0x02BC1000
+#define CS35L45_DSP1_YMEM_PACK_0 0x02C00000
+#define CS35L45_DSP1_YMEM_PACK_1532 0x02C017F0
+#define CS35L45_DSP1_YMEM_UNPACK32_0 0x03000000
+#define CS35L45_DSP1_YMEM_UNPACK32_1022 0x03000FF8
+#define CS35L45_DSP1_YMEM_UNPACK24_0 0x03400000
+#define CS35L45_DSP1_YMEM_UNPACK24_2043 0x03401FEC
+#define CS35L45_DSP1_PMEM_0 0x03800000
+#define CS35L45_DSP1_PMEM_3834 0x03803BE8
+#define CS35L45_LASTREG 0x03C6EFE8
/* SFT_RESET */
#define CS35L45_SOFT_RESET_TRIGGER 0x5A000000
@@ -70,9 +173,20 @@
/* BLOCK_ENABLES2 */
#define CS35L45_ASP_EN_SHIFT 27
+#define CS35L45_MEM_RDY_SHIFT 1
+#define CS35L45_MEM_RDY_MASK BIT(1)
+
/* ERROR_RELEASE */
#define CS35L45_GLOBAL_ERR_RLS_MASK BIT(11)
+/* CCM_CORE */
+#define CS35L45_CCM_CORE_RESET_SHIFT 9
+#define CS35L45_CCM_CORE_RESET_MASK BIT(9)
+#define CS35L45_CCM_PM_REMAP_SHIFT 7
+#define CS35L45_CCM_PM_REMAP_MASK BIT(7)
+#define CS35L45_CCM_CORE_EN_SHIFT 0
+#define CS35L45_CCM_CORE_EN_MASK BIT(0)
+
/* REFCLK_INPUT */
#define CS35L45_PLL_FORCE_EN_SHIFT 16
#define CS35L45_PLL_FORCE_EN_MASK BIT(16)
@@ -165,6 +279,56 @@
#define CS35L45_OTP_BOOT_DONE_STS_MASK BIT(1)
#define CS35L45_OTP_BUSY_MASK BIT(0)
+/* GPIOX_CTRL1 */
+#define CS35L45_GPIO_DIR_SHIFT 31
+#define CS35L45_GPIO_DIR_MASK BIT(31)
+#define CS35L45_GPIO_LVL_SHIFT 15
+#define CS35L45_GPIO_LVL_MASK BIT(15)
+#define CS35L45_GPIO_OP_CFG_SHIFT 14
+#define CS35L45_GPIO_OP_CFG_MASK BIT(14)
+#define CS35L45_GPIO_POL_SHIFT 12
+#define CS35L45_GPIO_POL_MASK BIT(12)
+
+/* SYNC_GPIO1, INTB_GPIO2_MCLK_REF, GPIO3 */
+#define CS35L45_GPIO_CTRL_SHIFT 20
+#define CS35L45_GPIO_CTRL_MASK GENMASK(22, 20)
+#define CS35L45_GPIO_INVERT_SHIFT 19
+#define CS35L45_GPIO_INVERT_MASK BIT(19)
+
+/* CS35L45_IRQ1_EINT_1 */
+#define CS35L45_BST_UVP_ERR_SHIFT 7
+#define CS35L45_BST_UVP_ERR_MASK BIT(7)
+#define CS35L45_BST_SHORT_ERR_SHIFT 8
+#define CS35L45_BST_SHORT_ERR_MASK BIT(8)
+#define CS35L45_TEMP_ERR_SHIFT 17
+#define CS35L45_TEMP_ERR_MASK BIT(17)
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_SHIFT 22
+#define CS35L45_MSM_GLOBAL_EN_ASSERT_MASK BIT(22)
+#define CS35L45_UVLO_VDDBATT_ERR_SHIFT 29
+#define CS35L45_UVLO_VDDBATT_ERR_MASK BIT(29)
+#define CS35L45_AMP_SHORT_ERR_SHIFT 31
+#define CS35L45_AMP_SHORT_ERR_MASK BIT(31)
+
+/* CS35L45_IRQ1_EINT_2 */
+#define CS35L45_DSP_WDT_EXPIRE_SHIFT 4
+#define CS35L45_DSP_WDT_EXPIRE_MASK BIT(4)
+#define CS35L45_DSP_VIRT2_MBOX_SHIFT 21
+#define CS35L45_DSP_VIRT2_MBOX_MASK BIT(21)
+
+/* CS35L45_IRQ1_EINT_3 */
+#define CS35L45_PLL_LOCK_FLAG_SHIFT 1
+#define CS35L45_PLL_LOCK_FLAG_MASK BIT(1)
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_SHIFT 4
+#define CS35L45_PLL_UNLOCK_FLAG_RISE_MASK BIT(4)
+#define CS35L45_AMP_CAL_ERR_SHIFT 25
+#define CS35L45_AMP_CAL_ERR_MASK BIT(25)
+
+/* CS35L45_IRQ1_EINT_18 */
+#define CS35L45_GLOBAL_ERROR_SHIFT 15
+#define CS35L45_GLOBAL_ERROR_MASK BIT(15)
+#define CS35L45_UVLO_VDDLV_ERR_SHIFT 16
+#define CS35L45_UVLO_VDDLV_ERR_MASK BIT(16)
+
/* Mixer sources */
#define CS35L45_PCM_SRC_MASK 0x7F
#define CS35L45_PCM_SRC_ZERO 0x00
@@ -176,6 +340,8 @@
#define CS35L45_PCM_SRC_CLASSH_TGT 0x21
#define CS35L45_PCM_SRC_VDD_BATTMON 0x28
#define CS35L45_PCM_SRC_VDD_BSTMON 0x29
+#define CS35L45_PCM_SRC_DSP_TX1 0x32
+#define CS35L45_PCM_SRC_DSP_TX2 0x33
#define CS35L45_PCM_SRC_TEMPMON 0x3A
#define CS35L45_PCM_SRC_INTERPOLATOR 0x40
#define CS35L45_PCM_SRC_IL_TARGET 0x48
@@ -185,6 +351,51 @@
#define CS35L45_POST_GLOBAL_EN_US 5000
#define CS35L45_PRE_GLOBAL_DIS_US 3000
+/* WAKESRC_CTL */
+#define CS35L45_WKSRC_SYNC_GPIO1 BIT(0)
+#define CS35L45_WKSRC_INT_GPIO2 BIT(1)
+#define CS35L45_WKSRC_GPIO3 BIT(2)
+#define CS35L45_WKSRC_SPI BIT(3)
+#define CS35L45_WKSRC_I2C BIT(4)
+#define CS35L45_UPDT_WKCTL_SHIFT 15
+#define CS35L45_UPDT_WKCTL_MASK BIT(15)
+#define CS35L45_WKSRC_EN_SHIFT 8
+#define CS35L45_WKSRC_EN_MASK GENMASK(12, 8)
+#define CS35L45_WKSRC_POL_SHIFT 0
+#define CS35L45_WKSRC_POL_MASK GENMASK(3, 0)
+
+/* WAKEI2C_CTL */
+#define CS35L45_UPDT_WKI2C_SHIFT 15
+#define CS35L45_UPDT_WKI2C_MASK BIT(15)
+#define CS35L45_WKI2C_ADDR_SHIFT 0
+#define CS35L45_WKI2C_ADDR_MASK GENMASK(6, 0)
+
+#define CS35L45_SPI_MAX_FREQ 4000000
+
+enum cs35l45_cspl_mboxstate {
+ CSPL_MBOX_STS_RUNNING = 0,
+ CSPL_MBOX_STS_PAUSED = 1,
+ CSPL_MBOX_STS_RDY_FOR_REINIT = 2,
+ CSPL_MBOX_STS_HIBERNATE = 3,
+};
+
+enum cs35l45_cspl_mboxcmd {
+ CSPL_MBOX_CMD_NONE = 0,
+ CSPL_MBOX_CMD_PAUSE = 1,
+ CSPL_MBOX_CMD_RESUME = 2,
+ CSPL_MBOX_CMD_REINIT = 3,
+ CSPL_MBOX_CMD_STOP_PRE_REINIT = 4,
+ CSPL_MBOX_CMD_HIBERNATE = 5,
+ CSPL_MBOX_CMD_OUT_OF_HIBERNATE = 6,
+ CSPL_MBOX_CMD_UNKNOWN_CMD = -1,
+ CSPL_MBOX_CMD_INVALID_SEQUENCE = -2,
+};
+
+enum control_bus_type {
+ CONTROL_BUS_I2C = 0,
+ CONTROL_BUS_SPI = 1,
+};
+
#define CS35L45_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_3LE| \
SNDRV_PCM_FMTBIT_S24_LE)
@@ -194,7 +405,56 @@
SNDRV_PCM_RATE_88200 | \
SNDRV_PCM_RATE_96000)
+/*
+ * IRQs
+ */
+#define CS35L45_IRQ(_irq, _name, _hand) \
+ { \
+ .irq = CS35L45_ ## _irq ## _IRQ,\
+ .name = _name, \
+ .handler = _hand, \
+ }
+
+struct cs35l45_irq {
+ int irq;
+ const char *name;
+ irqreturn_t (*handler)(int irq, void *data);
+};
+
+#define CS35L45_REG_IRQ(_reg, _irq) \
+ [CS35L45_ ## _irq ## _IRQ] = { \
+ .reg_offset = (CS35L45_ ## _reg) - CS35L45_IRQ1_EINT_1, \
+ .mask = CS35L45_ ## _irq ## _MASK \
+ }
+
+enum cs35l45_irq_list {
+ CS35L45_AMP_SHORT_ERR_IRQ,
+ CS35L45_UVLO_VDDBATT_ERR_IRQ,
+ CS35L45_BST_SHORT_ERR_IRQ,
+ CS35L45_BST_UVP_ERR_IRQ,
+ CS35L45_TEMP_ERR_IRQ,
+ CS35L45_AMP_CAL_ERR_IRQ,
+ CS35L45_UVLO_VDDLV_ERR_IRQ,
+ CS35L45_GLOBAL_ERROR_IRQ,
+ CS35L45_DSP_WDT_EXPIRE_IRQ,
+ CS35L45_PLL_UNLOCK_FLAG_RISE_IRQ,
+ CS35L45_PLL_LOCK_FLAG_IRQ,
+ CS35L45_DSP_VIRT2_MBOX_IRQ,
+ CS35L45_NUM_IRQ
+};
+
+#define CS35L45_MBOX3_CMD_MASK 0xFF
+#define CS35L45_MBOX3_CMD_SHIFT 0
+#define CS35L45_MBOX3_DATA_MASK 0xFFFFFF00
+#define CS35L45_MBOX3_DATA_SHIFT 8
+
+enum mbox3_events {
+ EVENT_SPEAKER_STATUS = 0x66,
+ EVENT_BOOT_DONE = 0x67,
+};
+
struct cs35l45_private {
+ struct wm_adsp dsp; /* needs to be first member */
struct device *dev;
struct regmap *regmap;
struct gpio_desc *reset_gpio;
@@ -204,6 +464,11 @@ struct cs35l45_private {
bool sysclk_set;
u8 slot_width;
u8 slot_count;
+ int irq_invert;
+ int irq;
+ unsigned int i2c_addr;
+ enum control_bus_type bus_type;
+ struct regmap_irq_chip_data *irq_data;
};
extern const struct dev_pm_ops cs35l45_pm_ops;
diff --git a/sound/soc/codecs/cs35l56-i2c.c b/sound/soc/codecs/cs35l56-i2c.c
new file mode 100644
index 000000000000..295caad26224
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-i2c.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver I2C binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_i2c_probe(struct i2c_client *client)
+{
+ struct cs35l56_private *cs35l56;
+ struct device *dev = &client->dev;
+ const struct regmap_config *regmap_config = &cs35l56_regmap_i2c;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->dev = dev;
+ cs35l56->can_hibernate = true;
+
+ i2c_set_clientdata(client, cs35l56);
+ cs35l56->regmap = devm_regmap_init_i2c(client, regmap_config);
+ if (IS_ERR(cs35l56->regmap)) {
+ ret = PTR_ERR(cs35l56->regmap);
+ return dev_err_probe(cs35l56->dev, ret, "Failed to allocate register map\n");
+ }
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret == 0)
+ ret = cs35l56_irq_request(cs35l56, client->irq);
+ if (ret < 0)
+ cs35l56_remove(cs35l56);
+
+ return ret;
+}
+
+static void cs35l56_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l56_private *cs35l56 = i2c_get_clientdata(client);
+
+ cs35l56_remove(cs35l56);
+}
+
+static const struct i2c_device_id cs35l56_id_i2c[] = {
+ { "cs35l56", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l56_id_i2c);
+
+static struct i2c_driver cs35l56_i2c_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = &cs35l56_pm_ops_i2c_spi,
+ },
+ .id_table = cs35l56_id_i2c,
+ .probe_new = cs35l56_i2c_probe,
+ .remove = cs35l56_i2c_remove,
+};
+
+module_i2c_driver(cs35l56_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 I2C driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c
new file mode 100644
index 000000000000..2cde78605ba9
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-sdw.c
@@ -0,0 +1,566 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SoundWire binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/swab.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+
+#include "cs35l56.h"
+
+/* Register addresses are offset when sent over SoundWire */
+#define CS35L56_SDW_ADDR_OFFSET 0x8000
+
+static int cs35l56_sdw_read_one(struct sdw_slave *peripheral, unsigned int reg, void *buf)
+{
+ int ret;
+
+ ret = sdw_nread_no_pm(peripheral, reg, 4, (u8 *)buf);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Read failed @%#x:%d\n", reg, ret);
+ return ret;
+ }
+
+ swab32s((u32 *)buf);
+
+ return 0;
+}
+
+static int cs35l56_sdw_read(void *context, const void *reg_buf,
+ const size_t reg_size, void *val_buf,
+ size_t val_size)
+{
+ struct sdw_slave *peripheral = context;
+ u8 *buf8 = val_buf;
+ unsigned int reg, bytes;
+ int ret;
+
+ reg = le32_to_cpu(*(const __le32 *)reg_buf);
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ if (val_size == 4)
+ return cs35l56_sdw_read_one(peripheral, reg, val_buf);
+
+ while (val_size) {
+ bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+ if (bytes > val_size)
+ bytes = val_size;
+
+ ret = sdw_nread_no_pm(peripheral, reg, bytes, buf8);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Read failed @%#x..%#x:%d\n",
+ reg, reg + bytes - 1, ret);
+ return ret;
+ }
+
+ swab32_array((u32 *)buf8, bytes / 4);
+ val_size -= bytes;
+ reg += bytes;
+ buf8 += bytes;
+ }
+
+ return 0;
+}
+
+static inline void cs35l56_swab_copy(void *dest, const void *src, size_t nbytes)
+{
+ u32 *dest32 = dest;
+ const u32 *src32 = src;
+
+ for (; nbytes > 0; nbytes -= 4)
+ *dest32++ = swab32(*src32++);
+}
+
+static int cs35l56_sdw_write_one(struct sdw_slave *peripheral, unsigned int reg, const void *buf)
+{
+ u32 val_le = swab32(*(u32 *)buf);
+ int ret;
+
+ ret = sdw_nwrite_no_pm(peripheral, reg, 4, (u8 *)&val_le);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Write failed @%#x:%d\n", reg, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_gather_write(void *context,
+ const void *reg_buf, size_t reg_size,
+ const void *val_buf, size_t val_size)
+{
+ struct sdw_slave *peripheral = context;
+ const u8 *src_be = val_buf;
+ u32 val_le_buf[64]; /* Define u32 so it is 32-bit aligned */
+ unsigned int reg, bytes;
+ int ret;
+
+ reg = le32_to_cpu(*(const __le32 *)reg_buf);
+ reg += CS35L56_SDW_ADDR_OFFSET;
+
+ if (val_size == 4)
+ return cs35l56_sdw_write_one(peripheral, reg, src_be);
+
+ while (val_size) {
+ bytes = SDW_REG_NO_PAGE - (reg & SDW_REGADDR); /* to end of page */
+ if (bytes > val_size)
+ bytes = val_size;
+ if (bytes > sizeof(val_le_buf))
+ bytes = sizeof(val_le_buf);
+
+ cs35l56_swab_copy(val_le_buf, src_be, bytes);
+
+ ret = sdw_nwrite_no_pm(peripheral, reg, bytes, (u8 *)val_le_buf);
+ if (ret != 0) {
+ dev_err(&peripheral->dev, "Write failed @%#x..%#x:%d\n",
+ reg, reg + bytes - 1, ret);
+ return ret;
+ }
+
+ val_size -= bytes;
+ reg += bytes;
+ src_be += bytes;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_write(void *context, const void *val_buf, size_t val_size)
+{
+ const u8 *src_buf = val_buf;
+
+ /* First word of val_buf contains the destination address */
+ return cs35l56_sdw_gather_write(context, &src_buf[0], 4, &src_buf[4], val_size - 4);
+}
+
+/*
+ * Registers are big-endian on I2C and SPI but little-endian on SoundWire.
+ * Exported firmware controls are big-endian on I2C/SPI but little-endian on
+ * SoundWire. Firmware files are always big-endian and are opaque blobs.
+ * Present a big-endian regmap and hide the endianness swap, so that the ALSA
+ * byte controls always have the same byte order, and firmware file blobs
+ * can be written verbatim.
+ */
+static const struct regmap_bus cs35l56_regmap_bus_sdw = {
+ .read = cs35l56_sdw_read,
+ .write = cs35l56_sdw_write,
+ .gather_write = cs35l56_sdw_gather_write,
+ .reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static void cs35l56_sdw_init(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ int ret;
+
+ pm_runtime_get_noresume(cs35l56->dev);
+
+ regcache_cache_only(cs35l56->regmap, false);
+
+ ret = cs35l56_init(cs35l56);
+ if (ret < 0) {
+ regcache_cache_only(cs35l56->regmap, true);
+ goto out;
+ }
+
+ /*
+ * cs35l56_init can return with !init_done if it triggered
+ * a soft reset.
+ */
+ if (cs35l56->init_done) {
+ /* Enable SoundWire interrupts */
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ }
+
+out:
+ pm_runtime_mark_last_busy(cs35l56->dev);
+ pm_runtime_put_autosuspend(cs35l56->dev);
+}
+
+static int cs35l56_sdw_interrupt(struct sdw_slave *peripheral,
+ struct sdw_slave_intr_status *status)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ /* SoundWire core holds our pm_runtime when calling this function. */
+
+ dev_dbg(cs35l56->dev, "int control_port=%#x\n", status->control_port);
+
+ if ((status->control_port & SDW_SCP_INT1_IMPL_DEF) == 0)
+ return 0;
+
+ /*
+ * Prevent bus manager suspending and possibly issuing a
+ * bus-reset before the queued work has run.
+ */
+ pm_runtime_get_noresume(cs35l56->dev);
+
+ /*
+ * Mask and clear until it has been handled. The read of GEN_INT_STAT_1
+ * is required as per the SoundWire spec for interrupt status bits
+ * to clear. GEN_INT_MASK_1 masks the _inputs_ to GEN_INT_STAT1.
+ * None of the interrupts are time-critical so use the
+ * power-efficient queue.
+ */
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ queue_work(system_power_efficient_wq, &cs35l56->sdw_irq_work);
+
+ return 0;
+}
+
+static void cs35l56_sdw_irq_work(struct work_struct *work)
+{
+ struct cs35l56_private *cs35l56 = container_of(work,
+ struct cs35l56_private,
+ sdw_irq_work);
+
+ cs35l56_irq(-1, cs35l56);
+
+ /* unmask interrupts */
+ if (!cs35l56->sdw_irq_no_unmask)
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+ pm_runtime_put_autosuspend(cs35l56->dev);
+}
+
+static int cs35l56_sdw_read_prop(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ struct sdw_slave_prop *prop = &peripheral->prop;
+ struct sdw_dpn_prop *ports;
+
+ ports = devm_kcalloc(cs35l56->dev, 2, sizeof(*ports), GFP_KERNEL);
+ if (!ports)
+ return -ENOMEM;
+
+ prop->source_ports = BIT(CS35L56_SDW1_CAPTURE_PORT);
+ prop->sink_ports = BIT(CS35L56_SDW1_PLAYBACK_PORT);
+ prop->paging_support = true;
+ prop->clk_stop_mode1 = false;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY | SDW_SCP_INT1_IMPL_DEF;
+
+ /* DP1 - playback */
+ ports[0].num = CS35L56_SDW1_PLAYBACK_PORT;
+ ports[0].type = SDW_DPN_FULL;
+ ports[0].ch_prep_timeout = 10;
+ prop->sink_dpn_prop = &ports[0];
+
+ /* DP3 - capture */
+ ports[1].num = CS35L56_SDW1_CAPTURE_PORT;
+ ports[1].type = SDW_DPN_FULL;
+ ports[1].ch_prep_timeout = 10;
+ prop->src_dpn_prop = &ports[1];
+
+ return 0;
+}
+
+static int cs35l56_sdw_update_status(struct sdw_slave *peripheral,
+ enum sdw_slave_status status)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ switch (status) {
+ case SDW_SLAVE_ATTACHED:
+ dev_dbg(cs35l56->dev, "%s: ATTACHED\n", __func__);
+ if (cs35l56->sdw_attached)
+ break;
+
+ if (!cs35l56->init_done || cs35l56->soft_resetting)
+ cs35l56_sdw_init(peripheral);
+
+ cs35l56->sdw_attached = true;
+ break;
+ case SDW_SLAVE_UNATTACHED:
+ dev_dbg(cs35l56->dev, "%s: UNATTACHED\n", __func__);
+ cs35l56->sdw_attached = false;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs35l56_a1_kick_divider(struct cs35l56_private *cs35l56,
+ struct sdw_slave *peripheral)
+{
+ unsigned int curr_scale_reg, next_scale_reg;
+ int curr_scale, next_scale, ret;
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ if (peripheral->bus->params.curr_bank) {
+ curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+ next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+ } else {
+ curr_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B0;
+ next_scale_reg = SDW_SCP_BUSCLOCK_SCALE_B1;
+ }
+
+ /*
+ * Current clock scale value must be different to new value.
+ * Modify current to guarantee this. If next still has the dummy
+ * value we wrote when it was current, the core code has not set
+ * a new scale so restore its original good value
+ */
+ curr_scale = sdw_read_no_pm(peripheral, curr_scale_reg);
+ if (curr_scale < 0) {
+ dev_err(cs35l56->dev, "Failed to read current clock scale: %d\n", curr_scale);
+ return curr_scale;
+ }
+
+ next_scale = sdw_read_no_pm(peripheral, next_scale_reg);
+ if (next_scale < 0) {
+ dev_err(cs35l56->dev, "Failed to read next clock scale: %d\n", next_scale);
+ return next_scale;
+ }
+
+ if (next_scale == CS35L56_SDW_INVALID_BUS_SCALE) {
+ next_scale = cs35l56->old_sdw_clock_scale;
+ ret = sdw_write_no_pm(peripheral, next_scale_reg, next_scale);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+ return ret;
+ }
+ }
+
+ cs35l56->old_sdw_clock_scale = curr_scale;
+ ret = sdw_write_no_pm(peripheral, curr_scale_reg, CS35L56_SDW_INVALID_BUS_SCALE);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Failed to modify current clock scale: %d\n", ret);
+ return ret;
+ }
+
+ dev_dbg(cs35l56->dev, "Next bus scale: %#x\n", next_scale);
+
+ return 0;
+}
+
+static int cs35l56_sdw_bus_config(struct sdw_slave *peripheral,
+ struct sdw_bus_params *params)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+ int sclk;
+
+ sclk = params->curr_dr_freq / 2;
+ dev_dbg(cs35l56->dev, "%s: sclk=%u c=%u r=%u\n", __func__, sclk, params->col, params->row);
+
+ if (cs35l56->rev < 0xb0)
+ return cs35l56_a1_kick_divider(cs35l56, peripheral);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_clk_stop(struct sdw_slave *peripheral,
+ enum sdw_clk_stop_mode mode,
+ enum sdw_clk_stop_type type)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ dev_dbg(cs35l56->dev, "%s: mode:%d type:%d\n", __func__, mode, type);
+
+ return 0;
+}
+
+static const struct sdw_slave_ops cs35l56_sdw_ops = {
+ .read_prop = cs35l56_sdw_read_prop,
+ .interrupt_callback = cs35l56_sdw_interrupt,
+ .update_status = cs35l56_sdw_update_status,
+ .bus_config = cs35l56_sdw_bus_config,
+#ifdef DEBUG
+ .clk_stop = cs35l56_sdw_clk_stop,
+#endif
+};
+
+static int __maybe_unused cs35l56_sdw_handle_unattach(struct cs35l56_private *cs35l56)
+{
+ struct sdw_slave *peripheral = cs35l56->sdw_peripheral;
+
+ if (peripheral->unattach_request) {
+ /* Cannot access registers until bus is re-initialized. */
+ dev_dbg(cs35l56->dev, "Wait for initialization_complete\n");
+ if (!wait_for_completion_timeout(&peripheral->initialization_complete,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->dev, "initialization_complete timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ peripheral->unattach_request = 0;
+
+ /*
+ * Don't call regcache_mark_dirty(), we can't be sure that the
+ * Manager really did issue a Bus Reset.
+ */
+ }
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ return cs35l56_runtime_suspend(dev);
+}
+
+static int __maybe_unused cs35l56_sdw_runtime_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "Runtime resume\n");
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ ret = cs35l56_sdw_handle_unattach(cs35l56);
+ if (ret < 0)
+ return ret;
+
+ ret = cs35l56_runtime_resume_common(cs35l56);
+ if (ret)
+ return ret;
+
+ /* Re-enable SoundWire interrupts */
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l56_sdw_system_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ /*
+ * Disable SoundWire interrupts.
+ * Flush - don't cancel because that could leave an unbalanced pm_runtime_get.
+ */
+ cs35l56->sdw_irq_no_unmask = true;
+ flush_work(&cs35l56->sdw_irq_work);
+
+ /* Mask interrupts and flush in case sdw_irq_work was queued again */
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ flush_work(&cs35l56->sdw_irq_work);
+
+ return cs35l56_system_suspend(dev);
+}
+
+static int __maybe_unused cs35l56_sdw_system_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ cs35l56->sdw_irq_no_unmask = false;
+ /* runtime_resume re-enables the interrupt */
+
+ return cs35l56_system_resume(dev);
+}
+
+static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_device_id *id)
+{
+ struct device *dev = &peripheral->dev;
+ struct cs35l56_private *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(dev, sizeof(*cs35l56), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ cs35l56->dev = dev;
+ cs35l56->sdw_peripheral = peripheral;
+ INIT_WORK(&cs35l56->sdw_irq_work, cs35l56_sdw_irq_work);
+
+ dev_set_drvdata(dev, cs35l56);
+
+ cs35l56->regmap = devm_regmap_init(dev, &cs35l56_regmap_bus_sdw,
+ peripheral, &cs35l56_regmap_sdw);
+ if (IS_ERR(cs35l56->regmap)) {
+ ret = PTR_ERR(cs35l56->regmap);
+ return dev_err_probe(dev, ret, "Failed to allocate register map\n");
+ }
+
+ /* Start in cache-only until device is enumerated */
+ regcache_cache_only(cs35l56->regmap, true);
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ return 0;
+}
+
+static int cs35l56_sdw_remove(struct sdw_slave *peripheral)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev);
+
+ /* Disable SoundWire interrupts */
+ cs35l56->sdw_irq_no_unmask = true;
+ cancel_work_sync(&cs35l56->sdw_irq_work);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+
+ cs35l56_remove(cs35l56);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l56_sdw_pm = {
+ SET_RUNTIME_PM_OPS(cs35l56_sdw_runtime_suspend, cs35l56_sdw_runtime_resume, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_sdw_system_suspend, cs35l56_sdw_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+ /* NOIRQ stage not needed, SoundWire doesn't use a hard IRQ */
+};
+
+static const struct sdw_device_id cs35l56_sdw_id[] = {
+ SDW_SLAVE_ENTRY(0x01FA, 0x3556, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, cs35l56_sdw_id);
+
+static struct sdw_driver cs35l56_sdw_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = &cs35l56_sdw_pm,
+ },
+ .probe = cs35l56_sdw_probe,
+ .remove = cs35l56_sdw_remove,
+ .ops = &cs35l56_sdw_ops,
+ .id_table = cs35l56_sdw_id,
+};
+
+module_sdw_driver(cs35l56_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SoundWire driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-shared.c b/sound/soc/codecs/cs35l56-shared.c
new file mode 100644
index 000000000000..60da8c75b7b9
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-shared.c
@@ -0,0 +1,362 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Components shared between ASoC and HDA CS35L56 drivers
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static const struct reg_default cs35l56_reg_defaults[] = {
+ { CS35L56_ASP1_ENABLES1, 0x00000000 },
+ { CS35L56_ASP1_CONTROL1, 0x00000028 },
+ { CS35L56_ASP1_CONTROL2, 0x18180200 },
+ { CS35L56_ASP1_CONTROL3, 0x00000002 },
+ { CS35L56_ASP1_FRAME_CONTROL1, 0x03020100 },
+ { CS35L56_ASP1_FRAME_CONTROL5, 0x00020100 },
+ { CS35L56_ASP1_DATA_CONTROL1, 0x00000018 },
+ { CS35L56_ASP1_DATA_CONTROL5, 0x00000018 },
+ { CS35L56_ASP1TX1_INPUT, 0x00000018 },
+ { CS35L56_ASP1TX2_INPUT, 0x00000019 },
+ { CS35L56_ASP1TX3_INPUT, 0x00000020 },
+ { CS35L56_ASP1TX4_INPUT, 0x00000028 },
+ { CS35L56_SWIRE_DP3_CH1_INPUT, 0x00000018 },
+ { CS35L56_SWIRE_DP3_CH2_INPUT, 0x00000019 },
+ { CS35L56_SWIRE_DP3_CH3_INPUT, 0x00000029 },
+ { CS35L56_SWIRE_DP3_CH4_INPUT, 0x00000028 },
+ { CS35L56_IRQ1_CFG, 0x00000000 },
+ { CS35L56_IRQ1_MASK_1, 0x83ffffff },
+ { CS35L56_IRQ1_MASK_2, 0xffff7fff },
+ { CS35L56_IRQ1_MASK_4, 0xe0ffffff },
+ { CS35L56_IRQ1_MASK_8, 0xfc000fff },
+ { CS35L56_IRQ1_MASK_18, 0x1f7df0ff },
+ { CS35L56_IRQ1_MASK_20, 0x15c00000 },
+ /* CS35L56_MAIN_RENDER_USER_MUTE - soft register, no default */
+ /* CS35L56_MAIN_RENDER_USER_VOLUME - soft register, no default */
+ /* CS35L56_MAIN_POSTURE_NUMBER - soft register, no default */
+};
+
+static bool cs35l56_is_dsp_memory(unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+ case CS35L56_DSP1_XMEM_UNPACKED32_0 ... CS35L56_DSP1_XMEM_UNPACKED32_4095:
+ case CS35L56_DSP1_XMEM_UNPACKED24_0 ... CS35L56_DSP1_XMEM_UNPACKED24_8191:
+ case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+ case CS35L56_DSP1_YMEM_UNPACKED32_0 ... CS35L56_DSP1_YMEM_UNPACKED32_3070:
+ case CS35L56_DSP1_YMEM_UNPACKED24_0 ... CS35L56_DSP1_YMEM_UNPACKED24_6141:
+ case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l56_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DEVID:
+ case CS35L56_REVID:
+ case CS35L56_RELID:
+ case CS35L56_OTPID:
+ case CS35L56_SFT_RESET:
+ case CS35L56_GLOBAL_ENABLES:
+ case CS35L56_BLOCK_ENABLES:
+ case CS35L56_BLOCK_ENABLES2:
+ case CS35L56_REFCLK_INPUT:
+ case CS35L56_GLOBAL_SAMPLE_RATE:
+ case CS35L56_ASP1_ENABLES1:
+ case CS35L56_ASP1_CONTROL1:
+ case CS35L56_ASP1_CONTROL2:
+ case CS35L56_ASP1_CONTROL3:
+ case CS35L56_ASP1_FRAME_CONTROL1:
+ case CS35L56_ASP1_FRAME_CONTROL5:
+ case CS35L56_ASP1_DATA_CONTROL1:
+ case CS35L56_ASP1_DATA_CONTROL5:
+ case CS35L56_DACPCM1_INPUT:
+ case CS35L56_DACPCM2_INPUT:
+ case CS35L56_ASP1TX1_INPUT:
+ case CS35L56_ASP1TX2_INPUT:
+ case CS35L56_ASP1TX3_INPUT:
+ case CS35L56_ASP1TX4_INPUT:
+ case CS35L56_DSP1RX1_INPUT:
+ case CS35L56_DSP1RX2_INPUT:
+ case CS35L56_SWIRE_DP3_CH1_INPUT:
+ case CS35L56_SWIRE_DP3_CH2_INPUT:
+ case CS35L56_SWIRE_DP3_CH3_INPUT:
+ case CS35L56_SWIRE_DP3_CH4_INPUT:
+ case CS35L56_IRQ1_CFG:
+ case CS35L56_IRQ1_STATUS:
+ case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+ case CS35L56_IRQ1_EINT_18:
+ case CS35L56_IRQ1_EINT_20:
+ case CS35L56_IRQ1_MASK_1:
+ case CS35L56_IRQ1_MASK_2:
+ case CS35L56_IRQ1_MASK_4:
+ case CS35L56_IRQ1_MASK_8:
+ case CS35L56_IRQ1_MASK_18:
+ case CS35L56_IRQ1_MASK_20:
+ case CS35L56_DSP_VIRTUAL1_MBOX_1:
+ case CS35L56_DSP_VIRTUAL1_MBOX_2:
+ case CS35L56_DSP_VIRTUAL1_MBOX_3:
+ case CS35L56_DSP_VIRTUAL1_MBOX_4:
+ case CS35L56_DSP_VIRTUAL1_MBOX_5:
+ case CS35L56_DSP_VIRTUAL1_MBOX_6:
+ case CS35L56_DSP_VIRTUAL1_MBOX_7:
+ case CS35L56_DSP_VIRTUAL1_MBOX_8:
+ case CS35L56_DSP_RESTRICT_STS1:
+ case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS35L56_DSP1_SCRATCH1:
+ case CS35L56_DSP1_SCRATCH2:
+ case CS35L56_DSP1_SCRATCH3:
+ case CS35L56_DSP1_SCRATCH4:
+ return true;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static bool cs35l56_precious_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DSP1_XMEM_PACKED_0 ... CS35L56_DSP1_XMEM_PACKED_6143:
+ case CS35L56_DSP1_YMEM_PACKED_0 ... CS35L56_DSP1_YMEM_PACKED_4604:
+ case CS35L56_DSP1_PMEM_0 ... CS35L56_DSP1_PMEM_5114:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l56_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L56_DEVID:
+ case CS35L56_REVID:
+ case CS35L56_RELID:
+ case CS35L56_OTPID:
+ case CS35L56_SFT_RESET:
+ case CS35L56_GLOBAL_ENABLES: /* owned by firmware */
+ case CS35L56_BLOCK_ENABLES: /* owned by firmware */
+ case CS35L56_BLOCK_ENABLES2: /* owned by firmware */
+ case CS35L56_REFCLK_INPUT: /* owned by firmware */
+ case CS35L56_GLOBAL_SAMPLE_RATE: /* owned by firmware */
+ case CS35L56_DACPCM1_INPUT: /* owned by firmware */
+ case CS35L56_DACPCM2_INPUT: /* owned by firmware */
+ case CS35L56_DSP1RX1_INPUT: /* owned by firmware */
+ case CS35L56_DSP1RX2_INPUT: /* owned by firmware */
+ case CS35L56_IRQ1_STATUS:
+ case CS35L56_IRQ1_EINT_1 ... CS35L56_IRQ1_EINT_8:
+ case CS35L56_IRQ1_EINT_18:
+ case CS35L56_IRQ1_EINT_20:
+ case CS35L56_DSP_VIRTUAL1_MBOX_1:
+ case CS35L56_DSP_VIRTUAL1_MBOX_2:
+ case CS35L56_DSP_VIRTUAL1_MBOX_3:
+ case CS35L56_DSP_VIRTUAL1_MBOX_4:
+ case CS35L56_DSP_VIRTUAL1_MBOX_5:
+ case CS35L56_DSP_VIRTUAL1_MBOX_6:
+ case CS35L56_DSP_VIRTUAL1_MBOX_7:
+ case CS35L56_DSP_VIRTUAL1_MBOX_8:
+ case CS35L56_DSP_RESTRICT_STS1:
+ case CS35L56_DSP1_SYS_INFO_ID ... CS35L56_DSP1_SYS_INFO_END:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_0:
+ case CS35L56_DSP1_AHBM_WINDOW_DEBUG_1:
+ case CS35L56_DSP1_SCRATCH1:
+ case CS35L56_DSP1_SCRATCH2:
+ case CS35L56_DSP1_SCRATCH3:
+ case CS35L56_DSP1_SCRATCH4:
+ return true;
+ case CS35L56_MAIN_RENDER_USER_MUTE:
+ case CS35L56_MAIN_RENDER_USER_VOLUME:
+ case CS35L56_MAIN_POSTURE_NUMBER:
+ return false;
+ default:
+ return cs35l56_is_dsp_memory(reg);
+ }
+}
+
+static const u32 cs35l56_firmware_registers[] = {
+ CS35L56_MAIN_RENDER_USER_MUTE,
+ CS35L56_MAIN_RENDER_USER_VOLUME,
+ CS35L56_MAIN_POSTURE_NUMBER,
+};
+
+void cs35l56_reread_firmware_registers(struct device *dev, struct regmap *regmap)
+{
+ int i;
+ unsigned int val;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l56_firmware_registers); i++) {
+ regmap_read(regmap, cs35l56_firmware_registers[i], &val);
+ dev_dbg(dev, "%s: %d: %#x: %#x\n", __func__,
+ i, cs35l56_firmware_registers[i], val);
+ }
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_reread_firmware_registers, SND_SOC_CS35L56_SHARED);
+
+const struct cs_dsp_region cs35l56_dsp1_regions[] = {
+ { .type = WMFW_HALO_PM_PACKED, .base = CS35L56_DSP1_PMEM_0 },
+ { .type = WMFW_HALO_XM_PACKED, .base = CS35L56_DSP1_XMEM_PACKED_0 },
+ { .type = WMFW_HALO_YM_PACKED, .base = CS35L56_DSP1_YMEM_PACKED_0 },
+ { .type = WMFW_ADSP2_XM, .base = CS35L56_DSP1_XMEM_UNPACKED24_0 },
+ { .type = WMFW_ADSP2_YM, .base = CS35L56_DSP1_YMEM_UNPACKED24_0 },
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_dsp1_regions, SND_SOC_CS35L56_SHARED);
+
+static const u32 cs35l56_bclk_valid_for_pll_freq_table[] = {
+ [0x0C] = 128000,
+ [0x0F] = 256000,
+ [0x11] = 384000,
+ [0x12] = 512000,
+ [0x15] = 768000,
+ [0x17] = 1024000,
+ [0x1A] = 1500000,
+ [0x1B] = 1536000,
+ [0x1C] = 2000000,
+ [0x1D] = 2048000,
+ [0x1E] = 2400000,
+ [0x20] = 3000000,
+ [0x21] = 3072000,
+ [0x23] = 4000000,
+ [0x24] = 4096000,
+ [0x25] = 4800000,
+ [0x27] = 6000000,
+ [0x28] = 6144000,
+ [0x29] = 6250000,
+ [0x2A] = 6400000,
+ [0x2E] = 8000000,
+ [0x2F] = 8192000,
+ [0x30] = 9600000,
+ [0x32] = 12000000,
+ [0x33] = 12288000,
+ [0x37] = 13500000,
+ [0x38] = 19200000,
+ [0x39] = 22579200,
+ [0x3B] = 24576000,
+};
+
+int cs35l56_get_bclk_freq_id(unsigned int freq)
+{
+ int i;
+
+ if (freq == 0)
+ return -EINVAL;
+
+ /* The BCLK frequency must be a valid PLL REFCLK */
+ for (i = 0; i < ARRAY_SIZE(cs35l56_bclk_valid_for_pll_freq_table); ++i) {
+ if (cs35l56_bclk_valid_for_pll_freq_table[i] == freq)
+ return i;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_get_bclk_freq_id, SND_SOC_CS35L56_SHARED);
+
+static const char * const cs35l56_supplies[/* auto-sized */] = {
+ "VDD_P",
+ "VDD_IO",
+ "VDD_A",
+};
+
+void cs35l56_fill_supply_names(struct regulator_bulk_data *data)
+{
+ int i;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_supplies) != CS35L56_NUM_BULK_SUPPLIES);
+ for (i = 0; i < ARRAY_SIZE(cs35l56_supplies); i++)
+ data[i].supply = cs35l56_supplies[i];
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_fill_supply_names, SND_SOC_CS35L56_SHARED);
+
+const char * const cs35l56_tx_input_texts[] = {
+ "None", "ASP1RX1", "ASP1RX2", "VMON", "IMON", "ERRVOL", "CLASSH",
+ "VDDBMON", "VBSTMON", "DSP1TX1", "DSP1TX2", "DSP1TX3", "DSP1TX4",
+ "DSP1TX5", "DSP1TX6", "DSP1TX7", "DSP1TX8", "TEMPMON",
+ "INTERPOLATOR", "SDW1RX1", "SDW1RX2",
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_texts, SND_SOC_CS35L56_SHARED);
+
+const unsigned int cs35l56_tx_input_values[] = {
+ CS35L56_INPUT_SRC_NONE,
+ CS35L56_INPUT_SRC_ASP1RX1,
+ CS35L56_INPUT_SRC_ASP1RX2,
+ CS35L56_INPUT_SRC_VMON,
+ CS35L56_INPUT_SRC_IMON,
+ CS35L56_INPUT_SRC_ERR_VOL,
+ CS35L56_INPUT_SRC_CLASSH,
+ CS35L56_INPUT_SRC_VDDBMON,
+ CS35L56_INPUT_SRC_VBSTMON,
+ CS35L56_INPUT_SRC_DSP1TX1,
+ CS35L56_INPUT_SRC_DSP1TX2,
+ CS35L56_INPUT_SRC_DSP1TX3,
+ CS35L56_INPUT_SRC_DSP1TX4,
+ CS35L56_INPUT_SRC_DSP1TX5,
+ CS35L56_INPUT_SRC_DSP1TX6,
+ CS35L56_INPUT_SRC_DSP1TX7,
+ CS35L56_INPUT_SRC_DSP1TX8,
+ CS35L56_INPUT_SRC_TEMPMON,
+ CS35L56_INPUT_SRC_INTERPOLATOR,
+ CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL1,
+ CS35L56_INPUT_SRC_SWIRE_DP1_CHANNEL2,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_tx_input_values, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_i2c = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_i2c, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_spi = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .pad_bits = 16,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_BIG,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_spi, SND_SOC_CS35L56_SHARED);
+
+struct regmap_config cs35l56_regmap_sdw = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = CS35L56_DSP1_PMEM_5114,
+ .reg_defaults = cs35l56_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs35l56_reg_defaults),
+ .volatile_reg = cs35l56_volatile_reg,
+ .readable_reg = cs35l56_readable_reg,
+ .precious_reg = cs35l56_precious_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_regmap_sdw, SND_SOC_CS35L56_SHARED);
+
+MODULE_DESCRIPTION("ASoC CS35L56 Shared");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56-spi.c b/sound/soc/codecs/cs35l56-spi.c
new file mode 100644
index 000000000000..996aab10500e
--- /dev/null
+++ b/sound/soc/codecs/cs35l56-spi.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// CS35L56 ALSA SoC audio driver SPI binding
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "cs35l56.h"
+
+static int cs35l56_spi_probe(struct spi_device *spi)
+{
+ const struct regmap_config *regmap_config = &cs35l56_regmap_spi;
+ struct cs35l56_private *cs35l56;
+ int ret;
+
+ cs35l56 = devm_kzalloc(&spi->dev, sizeof(struct cs35l56_private), GFP_KERNEL);
+ if (!cs35l56)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, cs35l56);
+ cs35l56->regmap = devm_regmap_init_spi(spi, regmap_config);
+ if (IS_ERR(cs35l56->regmap)) {
+ ret = PTR_ERR(cs35l56->regmap);
+ return dev_err_probe(&spi->dev, ret, "Failed to allocate register map\n");
+ }
+
+ cs35l56->dev = &spi->dev;
+
+ ret = cs35l56_common_probe(cs35l56);
+ if (ret != 0)
+ return ret;
+
+ ret = cs35l56_init(cs35l56);
+ if (ret == 0)
+ ret = cs35l56_irq_request(cs35l56, spi->irq);
+ if (ret < 0)
+ cs35l56_remove(cs35l56);
+
+ return ret;
+}
+
+static void cs35l56_spi_remove(struct spi_device *spi)
+{
+ struct cs35l56_private *cs35l56 = spi_get_drvdata(spi);
+
+ cs35l56_remove(cs35l56);
+}
+
+static const struct spi_device_id cs35l56_id_spi[] = {
+ { "cs35l56", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, cs35l56_id_spi);
+
+static struct spi_driver cs35l56_spi_driver = {
+ .driver = {
+ .name = "cs35l56",
+ .pm = &cs35l56_pm_ops_i2c_spi,
+ },
+ .id_table = cs35l56_id_spi,
+ .probe = cs35l56_spi_probe,
+ .remove = cs35l56_spi_remove,
+};
+
+module_spi_driver(cs35l56_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L56 SPI driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_CORE);
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
new file mode 100644
index 000000000000..46762f7f1449
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.c
@@ -0,0 +1,1601 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// Driver for Cirrus Logic CS35L56 smart amp
+//
+// Copyright (C) 2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/pm.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/types.h>
+#include <linux/workqueue.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/tlv.h>
+
+#include "wm_adsp.h"
+#include "cs35l56.h"
+
+static int cs35l56_dsp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+static int cs35l56_mbox_send(struct cs35l56_private *cs35l56, unsigned int command)
+{
+ unsigned int val;
+ int ret;
+
+ regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, command);
+ ret = regmap_read_poll_timeout(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ val, (val == 0),
+ CS35L56_MBOX_POLL_US, CS35L56_MBOX_TIMEOUT_US);
+ if (ret) {
+ dev_warn(cs35l56->dev, "MBOX command %#x failed: %d\n", command, ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void cs35l56_wait_dsp_ready(struct cs35l56_private *cs35l56)
+{
+ /* Wait for patching to complete */
+ flush_work(&cs35l56->dsp_work);
+}
+
+static int cs35l56_dspwait_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cs35l56_wait_dsp_ready(cs35l56);
+ return snd_soc_get_volsw(kcontrol, ucontrol);
+}
+
+static int cs35l56_dspwait_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cs35l56_wait_dsp_ready(cs35l56);
+ return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+static DECLARE_TLV_DB_SCALE(vol_tlv, -10000, 25, 0);
+
+static const struct snd_kcontrol_new cs35l56_controls[] = {
+ SOC_SINGLE_EXT("Speaker Switch",
+ CS35L56_MAIN_RENDER_USER_MUTE, 0, 1, 1,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+ SOC_SINGLE_S_EXT_TLV("Speaker Volume",
+ CS35L56_MAIN_RENDER_USER_VOLUME,
+ 6, -400, 400, 9, 0,
+ cs35l56_dspwait_get_volsw,
+ cs35l56_dspwait_put_volsw,
+ vol_tlv),
+ SOC_SINGLE_EXT("Posture Number", CS35L56_MAIN_POSTURE_NUMBER,
+ 0, 255, 0,
+ cs35l56_dspwait_get_volsw, cs35l56_dspwait_put_volsw),
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx1_enum,
+ CS35L56_ASP1TX1_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx1_mux =
+ SOC_DAPM_ENUM("ASP1TX1 SRC", cs35l56_asp1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx2_enum,
+ CS35L56_ASP1TX2_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx2_mux =
+ SOC_DAPM_ENUM("ASP1TX2 SRC", cs35l56_asp1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx3_enum,
+ CS35L56_ASP1TX3_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx3_mux =
+ SOC_DAPM_ENUM("ASP1TX3 SRC", cs35l56_asp1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_asp1tx4_enum,
+ CS35L56_ASP1TX4_INPUT,
+ 0, CS35L56_ASP_TXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new asp1_tx4_mux =
+ SOC_DAPM_ENUM("ASP1TX4 SRC", cs35l56_asp1tx4_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx1_enum,
+ CS35L56_SWIRE_DP3_CH1_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx1_mux =
+ SOC_DAPM_ENUM("SDW1TX1 SRC", cs35l56_sdw1tx1_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx2_enum,
+ CS35L56_SWIRE_DP3_CH2_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx2_mux =
+ SOC_DAPM_ENUM("SDW1TX2 SRC", cs35l56_sdw1tx2_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx3_enum,
+ CS35L56_SWIRE_DP3_CH3_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx3_mux =
+ SOC_DAPM_ENUM("SDW1TX3 SRC", cs35l56_sdw1tx3_enum);
+
+static SOC_VALUE_ENUM_SINGLE_DECL(cs35l56_sdw1tx4_enum,
+ CS35L56_SWIRE_DP3_CH4_INPUT,
+ 0, CS35L56_SWIRETXn_SRC_MASK,
+ cs35l56_tx_input_texts,
+ cs35l56_tx_input_values);
+
+static const struct snd_kcontrol_new sdw1_tx4_mux =
+ SOC_DAPM_ENUM("SDW1TX4 SRC", cs35l56_sdw1tx4_enum);
+
+static int cs35l56_play_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 cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ unsigned int val;
+ int ret;
+
+ dev_dbg(cs35l56->dev, "play: %d\n", event);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Don't wait for ACK, we check in POST_PMU that it completed */
+ return regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_AUDIO_PLAY);
+ case SND_SOC_DAPM_POST_PMU:
+ /* Wait for firmware to enter PS0 power state */
+ ret = regmap_read_poll_timeout(cs35l56->regmap,
+ CS35L56_TRANSDUCER_ACTUAL_PS,
+ val, (val == CS35L56_PS0),
+ CS35L56_PS0_POLL_US,
+ CS35L56_PS0_TIMEOUT_US);
+ if (ret)
+ dev_err(cs35l56->dev, "PS0 wait failed: %d\n", ret);
+ return ret;
+ case SND_SOC_DAPM_POST_PMD:
+ return cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_AUDIO_PAUSE);
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs35l56_dapm_widgets[] = {
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_B", 0, 0),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("VDD_AMP", 0, 0),
+
+ SND_SOC_DAPM_SUPPLY("PLAY", SND_SOC_NOPM, 0, 0, cs35l56_play_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUT_DRV("AMP", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, cs35l56_dsp_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_AIF_IN("ASP1RX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_RX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASP1RX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_RX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX1", NULL, 0, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX2", NULL, 1, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX3", NULL, 2, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASP1TX4", NULL, 3, CS35L56_ASP1_ENABLES1,
+ CS35L56_ASP_TX4_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_MUX("ASP1 TX1 Source", SND_SOC_NOPM, 0, 0, &asp1_tx1_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX2 Source", SND_SOC_NOPM, 0, 0, &asp1_tx2_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX3 Source", SND_SOC_NOPM, 0, 0, &asp1_tx3_mux),
+ SND_SOC_DAPM_MUX("ASP1 TX4 Source", SND_SOC_NOPM, 0, 0, &asp1_tx4_mux),
+
+ SND_SOC_DAPM_MUX("SDW1 TX1 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx1_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX2 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx2_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX3 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx3_mux),
+ SND_SOC_DAPM_MUX("SDW1 TX4 Source", SND_SOC_NOPM, 0, 0, &sdw1_tx4_mux),
+
+ SND_SOC_DAPM_SIGGEN("VMON ADC"),
+ SND_SOC_DAPM_SIGGEN("IMON ADC"),
+ SND_SOC_DAPM_SIGGEN("ERRVOL ADC"),
+ SND_SOC_DAPM_SIGGEN("CLASSH ADC"),
+ SND_SOC_DAPM_SIGGEN("VDDBMON ADC"),
+ SND_SOC_DAPM_SIGGEN("VBSTMON ADC"),
+ SND_SOC_DAPM_SIGGEN("TEMPMON ADC"),
+};
+
+#define CS35L56_SRC_ROUTE(name) \
+ { name" Source", "ASP1RX1", "ASP1RX1" }, \
+ { name" Source", "ASP1RX2", "ASP1RX2" }, \
+ { name" Source", "VMON", "VMON ADC" }, \
+ { name" Source", "IMON", "IMON ADC" }, \
+ { name" Source", "ERRVOL", "ERRVOL ADC" }, \
+ { name" Source", "CLASSH", "CLASSH ADC" }, \
+ { name" Source", "VDDBMON", "VDDBMON ADC" }, \
+ { name" Source", "VBSTMON", "VBSTMON ADC" }, \
+ { name" Source", "DSP1TX1", "DSP1" }, \
+ { name" Source", "DSP1TX2", "DSP1" }, \
+ { name" Source", "DSP1TX3", "DSP1" }, \
+ { name" Source", "DSP1TX4", "DSP1" }, \
+ { name" Source", "DSP1TX5", "DSP1" }, \
+ { name" Source", "DSP1TX6", "DSP1" }, \
+ { name" Source", "DSP1TX7", "DSP1" }, \
+ { name" Source", "DSP1TX8", "DSP1" }, \
+ { name" Source", "TEMPMON", "TEMPMON ADC" }, \
+ { name" Source", "INTERPOLATOR", "AMP" }, \
+ { name" Source", "SDW1RX1", "SDW1 Playback" }, \
+ { name" Source", "SDW1RX2", "SDW1 Playback" },
+
+static const struct snd_soc_dapm_route cs35l56_audio_map[] = {
+ { "AMP", NULL, "VDD_B" },
+ { "AMP", NULL, "VDD_AMP" },
+
+ { "ASP1 Playback", NULL, "PLAY" },
+ { "SDW1 Playback", NULL, "PLAY" },
+
+ { "ASP1RX1", NULL, "ASP1 Playback" },
+ { "ASP1RX2", NULL, "ASP1 Playback" },
+ { "DSP1", NULL, "ASP1RX1" },
+ { "DSP1", NULL, "ASP1RX2" },
+ { "DSP1", NULL, "SDW1 Playback" },
+ { "AMP", NULL, "DSP1" },
+ { "SPK", NULL, "AMP" },
+
+ CS35L56_SRC_ROUTE("ASP1 TX1")
+ CS35L56_SRC_ROUTE("ASP1 TX2")
+ CS35L56_SRC_ROUTE("ASP1 TX3")
+ CS35L56_SRC_ROUTE("ASP1 TX4")
+
+ { "ASP1TX1", NULL, "ASP1 TX1 Source" },
+ { "ASP1TX2", NULL, "ASP1 TX2 Source" },
+ { "ASP1TX3", NULL, "ASP1 TX3 Source" },
+ { "ASP1TX4", NULL, "ASP1 TX4 Source" },
+ { "ASP1 Capture", NULL, "ASP1TX1" },
+ { "ASP1 Capture", NULL, "ASP1TX2" },
+ { "ASP1 Capture", NULL, "ASP1TX3" },
+ { "ASP1 Capture", NULL, "ASP1TX4" },
+
+ CS35L56_SRC_ROUTE("SDW1 TX1")
+ CS35L56_SRC_ROUTE("SDW1 TX2")
+ CS35L56_SRC_ROUTE("SDW1 TX3")
+ CS35L56_SRC_ROUTE("SDW1 TX4")
+ { "SDW1 Capture", NULL, "SDW1 TX1 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX2 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX3 Source" },
+ { "SDW1 Capture", NULL, "SDW1 TX4 Source" },
+};
+
+static int cs35l56_dsp_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 cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ dev_dbg(cs35l56->dev, "%s: %d\n", __func__, event);
+
+ return wm_adsp_event(w, kcontrol, event);
+}
+
+irqreturn_t cs35l56_irq(int irq, void *data)
+{
+ struct cs35l56_private *cs35l56 = data;
+ unsigned int status1 = 0, status8 = 0, status20 = 0;
+ unsigned int mask1, mask8, mask20;
+ unsigned int val;
+ int rv;
+
+ irqreturn_t ret = IRQ_NONE;
+
+ if (!cs35l56->init_done)
+ return IRQ_NONE;
+
+ mutex_lock(&cs35l56->irq_lock);
+
+ rv = pm_runtime_resume_and_get(cs35l56->dev);
+ if (rv < 0) {
+ dev_err(cs35l56->dev, "irq: failed to get pm_runtime: %d\n", rv);
+ goto err_unlock;
+ }
+
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_STATUS, &val);
+ if ((val & CS35L56_IRQ1_STS_MASK) == 0) {
+ dev_dbg(cs35l56->dev, "Spurious IRQ: no pending interrupt\n");
+ goto err;
+ }
+
+ /* Ack interrupts */
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_1, &status1);
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_1, &mask1);
+ status1 &= ~mask1;
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_1, status1);
+
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_8, &status8);
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_8, &mask8);
+ status8 &= ~mask8;
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_8, status8);
+
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_20, &status20);
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_MASK_20, &mask20);
+ status20 &= ~mask20;
+ /* We don't want EINT20 but they default to unmasked: force mask */
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_MASK_20, 0xffffffff);
+
+ dev_dbg(cs35l56->dev, "%s: %#x %#x\n", __func__, status1, status8);
+
+ /* Check to see if unmasked bits are active */
+ if (!status1 && !status8 && !status20)
+ goto err;
+
+ if (status1 & CS35L56_AMP_SHORT_ERR_EINT1_MASK)
+ dev_crit(cs35l56->dev, "Amp short error\n");
+
+ if (status8 & CS35L56_TEMP_ERR_EINT1_MASK)
+ dev_crit(cs35l56->dev, "Overtemp error\n");
+
+ ret = IRQ_HANDLED;
+
+err:
+ pm_runtime_put(cs35l56->dev);
+err_unlock:
+ mutex_unlock(&cs35l56->irq_lock);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq, SND_SOC_CS35L56_CORE);
+
+int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq)
+{
+ int ret;
+
+ if (!irq)
+ return 0;
+
+ ret = devm_request_threaded_irq(cs35l56->dev, irq, NULL, cs35l56_irq,
+ IRQF_ONESHOT | IRQF_SHARED | IRQF_TRIGGER_LOW,
+ "cs35l56", cs35l56);
+ if (!ret)
+ cs35l56->irq = irq;
+ else
+ dev_err(cs35l56->dev, "Failed to get IRQ: %d\n", ret);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_irq_request, SND_SOC_CS35L56_CORE);
+
+static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(codec_dai->component);
+ unsigned int val;
+
+ dev_dbg(cs35l56->dev, "%s: %#x\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ break;
+ default:
+ dev_err(cs35l56->dev, "Unsupported clock source mode\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ val = CS35L56_ASP_FMT_DSP_A << CS35L56_ASP_FMT_SHIFT;
+ cs35l56->tdm_mode = true;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ val = CS35L56_ASP_FMT_I2S << CS35L56_ASP_FMT_SHIFT;
+ cs35l56->tdm_mode = false;
+ break;
+ default:
+ dev_err(cs35l56->dev, "Unsupported DAI format\n");
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_IF:
+ val |= CS35L56_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ val |= CS35L56_ASP_BCLK_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ val |= CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ default:
+ dev_err(cs35l56->dev, "Invalid clock invert\n");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->regmap,
+ CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_FMT_MASK |
+ CS35L56_ASP_BCLK_INV_MASK | CS35L56_ASP_FSYNC_INV_MASK,
+ val);
+
+ /* Hi-Z DOUT in unused slots and when all TX are disabled */
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL3,
+ CS35L56_ASP1_DOUT_HIZ_CTRL_MASK,
+ CS35L56_ASP_UNUSED_HIZ_OFF_HIZ);
+
+ return 0;
+}
+
+static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56,
+ unsigned int reg, unsigned long mask)
+{
+ unsigned int reg_val, channel_shift;
+ int bit_num;
+
+ /* Init all slots to 63 */
+ switch (reg) {
+ case CS35L56_ASP1_FRAME_CONTROL1:
+ reg_val = 0x3f3f3f3f;
+ break;
+ case CS35L56_ASP1_FRAME_CONTROL5:
+ reg_val = 0x3f3f3f;
+ break;
+ }
+
+ /* Enable consecutive TX1..TXn for each of the slots set in mask */
+ channel_shift = 0;
+ for_each_set_bit(bit_num, &mask, 32) {
+ reg_val &= ~(0x3f << channel_shift);
+ reg_val |= bit_num << channel_shift;
+ channel_shift += 8;
+ }
+
+ regmap_write(cs35l56->regmap, reg, reg_val);
+}
+
+static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+ if ((slots == 0) || (slot_width == 0)) {
+ dev_dbg(cs35l56->dev, "tdm config cleared\n");
+ cs35l56->asp_slot_width = 0;
+ cs35l56->asp_slot_count = 0;
+ return 0;
+ }
+
+ if (slot_width > (CS35L56_ASP_RX_WIDTH_MASK >> CS35L56_ASP_RX_WIDTH_SHIFT)) {
+ dev_err(cs35l56->dev, "tdm invalid slot width %d\n", slot_width);
+ return -EINVAL;
+ }
+
+ /* More than 32 slots would give an unsupportable BCLK frequency */
+ if (slots > 32) {
+ dev_err(cs35l56->dev, "tdm invalid slot count %d\n", slots);
+ return -EINVAL;
+ }
+
+ cs35l56->asp_slot_width = (u8)slot_width;
+ cs35l56->asp_slot_count = (u8)slots;
+
+ // Note: rx/tx is from point of view of the CPU end
+ if (tx_mask == 0)
+ tx_mask = 0x3; // ASPRX1/RX2 in slots 0 and 1
+
+ if (rx_mask == 0)
+ rx_mask = 0xf; // ASPTX1..TX4 in slots 0..3
+
+ cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL1, rx_mask);
+ cs35l56_set_asp_slot_positions(cs35l56, CS35L56_ASP1_FRAME_CONTROL5, tx_mask);
+
+ dev_dbg(cs35l56->dev, "tdm slot width: %u count: %u tx_mask: %#x rx_mask: %#x\n",
+ cs35l56->asp_slot_width, cs35l56->asp_slot_count, tx_mask, rx_mask);
+
+ return 0;
+}
+
+static int cs35l56_asp_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ unsigned int rate = params_rate(params);
+ u8 asp_width, asp_wl;
+
+ asp_wl = params_width(params);
+ if (cs35l56->asp_slot_width)
+ asp_width = cs35l56->asp_slot_width;
+ else
+ asp_width = asp_wl;
+
+ dev_dbg(cs35l56->dev, "%s: wl=%d, width=%d, rate=%d", __func__, asp_wl, asp_width, rate);
+
+ if (!cs35l56->sysclk_set) {
+ unsigned int slots = cs35l56->asp_slot_count;
+ unsigned int bclk_freq;
+ int freq_id;
+
+ if (slots == 0) {
+ slots = params_channels(params);
+
+ /* I2S always has an even number of slots */
+ if (!cs35l56->tdm_mode)
+ slots = round_up(slots, 2);
+ }
+
+ bclk_freq = asp_width * slots * rate;
+ freq_id = cs35l56_get_bclk_freq_id(bclk_freq);
+ if (freq_id < 0) {
+ dev_err(cs35l56->dev, "%s: Invalid BCLK %u\n", __func__, bclk_freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+ CS35L56_ASP_BCLK_FREQ_MASK,
+ freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+ }
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_RX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_RX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL5,
+ CS35L56_ASP_RX_WL_MASK, asp_wl);
+ } else {
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL2,
+ CS35L56_ASP_TX_WIDTH_MASK, asp_width <<
+ CS35L56_ASP_TX_WIDTH_SHIFT);
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_DATA_CONTROL1,
+ CS35L56_ASP_TX_WL_MASK, asp_wl);
+ }
+
+ return 0;
+}
+
+static int cs35l56_asp_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ int freq_id;
+
+ if (freq == 0) {
+ cs35l56->sysclk_set = false;
+ return 0;
+ }
+
+ freq_id = cs35l56_get_bclk_freq_id(freq);
+ if (freq_id < 0)
+ return freq_id;
+
+ regmap_update_bits(cs35l56->regmap, CS35L56_ASP1_CONTROL1,
+ CS35L56_ASP_BCLK_FREQ_MASK,
+ freq_id << CS35L56_ASP_BCLK_FREQ_SHIFT);
+ cs35l56->sysclk_set = true;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_ops = {
+ .set_fmt = cs35l56_asp_dai_set_fmt,
+ .set_tdm_slot = cs35l56_asp_dai_set_tdm_slot,
+ .hw_params = cs35l56_asp_dai_hw_params,
+ .set_sysclk = cs35l56_asp_dai_set_sysclk,
+};
+
+static void cs35l56_sdw_dai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int cs35l56_sdw_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+
+ /* rx/tx are from point of view of the CPU end so opposite to our rx/tx */
+ cs35l56->rx_mask = tx_mask;
+ cs35l56->tx_mask = rx_mask;
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_stream_config sconfig;
+ struct sdw_port_config pconfig;
+ int ret;
+
+ dev_dbg(cs35l56->dev, "%s: rate %d\n", __func__, params_rate(params));
+
+ if (!cs35l56->init_done)
+ return -ENODEV;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ memset(&sconfig, 0, sizeof(sconfig));
+ memset(&pconfig, 0, sizeof(pconfig));
+
+ sconfig.frame_rate = params_rate(params);
+ sconfig.bps = snd_pcm_format_width(params_format(params));
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ sconfig.direction = SDW_DATA_DIR_RX;
+ pconfig.num = CS35L56_SDW1_PLAYBACK_PORT;
+ pconfig.ch_mask = cs35l56->rx_mask;
+ } else {
+ sconfig.direction = SDW_DATA_DIR_TX;
+ pconfig.num = CS35L56_SDW1_CAPTURE_PORT;
+ pconfig.ch_mask = cs35l56->tx_mask;
+ }
+
+ if (pconfig.ch_mask == 0) {
+ sconfig.ch_count = params_channels(params);
+ pconfig.ch_mask = GENMASK(sconfig.ch_count - 1, 0);
+ } else {
+ sconfig.ch_count = hweight32(pconfig.ch_mask);
+ }
+
+ ret = sdw_stream_add_slave(cs35l56->sdw_peripheral, &sconfig, &pconfig,
+ 1, sdw_stream);
+ if (ret) {
+ dev_err(dai->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!cs35l56->sdw_peripheral)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(cs35l56->sdw_peripheral, sdw_stream);
+
+ return 0;
+}
+
+static int cs35l56_sdw_dai_set_stream(struct snd_soc_dai *dai,
+ void *sdw_stream, int direction)
+{
+ if (!sdw_stream)
+ return 0;
+
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l56_sdw_dai_ops = {
+ .set_tdm_slot = cs35l56_sdw_dai_set_tdm_slot,
+ .shutdown = cs35l56_sdw_dai_shutdown,
+ .hw_params = cs35l56_sdw_dai_hw_params,
+ .hw_free = cs35l56_sdw_dai_hw_free,
+ .set_stream = cs35l56_sdw_dai_set_stream,
+};
+
+static struct snd_soc_dai_driver cs35l56_dai[] = {
+ {
+ .name = "cs35l56-asp1",
+ .id = 0,
+ .playback = {
+ .stream_name = "ASP1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "ASP1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_TX_FORMATS,
+ },
+ .ops = &cs35l56_ops,
+ .symmetric_rate = 1,
+ .symmetric_sample_bits = 1,
+ },
+ {
+ .name = "cs35l56-sdw1",
+ .id = 1,
+ .playback = {
+ .stream_name = "SDW1 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_RX_FORMATS,
+ },
+ .capture = {
+ .stream_name = "SDW1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS35L56_RATES,
+ .formats = CS35L56_TX_FORMATS,
+ },
+ .symmetric_rate = 1,
+ .ops = &cs35l56_sdw_dai_ops,
+ }
+};
+
+static int cs35l56_wait_for_firmware_boot(struct cs35l56_private *cs35l56)
+{
+ unsigned int reg;
+ unsigned int val;
+ int ret;
+
+ if (cs35l56->rev < CS35L56_REVID_B0)
+ reg = CS35L56_DSP1_HALO_STATE_A1;
+ else
+ reg = CS35L56_DSP1_HALO_STATE;
+
+ ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
+ val,
+ (val < 0xFFFF) && (val >= CS35L56_HALO_STATE_BOOT_DONE),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+
+ if ((ret < 0) && (ret != -ETIMEDOUT)) {
+ dev_err(cs35l56->dev, "Failed to read HALO_STATE: %d\n", ret);
+ return ret;
+ }
+
+ if ((ret == -ETIMEDOUT) || (val != CS35L56_HALO_STATE_BOOT_DONE)) {
+ dev_err(cs35l56->dev, "Firmware boot fail: HALO_STATE=%#x\n", val);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static inline void cs35l56_wait_min_reset_pulse(void)
+{
+ /* Satisfy minimum reset pulse width spec */
+ usleep_range(CS35L56_RESET_PULSE_MIN_US, 2 * CS35L56_RESET_PULSE_MIN_US);
+}
+
+static const struct reg_sequence cs35l56_system_reset_seq[] = {
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_SYSTEM_RESET),
+};
+
+static void cs35l56_system_reset(struct cs35l56_private *cs35l56)
+{
+ cs35l56->soft_resetting = true;
+
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled system reset sequence below.
+ */
+ regcache_cache_only(cs35l56->regmap, true);
+ regmap_multi_reg_write_bypassed(cs35l56->regmap,
+ cs35l56_system_reset_seq,
+ ARRAY_SIZE(cs35l56_system_reset_seq));
+
+ /* On SoundWire the registers won't be accessible until it re-enumerates. */
+ if (cs35l56->sdw_peripheral)
+ return;
+
+ usleep_range(CS35L56_CONTROL_PORT_READY_US, CS35L56_CONTROL_PORT_READY_US + 400);
+ regcache_cache_only(cs35l56->regmap, false);
+}
+
+static void cs35l56_dsp_work(struct work_struct *work)
+{
+ 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);
+
+ if (!cs35l56->dsp.part)
+ return;
+
+ pm_runtime_get_sync(cs35l56->dev);
+
+ /*
+ * Disable SoundWire interrupts to prevent race with IRQ work.
+ * Setting sdw_irq_no_unmask prevents the handler re-enabling
+ * the SoundWire interrupt.
+ */
+ if (cs35l56->sdw_peripheral) {
+ cs35l56->sdw_irq_no_unmask = true;
+ cancel_work_sync(&cs35l56->sdw_irq_work);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1, 0);
+ sdw_read_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1);
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF);
+ }
+
+ ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_SHUTDOWN);
+ if (ret)
+ goto err;
+
+ if (cs35l56->rev < CS35L56_REVID_B0)
+ reg = CS35L56_DSP1_PM_CUR_STATE_A1;
+ else
+ reg = CS35L56_DSP1_PM_CUR_STATE;
+
+ ret = regmap_read_poll_timeout(cs35l56->regmap, reg,
+ val, (val == CS35L56_HALO_STATE_SHUTDOWN),
+ CS35L56_HALO_STATE_POLL_US,
+ CS35L56_HALO_STATE_TIMEOUT_US);
+ if (ret < 0)
+ dev_err(cs35l56->dev, "Failed to poll PM_CUR_STATE to 1 is %d (ret %d)\n",
+ val, ret);
+
+ /* 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);
+ goto err;
+ }
+
+ mutex_lock(&cs35l56->irq_lock);
+
+ init_completion(&cs35l56->init_completion);
+
+ cs35l56_system_reset(cs35l56);
+
+ if (cs35l56->sdw_peripheral) {
+ /*
+ * The system-reset causes the CS35L56 to detach from the bus.
+ * Wait for the manager to re-enumerate the CS35L56 and
+ * cs35l56_init() to run again.
+ */
+ if (!wait_for_completion_timeout(&cs35l56->init_completion,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->dev, "%s: init_completion timed out (SDW)\n", __func__);
+ goto err_unlock;
+ }
+ } else if (cs35l56_init(cs35l56)) {
+ goto err_unlock;
+ }
+
+ regmap_clear_bits(cs35l56->regmap, CS35L56_PROTECTION_STATUS, CS35L56_FIRMWARE_MISSING);
+ cs35l56->fw_patched = true;
+
+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;
+ sdw_write_no_pm(cs35l56->sdw_peripheral, CS35L56_SDW_GEN_INT_MASK_1,
+ CS35L56_SDW_INT_MASK_CODEC_IRQ);
+ }
+}
+
+static int cs35l56_component_probe(struct snd_soc_component *component)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+ struct dentry *debugfs_root = component->debugfs_root;
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs35l56_tx_input_texts) != ARRAY_SIZE(cs35l56_tx_input_values));
+
+ if (!wait_for_completion_timeout(&cs35l56->init_completion,
+ msecs_to_jiffies(5000))) {
+ dev_err(cs35l56->dev, "%s: init_completion timed out\n", __func__);
+ return -ENODEV;
+ }
+
+ cs35l56->component = component;
+ wm_adsp2_component_probe(&cs35l56->dsp, component);
+
+ debugfs_create_bool("init_done", 0444, debugfs_root, &cs35l56->init_done);
+ debugfs_create_bool("can_hibernate", 0444, debugfs_root, &cs35l56->can_hibernate);
+ debugfs_create_bool("fw_patched", 0444, debugfs_root, &cs35l56->fw_patched);
+
+ queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+ return 0;
+}
+
+static void cs35l56_component_remove(struct snd_soc_component *component)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ cancel_work_sync(&cs35l56->dsp_work);
+}
+
+static int cs35l56_set_bias_level(struct snd_soc_component *component,
+ enum snd_soc_bias_level level)
+{
+ struct cs35l56_private *cs35l56 = snd_soc_component_get_drvdata(component);
+
+ switch (level) {
+ case SND_SOC_BIAS_STANDBY:
+ /*
+ * Wait for patching to complete when transitioning from
+ * BIAS_OFF to BIAS_STANDBY
+ */
+ if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF)
+ cs35l56_wait_dsp_ready(cs35l56);
+
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver soc_component_dev_cs35l56 = {
+ .probe = cs35l56_component_probe,
+ .remove = cs35l56_component_remove,
+
+ .dapm_widgets = cs35l56_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l56_dapm_widgets),
+ .dapm_routes = cs35l56_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l56_audio_map),
+ .controls = cs35l56_controls,
+ .num_controls = ARRAY_SIZE(cs35l56_controls),
+
+ .set_bias_level = cs35l56_set_bias_level,
+
+ .suspend_bias_off = 1, /* see cs35l56_system_resume() */
+};
+
+static const struct reg_sequence cs35l56_hibernate_seq[] = {
+ /* This must be the last register access */
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_HIBERNATE_NOW),
+};
+
+static const struct reg_sequence cs35l56_hibernate_wake_seq[] = {
+ REG_SEQ0(CS35L56_DSP_VIRTUAL1_MBOX_1, CS35L56_MBOX_CMD_WAKEUP),
+};
+
+int cs35l56_runtime_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ /* Firmware must have entered a power-save state */
+ ret = regmap_read_poll_timeout(cs35l56->regmap,
+ CS35L56_TRANSDUCER_ACTUAL_PS,
+ val, (val >= CS35L56_PS3),
+ CS35L56_PS3_POLL_US,
+ CS35L56_PS3_TIMEOUT_US);
+ if (ret)
+ dev_warn(cs35l56->dev, "PS3 wait failed: %d\n", ret);
+
+ /* Clear BOOT_DONE so it can be used to detect a reboot */
+ regmap_write(cs35l56->regmap, CS35L56_IRQ1_EINT_4, CS35L56_OTP_BOOT_DONE_MASK);
+
+ if (!cs35l56->can_hibernate) {
+ regcache_cache_only(cs35l56->regmap, true);
+ dev_dbg(dev, "Suspended: no hibernate");
+
+ return 0;
+ }
+
+ /*
+ * Enable auto-hibernate. If it is woken by some other wake source
+ * it will automatically return to hibernate.
+ */
+ cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_ALLOW_AUTO_HIBERNATE);
+
+ /*
+ * Must enter cache-only first so there can't be any more register
+ * accesses other than the controlled hibernate sequence below.
+ */
+ regcache_cache_only(cs35l56->regmap, true);
+
+ regmap_multi_reg_write_bypassed(cs35l56->regmap,
+ cs35l56_hibernate_seq,
+ ARRAY_SIZE(cs35l56_hibernate_seq));
+
+ dev_dbg(dev, "Suspended: hibernate");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_suspend, SND_SOC_CS35L56_CORE);
+
+static int __maybe_unused cs35l56_runtime_resume_i2c_spi(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ if (!cs35l56->init_done)
+ return 0;
+
+ return cs35l56_runtime_resume_common(cs35l56);
+}
+
+int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ if (!cs35l56->can_hibernate)
+ goto out_sync;
+
+ if (!cs35l56->sdw_peripheral) {
+ /*
+ * Dummy transaction to trigger I2C/SPI auto-wake. This will NAK on I2C.
+ * Must be done before releasing cache-only.
+ */
+ regmap_multi_reg_write_bypassed(cs35l56->regmap,
+ cs35l56_hibernate_wake_seq,
+ ARRAY_SIZE(cs35l56_hibernate_wake_seq));
+
+ usleep_range(CS35L56_CONTROL_PORT_READY_US,
+ CS35L56_CONTROL_PORT_READY_US + 400);
+ }
+
+out_sync:
+ regcache_cache_only(cs35l56->regmap, false);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56);
+ if (ret) {
+ dev_err(cs35l56->dev, "Hibernate wake failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ goto err;
+
+ /* BOOT_DONE will be 1 if the amp reset */
+ regmap_read(cs35l56->regmap, CS35L56_IRQ1_EINT_4, &val);
+ if (val & CS35L56_OTP_BOOT_DONE_MASK) {
+ dev_dbg(cs35l56->dev, "Registers reset in suspend\n");
+ regcache_mark_dirty(cs35l56->regmap);
+ }
+
+ regcache_sync(cs35l56->regmap);
+
+ dev_dbg(cs35l56->dev, "Resumed");
+
+ return 0;
+
+err:
+ regmap_write(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1,
+ CS35L56_MBOX_CMD_HIBERNATE_NOW);
+
+ regcache_cache_only(cs35l56->regmap, true);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_runtime_resume_common, SND_SOC_CS35L56_CORE);
+
+static int cs35l56_is_fw_reload_needed(struct cs35l56_private *cs35l56)
+{
+ unsigned int val;
+ int ret;
+
+ /* Nothing to re-patch if we haven't done any patching yet. */
+ if (!cs35l56->fw_patched)
+ return false;
+
+ /*
+ * If we have control of RESET we will have asserted it so the firmware
+ * will need re-patching.
+ */
+ if (cs35l56->reset_gpio)
+ return true;
+
+ /*
+ * In secure mode FIRMWARE_MISSING is cleared by the BIOS loader so
+ * can't be used here to test for memory retention.
+ * Assume that tuning must be re-loaded.
+ */
+ if (cs35l56->secured)
+ return true;
+
+ ret = pm_runtime_resume_and_get(cs35l56->dev);
+ if (ret) {
+ dev_err(cs35l56->dev, "Failed to runtime_get: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_PROTECTION_STATUS, &val);
+ if (ret)
+ dev_err(cs35l56->dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ else
+ ret = !!(val & CS35L56_FIRMWARE_MISSING);
+
+ pm_runtime_put_autosuspend(cs35l56->dev);
+
+ return ret;
+}
+
+int cs35l56_system_suspend(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend\n");
+
+ if (cs35l56->component)
+ flush_work(&cs35l56->dsp_work);
+
+ /*
+ * The interrupt line is normally shared, but after we start suspending
+ * we can't check if our device is the source of an interrupt, and can't
+ * clear it. Prevent this race by temporarily disabling the parent irq
+ * until we reach _no_irq.
+ */
+ if (cs35l56->irq)
+ disable_irq(cs35l56->irq);
+
+ return pm_runtime_force_suspend(dev);
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend);
+
+int cs35l56_system_suspend_late(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend_late\n");
+
+ /*
+ * Assert RESET before removing supplies.
+ * RESET is usually shared by all amps so it must not be asserted until
+ * all driver instances have done their suspend() stage.
+ */
+ if (cs35l56->reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_late);
+
+int cs35l56_system_suspend_no_irq(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_suspend_no_irq\n");
+
+ /* Handlers are now disabled so the parent IRQ can safely be re-enabled. */
+ if (cs35l56->irq)
+ enable_irq(cs35l56->irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_suspend_no_irq);
+
+int cs35l56_system_resume_no_irq(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "system_resume_no_irq\n");
+
+ /*
+ * WAKE interrupts unmask if the CS35L56 hibernates, which can cause
+ * spurious interrupts, and the interrupt line is normally shared.
+ * We can't check if our device is the source of an interrupt, and can't
+ * clear it, until it has fully resumed. Prevent this race by temporarily
+ * disabling the parent irq until we complete resume().
+ */
+ if (cs35l56->irq)
+ disable_irq(cs35l56->irq);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_no_irq);
+
+int cs35l56_system_resume_early(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "system_resume_early\n");
+
+ /* Ensure a spec-compliant RESET pulse. */
+ if (cs35l56->reset_gpio) {
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ cs35l56_wait_min_reset_pulse();
+ }
+
+ /* Enable supplies before releasing RESET. */
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+ if (ret) {
+ dev_err(dev, "system_resume_early failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Release shared RESET before drivers start resume(). */
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume_early);
+
+int cs35l56_system_resume(struct device *dev)
+{
+ struct cs35l56_private *cs35l56 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "system_resume\n");
+
+ /* Undo pm_runtime_force_suspend() before re-enabling the irq */
+ ret = pm_runtime_force_resume(dev);
+ if (cs35l56->irq)
+ enable_irq(cs35l56->irq);
+
+ if (ret)
+ return ret;
+
+ /* Firmware won't have been loaded if the component hasn't probed */
+ if (!cs35l56->component)
+ return 0;
+
+ ret = cs35l56_is_fw_reload_needed(cs35l56);
+ dev_dbg(cs35l56->dev, "fw_reload_needed: %d\n", ret);
+ if (ret < 1)
+ return ret;
+
+ cs35l56->fw_patched = false;
+ queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
+
+ /*
+ * suspend_bias_off ensures we are now in BIAS_OFF so there will be
+ * a BIAS_OFF->BIAS_STANDBY transition to complete dsp patching.
+ */
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(cs35l56_system_resume);
+
+static int cs35l56_dsp_init(struct cs35l56_private *cs35l56)
+{
+ struct wm_adsp *dsp;
+ int ret;
+
+ cs35l56->dsp_wq = create_singlethread_workqueue("cs35l56-dsp");
+ if (!cs35l56->dsp_wq)
+ return -ENOMEM;
+
+ INIT_WORK(&cs35l56->dsp_work, cs35l56_dsp_work);
+
+ dsp = &cs35l56->dsp;
+ dsp->part = "cs35l56";
+ dsp->cs_dsp.num = 1;
+ dsp->cs_dsp.type = WMFW_HALO;
+ dsp->cs_dsp.rev = 0;
+ dsp->fw = 12;
+ dsp->cs_dsp.dev = cs35l56->dev;
+ dsp->cs_dsp.regmap = cs35l56->regmap;
+ dsp->cs_dsp.base = CS35L56_DSP1_CORE_BASE;
+ dsp->cs_dsp.base_sysinfo = CS35L56_DSP1_SYS_INFO_ID;
+ dsp->cs_dsp.mem = cs35l56_dsp1_regions;
+ dsp->cs_dsp.num_mems = ARRAY_SIZE(cs35l56_dsp1_regions);
+ dsp->cs_dsp.no_core_startstop = true;
+ dsp->wmfw_optional = true;
+
+ dev_dbg(cs35l56->dev, "DSP system name: '%s'\n", dsp->system_name);
+
+ ret = wm_halo_init(dsp);
+ if (ret != 0) {
+ dev_err(cs35l56->dev, "wm_halo_init failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int cs35l56_acpi_get_name(struct cs35l56_private *cs35l56)
+{
+ acpi_handle handle = ACPI_HANDLE(cs35l56->dev);
+ const char *sub;
+
+ /* If there is no ACPI_HANDLE, there is no ACPI for this system, return 0 */
+ if (!handle)
+ return 0;
+
+ sub = acpi_get_subsystem_id(handle);
+ if (IS_ERR(sub)) {
+ /* If bad ACPI, return 0 and fallback to legacy firmware path, otherwise fail */
+ if (PTR_ERR(sub) == -ENODATA)
+ return 0;
+ else
+ return PTR_ERR(sub);
+ }
+
+ cs35l56->dsp.system_name = sub;
+ dev_dbg(cs35l56->dev, "Subsystem ID: %s\n", cs35l56->dsp.system_name);
+
+ return 0;
+}
+
+int cs35l56_common_probe(struct cs35l56_private *cs35l56)
+{
+ int ret;
+
+ init_completion(&cs35l56->init_completion);
+ mutex_init(&cs35l56->irq_lock);
+
+ dev_set_drvdata(cs35l56->dev, cs35l56);
+
+ cs35l56_fill_supply_names(cs35l56->supplies);
+ ret = devm_regulator_bulk_get(cs35l56->dev, ARRAY_SIZE(cs35l56->supplies),
+ cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->dev, ret, "Failed to request supplies\n");
+
+ /* Reset could be controlled by the BIOS or shared by multiple amps */
+ cs35l56->reset_gpio = devm_gpiod_get_optional(cs35l56->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l56->reset_gpio)) {
+ ret = PTR_ERR(cs35l56->reset_gpio);
+ /*
+ * If RESET is shared the first amp to probe will grab the reset
+ * line and reset all the amps
+ */
+ if (ret != -EBUSY)
+ return dev_err_probe(cs35l56->dev, ret, "Failed to get reset GPIO\n");
+
+ dev_info(cs35l56->dev, "Reset GPIO busy, assume shared reset\n");
+ cs35l56->reset_gpio = NULL;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+ if (ret != 0)
+ return dev_err_probe(cs35l56->dev, ret, "Failed to enable supplies\n");
+
+ if (cs35l56->reset_gpio) {
+ cs35l56_wait_min_reset_pulse();
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 1);
+ }
+
+ ret = cs35l56_acpi_get_name(cs35l56);
+ if (ret != 0)
+ goto err;
+
+ ret = cs35l56_dsp_init(cs35l56);
+ if (ret < 0) {
+ dev_err_probe(cs35l56->dev, ret, "DSP init failed\n");
+ goto err;
+ }
+
+ ret = devm_snd_soc_register_component(cs35l56->dev,
+ &soc_component_dev_cs35l56,
+ cs35l56_dai, ARRAY_SIZE(cs35l56_dai));
+ if (ret < 0) {
+ dev_err_probe(cs35l56->dev, ret, "Register codec failed\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+
+ return ret;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_common_probe, SND_SOC_CS35L56_CORE);
+
+int cs35l56_init(struct cs35l56_private *cs35l56)
+{
+ int ret;
+ unsigned int devid, revid, otpid, secured;
+
+ /*
+ * Check whether the actions associated with soft reset or one time
+ * init need to be performed.
+ */
+ if (cs35l56->soft_resetting)
+ goto post_soft_reset;
+
+ if (cs35l56->init_done)
+ return 0;
+
+ pm_runtime_set_autosuspend_delay(cs35l56->dev, 100);
+ pm_runtime_use_autosuspend(cs35l56->dev);
+ pm_runtime_set_active(cs35l56->dev);
+ pm_runtime_enable(cs35l56->dev);
+
+ /*
+ * If the system is not using a reset_gpio then issue a
+ * dummy read to force a wakeup.
+ */
+ if (!cs35l56->reset_gpio)
+ regmap_read(cs35l56->regmap, CS35L56_DSP_VIRTUAL1_MBOX_1, &devid);
+
+ /* Wait for control port to be ready (datasheet tIRS). */
+ usleep_range(CS35L56_CONTROL_PORT_READY_US,
+ CS35L56_CONTROL_PORT_READY_US + 400);
+
+ /*
+ * The HALO_STATE register is in different locations on Ax and B0
+ * devices so the REVID needs to be determined before waiting for the
+ * firmware to boot.
+ */
+ ret = regmap_read(cs35l56->regmap, CS35L56_REVID, &revid);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+ cs35l56->rev = revid & (CS35L56_AREVID_MASK | CS35L56_MTLREVID_MASK);
+
+ ret = cs35l56_wait_for_firmware_boot(cs35l56);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_DEVID, &devid);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Get Device ID failed\n");
+ return ret;
+ }
+ devid &= CS35L56_DEVID_MASK;
+
+ switch (devid) {
+ case 0x35A56:
+ break;
+ default:
+ dev_err(cs35l56->dev, "Unknown device %x\n", devid);
+ return ret;
+ }
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_DSP_RESTRICT_STS1, &secured);
+ if (ret) {
+ dev_err(cs35l56->dev, "Get Secure status failed\n");
+ return ret;
+ }
+
+ /* When any bus is restricted treat the device as secured */
+ if (secured & CS35L56_RESTRICTED_MASK)
+ cs35l56->secured = true;
+
+ ret = regmap_read(cs35l56->regmap, CS35L56_OTPID, &otpid);
+ if (ret < 0) {
+ dev_err(cs35l56->dev, "Get OTP ID failed\n");
+ return ret;
+ }
+
+ dev_info(cs35l56->dev, "Cirrus Logic CS35L56%s Rev %02X OTP%d\n",
+ cs35l56->secured ? "s" : "", cs35l56->rev, otpid);
+
+ /* 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,
+ CS35L56_AMP_SHORT_ERR_EINT1_MASK,
+ 0);
+ regmap_update_bits(cs35l56->regmap, CS35L56_IRQ1_MASK_8,
+ CS35L56_TEMP_ERR_EINT1_MASK,
+ 0);
+
+ if (!cs35l56->reset_gpio) {
+ dev_dbg(cs35l56->dev, "No reset gpio: using soft reset\n");
+ cs35l56_system_reset(cs35l56);
+ if (cs35l56->sdw_peripheral) {
+ /* Keep alive while we wait for re-enumeration */
+ pm_runtime_get_noresume(cs35l56->dev);
+ return 0;
+ }
+ }
+
+post_soft_reset:
+ if (cs35l56->soft_resetting) {
+ cs35l56->soft_resetting = false;
+
+ /* Done re-enumerating after one-time init so release the keep-alive */
+ if (cs35l56->sdw_peripheral && !cs35l56->init_done)
+ pm_runtime_put_noidle(cs35l56->dev);
+
+ regcache_mark_dirty(cs35l56->regmap);
+ ret = cs35l56_wait_for_firmware_boot(cs35l56);
+ if (ret)
+ return ret;
+
+ dev_dbg(cs35l56->dev, "Firmware rebooted after soft reset\n");
+ }
+
+ /* Disable auto-hibernate so that runtime_pm has control */
+ ret = cs35l56_mbox_send(cs35l56, CS35L56_MBOX_CMD_PREVENT_AUTO_HIBERNATE);
+ if (ret)
+ return ret;
+
+ /* Populate soft registers in the regmap cache */
+ cs35l56_reread_firmware_registers(cs35l56->dev, cs35l56->regmap);
+
+ /* Registers could be dirty after soft reset or SoundWire enumeration */
+ regcache_sync(cs35l56->regmap);
+
+ cs35l56->init_done = true;
+ complete(&cs35l56->init_completion);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_init, SND_SOC_CS35L56_CORE);
+
+void cs35l56_remove(struct cs35l56_private *cs35l56)
+{
+ cs35l56->init_done = false;
+
+ /*
+ * WAKE IRQs unmask if CS35L56 hibernates so free the handler to
+ * prevent it racing with remove().
+ */
+ if (cs35l56->irq)
+ devm_free_irq(cs35l56->dev, cs35l56->irq, cs35l56);
+
+ flush_workqueue(cs35l56->dsp_wq);
+ destroy_workqueue(cs35l56->dsp_wq);
+
+ pm_runtime_suspend(cs35l56->dev);
+ pm_runtime_disable(cs35l56->dev);
+
+ regcache_cache_only(cs35l56->regmap, true);
+
+ kfree(cs35l56->dsp.system_name);
+
+ gpiod_set_value_cansleep(cs35l56->reset_gpio, 0);
+ regulator_bulk_disable(ARRAY_SIZE(cs35l56->supplies), cs35l56->supplies);
+}
+EXPORT_SYMBOL_NS_GPL(cs35l56_remove, SND_SOC_CS35L56_CORE);
+
+const struct dev_pm_ops cs35l56_pm_ops_i2c_spi = {
+ SET_RUNTIME_PM_OPS(cs35l56_runtime_suspend, cs35l56_runtime_resume_i2c_spi, NULL)
+ SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend, cs35l56_system_resume)
+ LATE_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_late, cs35l56_system_resume_early)
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(cs35l56_system_suspend_no_irq, cs35l56_system_resume_no_irq)
+};
+EXPORT_SYMBOL_NS_GPL(cs35l56_pm_ops_i2c_spi, SND_SOC_CS35L56_CORE);
+
+MODULE_DESCRIPTION("ASoC CS35L56 driver");
+MODULE_IMPORT_NS(SND_SOC_CS35L56_SHARED);
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.cirrus.com>");
+MODULE_AUTHOR("Simon Trimmer <simont@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l56.h b/sound/soc/codecs/cs35l56.h
new file mode 100644
index 000000000000..1f7894662fcb
--- /dev/null
+++ b/sound/soc/codecs/cs35l56.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Driver for Cirrus Logic CS35L56 smart amp
+ *
+ * Copyright (C) 2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#ifndef CS35L56_H
+#define CS35L56_H
+
+#include <linux/completion.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/workqueue.h>
+#include <sound/cs35l56.h>
+#include "wm_adsp.h"
+
+#define CS35L56_SDW_GEN_INT_STAT_1 0xc0
+#define CS35L56_SDW_GEN_INT_MASK_1 0xc1
+#define CS35L56_SDW_INT_MASK_CODEC_IRQ BIT(0)
+
+#define CS35L56_SDW_INVALID_BUS_SCALE 0xf
+
+#define CS35L56_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+#define CS35L56_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \
+ | SNDRV_PCM_FMTBIT_S32_LE)
+
+#define CS35L56_RATES (SNDRV_PCM_RATE_48000)
+
+struct sdw_slave;
+
+struct cs35l56_private {
+ struct wm_adsp dsp; /* must be first member */
+ struct work_struct dsp_work;
+ struct workqueue_struct *dsp_wq;
+ struct mutex irq_lock;
+ struct snd_soc_component *component;
+ struct device *dev;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[CS35L56_NUM_BULK_SUPPLIES];
+ int irq;
+ struct sdw_slave *sdw_peripheral;
+ u8 rev;
+ struct work_struct sdw_irq_work;
+ bool secured;
+ bool sdw_irq_no_unmask;
+ bool soft_resetting;
+ bool init_done;
+ bool sdw_attached;
+ bool fw_patched;
+ bool can_hibernate;
+ struct completion init_completion;
+ struct gpio_desc *reset_gpio;
+
+ u32 rx_mask;
+ u32 tx_mask;
+ u8 asp_slot_width;
+ u8 asp_slot_count;
+ bool tdm_mode;
+ bool sysclk_set;
+ u8 old_sdw_clock_scale;
+};
+
+extern const struct dev_pm_ops cs35l56_pm_ops_i2c_spi;
+
+int cs35l56_runtime_suspend(struct device *dev);
+int cs35l56_runtime_resume_common(struct cs35l56_private *cs35l56);
+int cs35l56_system_suspend(struct device *dev);
+int cs35l56_system_suspend_late(struct device *dev);
+int cs35l56_system_suspend_no_irq(struct device *dev);
+int cs35l56_system_resume_no_irq(struct device *dev);
+int cs35l56_system_resume_early(struct device *dev);
+int cs35l56_system_resume(struct device *dev);
+irqreturn_t cs35l56_irq(int irq, void *data);
+int cs35l56_irq_request(struct cs35l56_private *cs35l56, int irq);
+int cs35l56_common_probe(struct cs35l56_private *cs35l56);
+int cs35l56_init(struct cs35l56_private *cs35l56);
+void cs35l56_remove(struct cs35l56_private *cs35l56);
+
+#endif /* ifndef CS35L56_H */
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
index 0e8a7cf0da50..4033be1c3bc1 100644
--- a/sound/soc/codecs/cs4271-i2c.c
+++ b/sound/soc/codecs/cs4271-i2c.c
@@ -17,7 +17,6 @@ static int cs4271_i2c_probe(struct i2c_client *client)
config = cs4271_regmap_config;
config.reg_bits = 8;
- config.val_bits = 8;
return cs4271_probe(&client->dev,
devm_regmap_init_i2c(client, &config));
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
index 7ef0a66b7778..4feb80436bd9 100644
--- a/sound/soc/codecs/cs4271-spi.c
+++ b/sound/soc/codecs/cs4271-spi.c
@@ -17,7 +17,6 @@ static int cs4271_spi_probe(struct spi_device *spi)
config = cs4271_regmap_config;
config.reg_bits = 16;
- config.val_bits = 8;
config.read_flag_mask = 0x21;
config.write_flag_mask = 0x20;
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 2021cf442606..188b8b43c524 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -689,8 +689,8 @@ const struct regmap_config cs4271_regmap_config = {
.reg_defaults = cs4271_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-
+ .cache_type = REGCACHE_FLAT,
+ .val_bits = 8,
.volatile_reg = cs4271_volatile_reg,
};
EXPORT_SYMBOL_GPL(cs4271_regmap_config);
diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c
index 7b539ee55499..eeab07c850f9 100644
--- a/sound/soc/codecs/cs42l42-sdw.c
+++ b/sound/soc/codecs/cs42l42-sdw.c
@@ -152,9 +152,6 @@ static int cs42l42_sdw_port_prep(struct sdw_slave *slave,
static int cs42l42_sdw_dai_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- if (!sdw_stream)
- return 0;
-
snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c
index 06c4214382e3..a6538dab6639 100644
--- a/sound/soc/codecs/cs47l15.c
+++ b/sound/soc/codecs/cs47l15.c
@@ -1468,7 +1468,7 @@ error_core:
return ret;
}
-static int cs47l15_remove(struct platform_device *pdev)
+static void cs47l15_remove(struct platform_device *pdev)
{
struct cs47l15 *cs47l15 = platform_get_drvdata(pdev);
@@ -1482,8 +1482,6 @@ static int cs47l15_remove(struct platform_device *pdev)
madera_free_irq(cs47l15->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l15);
madera_free_overheat(&cs47l15->core);
madera_core_free(&cs47l15->core);
-
- return 0;
}
static struct platform_driver cs47l15_codec_driver = {
@@ -1491,7 +1489,7 @@ static struct platform_driver cs47l15_codec_driver = {
.name = "cs47l15-codec",
},
.probe = &cs47l15_probe,
- .remove = &cs47l15_remove,
+ .remove_new = cs47l15_remove,
};
module_platform_driver(cs47l15_codec_driver);
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index f9a2b865d717..a07b621d463e 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -1319,7 +1319,7 @@ err_dsp_irq:
return ret;
}
-static int cs47l24_remove(struct platform_device *pdev)
+static void cs47l24_remove(struct platform_device *pdev)
{
struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
struct arizona *arizona = cs47l24->core.arizona;
@@ -1333,8 +1333,6 @@ static int cs47l24_remove(struct platform_device *pdev)
arizona_set_irq_wake(arizona, ARIZONA_IRQ_DSP_IRQ1, 0);
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
-
- return 0;
}
static struct platform_driver cs47l24_codec_driver = {
@@ -1342,7 +1340,7 @@ static struct platform_driver cs47l24_codec_driver = {
.name = "cs47l24-codec",
},
.probe = cs47l24_probe,
- .remove = cs47l24_remove,
+ .remove_new = cs47l24_remove,
};
module_platform_driver(cs47l24_codec_driver);
diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c
index c1032d6c9143..c05c80c16c84 100644
--- a/sound/soc/codecs/cs47l35.c
+++ b/sound/soc/codecs/cs47l35.c
@@ -1744,7 +1744,7 @@ error_core:
return ret;
}
-static int cs47l35_remove(struct platform_device *pdev)
+static void cs47l35_remove(struct platform_device *pdev)
{
struct cs47l35 *cs47l35 = platform_get_drvdata(pdev);
int i;
@@ -1758,8 +1758,6 @@ static int cs47l35_remove(struct platform_device *pdev)
madera_free_irq(cs47l35->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l35);
madera_free_overheat(&cs47l35->core);
madera_core_free(&cs47l35->core);
-
- return 0;
}
static struct platform_driver cs47l35_codec_driver = {
@@ -1767,7 +1765,7 @@ static struct platform_driver cs47l35_codec_driver = {
.name = "cs47l35-codec",
},
.probe = &cs47l35_probe,
- .remove = &cs47l35_remove,
+ .remove_new = cs47l35_remove,
};
module_platform_driver(cs47l35_codec_driver);
diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c
index 215d8211aa59..dd7997a53e70 100644
--- a/sound/soc/codecs/cs47l85.c
+++ b/sound/soc/codecs/cs47l85.c
@@ -2695,7 +2695,7 @@ error_core:
return ret;
}
-static int cs47l85_remove(struct platform_device *pdev)
+static void cs47l85_remove(struct platform_device *pdev)
{
struct cs47l85 *cs47l85 = platform_get_drvdata(pdev);
int i;
@@ -2709,8 +2709,6 @@ static int cs47l85_remove(struct platform_device *pdev)
madera_free_irq(cs47l85->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l85);
madera_free_overheat(&cs47l85->core);
madera_core_free(&cs47l85->core);
-
- return 0;
}
static struct platform_driver cs47l85_codec_driver = {
@@ -2718,7 +2716,7 @@ static struct platform_driver cs47l85_codec_driver = {
.name = "cs47l85-codec",
},
.probe = &cs47l85_probe,
- .remove = &cs47l85_remove,
+ .remove_new = cs47l85_remove,
};
module_platform_driver(cs47l85_codec_driver);
diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c
index 1ad6526c7871..cdd5e7e20b5d 100644
--- a/sound/soc/codecs/cs47l90.c
+++ b/sound/soc/codecs/cs47l90.c
@@ -2618,7 +2618,7 @@ error_core:
return ret;
}
-static int cs47l90_remove(struct platform_device *pdev)
+static void cs47l90_remove(struct platform_device *pdev)
{
struct cs47l90 *cs47l90 = platform_get_drvdata(pdev);
int i;
@@ -2633,8 +2633,6 @@ static int cs47l90_remove(struct platform_device *pdev)
madera_set_irq_wake(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, 0);
madera_free_irq(cs47l90->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l90);
madera_core_free(&cs47l90->core);
-
- return 0;
}
static struct platform_driver cs47l90_codec_driver = {
@@ -2642,7 +2640,7 @@ static struct platform_driver cs47l90_codec_driver = {
.name = "cs47l90-codec",
},
.probe = &cs47l90_probe,
- .remove = &cs47l90_remove,
+ .remove_new = cs47l90_remove,
};
module_platform_driver(cs47l90_codec_driver);
diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c
index fe576d64e089..bc4d311d4778 100644
--- a/sound/soc/codecs/cs47l92.c
+++ b/sound/soc/codecs/cs47l92.c
@@ -2068,7 +2068,7 @@ error_core:
return ret;
}
-static int cs47l92_remove(struct platform_device *pdev)
+static void cs47l92_remove(struct platform_device *pdev)
{
struct cs47l92 *cs47l92 = platform_get_drvdata(pdev);
@@ -2081,8 +2081,6 @@ static int cs47l92_remove(struct platform_device *pdev)
madera_free_irq(cs47l92->core.madera, MADERA_IRQ_DSP_IRQ1, cs47l92);
madera_core_free(&cs47l92->core);
-
- return 0;
}
static struct platform_driver cs47l92_codec_driver = {
@@ -2090,7 +2088,7 @@ static struct platform_driver cs47l92_codec_driver = {
.name = "cs47l92-codec",
},
.probe = &cs47l92_probe,
- .remove = &cs47l92_remove,
+ .remove_new = cs47l92_remove,
};
module_platform_driver(cs47l92_codec_driver);
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index 91372909d184..d9c28e701613 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -2893,14 +2893,10 @@ static int da7218_probe(struct snd_soc_component *component)
da7218_handle_pdata(component);
/* Check if MCLK provided, if not the clock is NULL */
- da7218->mclk = devm_clk_get(component->dev, "mclk");
+ da7218->mclk = devm_clk_get_optional(component->dev, "mclk");
if (IS_ERR(da7218->mclk)) {
- if (PTR_ERR(da7218->mclk) != -ENOENT) {
- ret = PTR_ERR(da7218->mclk);
- goto err_disable_reg;
- } else {
- da7218->mclk = NULL;
- }
+ ret = PTR_ERR(da7218->mclk);
+ goto err_disable_reg;
}
/* Default PC to free-running */
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index e3d398b8f54e..993a0d00bc48 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -342,36 +342,17 @@ static void da7219_aad_hptest_work(struct work_struct *work)
static void da7219_aad_jack_det_work(struct work_struct *work)
{
struct da7219_aad_priv *da7219_aad =
- container_of(work, struct da7219_aad_priv, jack_det_work);
+ container_of(work, struct da7219_aad_priv, jack_det_work.work);
struct snd_soc_component *component = da7219_aad->component;
- u8 srm_st;
- mutex_lock(&da7219_aad->jack_det_mutex);
-
- srm_st = snd_soc_component_read(component, DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK;
- msleep(da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 4);
/* Enable ground switch */
snd_soc_component_update_bits(component, 0xFB, 0x01, 0x01);
-
- mutex_unlock(&da7219_aad->jack_det_mutex);
}
-
/*
* IRQ
*/
-static irqreturn_t da7219_aad_pre_irq_thread(int irq, void *data)
-{
-
- struct da7219_aad_priv *da7219_aad = data;
-
- if (!da7219_aad->jack_inserted)
- schedule_work(&da7219_aad->jack_det_work);
-
- return IRQ_WAKE_THREAD;
-}
-
static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
{
struct da7219_aad_priv *da7219_aad = data;
@@ -392,6 +373,18 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
/* Read status register for jack insertion & type status */
statusa = snd_soc_component_read(component, DA7219_ACCDET_STATUS_A);
+ if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_INSERTED_MASK) {
+ u8 srm_st;
+ int delay = 0;
+
+ srm_st = snd_soc_component_read(component,
+ DA7219_PLL_SRM_STS) & DA7219_PLL_SRM_STS_MCLK;
+ delay = (da7219_aad->gnd_switch_delay * ((srm_st == 0x0) ? 2 : 1) - 2);
+ queue_delayed_work(da7219_aad->aad_wq,
+ &da7219_aad->jack_det_work,
+ msecs_to_jiffies(delay));
+ }
+
/* Clear events */
regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
events, DA7219_AAD_IRQ_REG_MAX);
@@ -400,9 +393,6 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
statusa);
- if (!da7219_aad->jack_inserted)
- cancel_work_sync(&da7219_aad->jack_det_work);
-
if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
/* Jack Insertion */
if (events[DA7219_AAD_IRQ_REG_A] &
@@ -430,9 +420,9 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
if (statusa & DA7219_JACK_TYPE_STS_MASK) {
report |= SND_JACK_HEADSET;
mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
- schedule_work(&da7219_aad->btn_det_work);
+ queue_work(da7219_aad->aad_wq, &da7219_aad->btn_det_work);
} else {
- schedule_work(&da7219_aad->hptest_work);
+ queue_work(da7219_aad->aad_wq, &da7219_aad->hptest_work);
}
}
@@ -465,6 +455,7 @@ static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
da7219_aad->jack_inserted = false;
/* Cancel any pending work */
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
cancel_work_sync(&da7219_aad->btn_det_work);
cancel_work_sync(&da7219_aad->hptest_work);
@@ -964,13 +955,19 @@ int da7219_aad_init(struct snd_soc_component *component)
snd_soc_component_update_bits(component, DA7219_ACCDET_CONFIG_1,
DA7219_BUTTON_CONFIG_MASK, 0);
+ da7219_aad_handle_gnd_switch_time(component);
+
+ da7219_aad->aad_wq = create_singlethread_workqueue("da7219-aad");
+ if (!da7219_aad->aad_wq) {
+ dev_err(component->dev, "Failed to create aad workqueue\n");
+ return -ENOMEM;
+ }
+
+ INIT_DELAYED_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work);
INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
- INIT_WORK(&da7219_aad->jack_det_work, da7219_aad_jack_det_work);
-
- mutex_init(&da7219_aad->jack_det_mutex);
- ret = request_threaded_irq(da7219_aad->irq, da7219_aad_pre_irq_thread,
+ ret = request_threaded_irq(da7219_aad->irq, NULL,
da7219_aad_irq_thread,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"da7219-aad", da7219_aad);
@@ -984,8 +981,6 @@ int da7219_aad_init(struct snd_soc_component *component)
regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
&mask, DA7219_AAD_IRQ_REG_MAX);
- da7219_aad_handle_gnd_switch_time(component);
-
return 0;
}
@@ -1002,8 +997,10 @@ void da7219_aad_exit(struct snd_soc_component *component)
free_irq(da7219_aad->irq, da7219_aad);
+ cancel_delayed_work_sync(&da7219_aad->jack_det_work);
cancel_work_sync(&da7219_aad->btn_det_work);
cancel_work_sync(&da7219_aad->hptest_work);
+ destroy_workqueue(da7219_aad->aad_wq);
}
/*
@@ -1031,4 +1028,5 @@ int da7219_aad_probe(struct i2c_client *i2c)
MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_AUTHOR("David Rau <David.Rau.opensource@dm.renesas.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
index be87ee47edde..fbfbf3e67918 100644
--- a/sound/soc/codecs/da7219-aad.h
+++ b/sound/soc/codecs/da7219-aad.h
@@ -197,9 +197,8 @@ struct da7219_aad_priv {
struct work_struct btn_det_work;
struct work_struct hptest_work;
- struct work_struct jack_det_work;
-
- struct mutex jack_det_mutex;
+ struct delayed_work jack_det_work;
+ struct workqueue_struct *aad_wq;
struct snd_soc_jack *jack;
bool micbias_resume_enable;
diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c
index 056c3082fe02..a27d80956459 100644
--- a/sound/soc/codecs/es8316.c
+++ b/sound/soc/codecs/es8316.c
@@ -803,14 +803,15 @@ static const struct snd_soc_component_driver soc_component_dev_es8316 = {
.endianness = 1,
};
-static const struct regmap_range es8316_volatile_ranges[] = {
- regmap_reg_range(ES8316_GPIO_FLAG, ES8316_GPIO_FLAG),
-};
-
-static const struct regmap_access_table es8316_volatile_table = {
- .yes_ranges = es8316_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(es8316_volatile_ranges),
-};
+static bool es8316_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8316_GPIO_FLAG:
+ return true;
+ default:
+ return false;
+ }
+}
static const struct regmap_config es8316_regmap = {
.reg_bits = 8,
@@ -818,7 +819,7 @@ static const struct regmap_config es8316_regmap = {
.use_single_read = true,
.use_single_write = true,
.max_register = 0x53,
- .volatile_table = &es8316_volatile_table,
+ .volatile_reg = es8316_volatile_reg,
.cache_type = REGCACHE_RBTREE,
};
@@ -842,12 +843,14 @@ static int es8316_i2c_probe(struct i2c_client *i2c_client)
es8316->irq = i2c_client->irq;
mutex_init(&es8316->lock);
- ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
- IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
- "es8316", es8316);
- if (ret) {
- dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
- es8316->irq = -ENXIO;
+ if (es8316->irq > 0) {
+ ret = devm_request_threaded_irq(dev, es8316->irq, NULL, es8316_irq,
+ IRQF_TRIGGER_HIGH | IRQF_ONESHOT | IRQF_NO_AUTOEN,
+ "es8316", es8316);
+ if (ret) {
+ dev_warn(dev, "Failed to get IRQ %d: %d\n", es8316->irq, ret);
+ es8316->irq = -ENXIO;
+ }
}
return devm_snd_soc_register_component(&i2c_client->dev,
diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c
index 8222cde6e3b9..11320423c69c 100644
--- a/sound/soc/codecs/inno_rk3036.c
+++ b/sound/soc/codecs/inno_rk3036.c
@@ -457,13 +457,11 @@ static int rk3036_codec_platform_probe(struct platform_device *pdev)
return ret;
}
-static int rk3036_codec_platform_remove(struct platform_device *pdev)
+static void rk3036_codec_platform_remove(struct platform_device *pdev)
{
struct rk3036_codec_priv *priv = dev_get_drvdata(&pdev->dev);
clk_disable_unprepare(priv->pclk);
-
- return 0;
}
static const struct of_device_id rk3036_codec_of_match[] __maybe_unused = {
@@ -478,7 +476,7 @@ static struct platform_driver rk3036_codec_platform_driver = {
.of_match_table = of_match_ptr(rk3036_codec_of_match),
},
.probe = rk3036_codec_platform_probe,
- .remove = rk3036_codec_platform_remove,
+ .remove_new = rk3036_codec_platform_remove,
};
module_platform_driver(rk3036_codec_platform_driver);
diff --git a/sound/soc/codecs/lpass-macro-common.c b/sound/soc/codecs/lpass-macro-common.c
index 1b9082d237c1..f54baaad54d4 100644
--- a/sound/soc/codecs/lpass-macro-common.c
+++ b/sound/soc/codecs/lpass-macro-common.c
@@ -16,7 +16,7 @@ struct lpass_macro *lpass_macro_pds_init(struct device *dev)
struct lpass_macro *l_pds;
int ret;
- if (!of_find_property(dev->of_node, "power-domains", NULL))
+ if (!of_property_present(dev->of_node, "power-domains"))
return NULL;
l_pds = devm_kzalloc(dev, sizeof(*l_pds), GFP_KERNEL);
diff --git a/sound/soc/codecs/lpass-macro-common.h b/sound/soc/codecs/lpass-macro-common.h
index f2cbf9fe2c6e..4eb886565ea3 100644
--- a/sound/soc/codecs/lpass-macro-common.h
+++ b/sound/soc/codecs/lpass-macro-common.h
@@ -6,6 +6,9 @@
#ifndef __LPASS_MACRO_COMMON_H__
#define __LPASS_MACRO_COMMON_H__
+/* NPL clock is expected */
+#define LPASS_MACRO_FLAG_HAS_NPL_CLOCK BIT(0)
+
struct lpass_macro {
struct device *macro_pd;
struct device *dcodec_pd;
diff --git a/sound/soc/codecs/lpass-rx-macro.c b/sound/soc/codecs/lpass-rx-macro.c
index faba4237bd3d..685ca95ef4a9 100644
--- a/sound/soc/codecs/lpass-rx-macro.c
+++ b/sound/soc/codecs/lpass-rx-macro.c
@@ -3491,7 +3491,10 @@ static int rx_macro_register_mclk_output(struct rx_macro *rx)
struct clk_init_data init;
int ret;
- parent_clk_name = __clk_get_name(rx->npl);
+ if (rx->npl)
+ parent_clk_name = __clk_get_name(rx->npl);
+ else
+ parent_clk_name = __clk_get_name(rx->mclk);
init.name = clk_name;
init.ops = &swclk_gate_ops;
@@ -3521,10 +3524,13 @@ static const struct snd_soc_component_driver rx_macro_component_drv = {
static int rx_macro_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
+ kernel_ulong_t flags;
struct rx_macro *rx;
void __iomem *base;
int ret;
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
rx = devm_kzalloc(dev, sizeof(*rx), GFP_KERNEL);
if (!rx)
return -ENOMEM;
@@ -3541,9 +3547,11 @@ static int rx_macro_probe(struct platform_device *pdev)
if (IS_ERR(rx->mclk))
return PTR_ERR(rx->mclk);
- rx->npl = devm_clk_get(dev, "npl");
- if (IS_ERR(rx->npl))
- return PTR_ERR(rx->npl);
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ rx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(rx->npl))
+ return PTR_ERR(rx->npl);
+ }
rx->fsgen = devm_clk_get(dev, "fsgen");
if (IS_ERR(rx->fsgen))
@@ -3639,7 +3647,7 @@ err:
return ret;
}
-static int rx_macro_remove(struct platform_device *pdev)
+static void rx_macro_remove(struct platform_device *pdev)
{
struct rx_macro *rx = dev_get_drvdata(&pdev->dev);
@@ -3650,15 +3658,25 @@ static int rx_macro_remove(struct platform_device *pdev)
clk_disable_unprepare(rx->dcodec);
lpass_macro_pds_exit(rx->pds);
-
- return 0;
}
static const struct of_device_id rx_macro_dt_match[] = {
- { .compatible = "qcom,sc7280-lpass-rx-macro" },
- { .compatible = "qcom,sm8250-lpass-rx-macro" },
- { .compatible = "qcom,sm8450-lpass-rx-macro" },
- { .compatible = "qcom,sc8280xp-lpass-rx-macro" },
+ {
+ .compatible = "qcom,sc7280-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+
+ }, {
+ .compatible = "qcom,sm8250-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-rx-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-rx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, rx_macro_dt_match);
@@ -3723,7 +3741,7 @@ static struct platform_driver rx_macro_driver = {
.pm = &rx_macro_pm_ops,
},
.probe = rx_macro_probe,
- .remove = rx_macro_remove,
+ .remove_new = rx_macro_remove,
};
module_platform_driver(rx_macro_driver);
diff --git a/sound/soc/codecs/lpass-tx-macro.c b/sound/soc/codecs/lpass-tx-macro.c
index 589c490a8c48..da6fcf7f0991 100644
--- a/sound/soc/codecs/lpass-tx-macro.c
+++ b/sound/soc/codecs/lpass-tx-macro.c
@@ -1915,7 +1915,10 @@ static int tx_macro_register_mclk_output(struct tx_macro *tx)
struct clk_init_data init;
int ret;
- parent_clk_name = __clk_get_name(tx->npl);
+ if (tx->npl)
+ parent_clk_name = __clk_get_name(tx->npl);
+ else
+ parent_clk_name = __clk_get_name(tx->mclk);
init.name = clk_name;
init.ops = &swclk_gate_ops;
@@ -1946,10 +1949,13 @@ static int tx_macro_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
+ kernel_ulong_t flags;
struct tx_macro *tx;
void __iomem *base;
int ret, reg;
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
tx = devm_kzalloc(dev, sizeof(*tx), GFP_KERNEL);
if (!tx)
return -ENOMEM;
@@ -1966,9 +1972,11 @@ static int tx_macro_probe(struct platform_device *pdev)
if (IS_ERR(tx->mclk))
return PTR_ERR(tx->mclk);
- tx->npl = devm_clk_get(dev, "npl");
- if (IS_ERR(tx->npl))
- return PTR_ERR(tx->npl);
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ tx->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(tx->npl))
+ return PTR_ERR(tx->npl);
+ }
tx->fsgen = devm_clk_get(dev, "fsgen");
if (IS_ERR(tx->fsgen))
@@ -2076,7 +2084,7 @@ err:
return ret;
}
-static int tx_macro_remove(struct platform_device *pdev)
+static void tx_macro_remove(struct platform_device *pdev)
{
struct tx_macro *tx = dev_get_drvdata(&pdev->dev);
@@ -2087,8 +2095,6 @@ static int tx_macro_remove(struct platform_device *pdev)
clk_disable_unprepare(tx->fsgen);
lpass_macro_pds_exit(tx->pds);
-
- return 0;
}
static int __maybe_unused tx_macro_runtime_suspend(struct device *dev)
@@ -2145,10 +2151,21 @@ static const struct dev_pm_ops tx_macro_pm_ops = {
};
static const struct of_device_id tx_macro_dt_match[] = {
- { .compatible = "qcom,sc7280-lpass-tx-macro" },
- { .compatible = "qcom,sm8250-lpass-tx-macro" },
- { .compatible = "qcom,sm8450-lpass-tx-macro" },
- { .compatible = "qcom,sc8280xp-lpass-tx-macro" },
+ {
+ .compatible = "qcom,sc7280-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8250-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-tx-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-tx-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
{ }
};
MODULE_DEVICE_TABLE(of, tx_macro_dt_match);
@@ -2160,7 +2177,7 @@ static struct platform_driver tx_macro_driver = {
.pm = &tx_macro_pm_ops,
},
.probe = tx_macro_probe,
- .remove = tx_macro_remove,
+ .remove_new = tx_macro_remove,
};
module_platform_driver(tx_macro_driver);
diff --git a/sound/soc/codecs/lpass-va-macro.c b/sound/soc/codecs/lpass-va-macro.c
index fd62817d29a0..74724448da50 100644
--- a/sound/soc/codecs/lpass-va-macro.c
+++ b/sound/soc/codecs/lpass-va-macro.c
@@ -1575,7 +1575,7 @@ err:
return ret;
}
-static int va_macro_remove(struct platform_device *pdev)
+static void va_macro_remove(struct platform_device *pdev)
{
struct va_macro *va = dev_get_drvdata(&pdev->dev);
@@ -1584,8 +1584,6 @@ static int va_macro_remove(struct platform_device *pdev)
clk_disable_unprepare(va->macro);
lpass_macro_pds_exit(va->pds);
-
- return 0;
}
static int __maybe_unused va_macro_runtime_suspend(struct device *dev)
@@ -1639,7 +1637,7 @@ static struct platform_driver va_macro_driver = {
.pm = &va_macro_pm_ops,
},
.probe = va_macro_probe,
- .remove = va_macro_remove,
+ .remove_new = va_macro_remove,
};
module_platform_driver(va_macro_driver);
diff --git a/sound/soc/codecs/lpass-wsa-macro.c b/sound/soc/codecs/lpass-wsa-macro.c
index 3f6f1bdd4e03..8ba7dc89daaa 100644
--- a/sound/soc/codecs/lpass-wsa-macro.c
+++ b/sound/soc/codecs/lpass-wsa-macro.c
@@ -14,6 +14,8 @@
#include <linux/pm_runtime.h>
#include <linux/of_platform.h>
#include <sound/tlv.h>
+
+#include "lpass-macro-common.h"
#include "lpass-wsa-macro.h"
#define CDC_WSA_CLK_RST_CTRL_MCLK_CONTROL (0x0000)
@@ -2346,7 +2348,10 @@ static int wsa_macro_register_mclk_output(struct wsa_macro *wsa)
struct clk_init_data init;
int ret;
- parent_clk_name = __clk_get_name(wsa->npl);
+ if (wsa->npl)
+ parent_clk_name = __clk_get_name(wsa->npl);
+ else
+ parent_clk_name = __clk_get_name(wsa->mclk);
init.name = "mclk";
of_property_read_string(dev_of_node(dev), "clock-output-names",
@@ -2379,9 +2384,12 @@ static int wsa_macro_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct wsa_macro *wsa;
+ kernel_ulong_t flags;
void __iomem *base;
int ret;
+ flags = (kernel_ulong_t)device_get_match_data(dev);
+
wsa = devm_kzalloc(dev, sizeof(*wsa), GFP_KERNEL);
if (!wsa)
return -ENOMEM;
@@ -2398,9 +2406,11 @@ static int wsa_macro_probe(struct platform_device *pdev)
if (IS_ERR(wsa->mclk))
return PTR_ERR(wsa->mclk);
- wsa->npl = devm_clk_get(dev, "npl");
- if (IS_ERR(wsa->npl))
- return PTR_ERR(wsa->npl);
+ if (flags & LPASS_MACRO_FLAG_HAS_NPL_CLOCK) {
+ wsa->npl = devm_clk_get(dev, "npl");
+ if (IS_ERR(wsa->npl))
+ return PTR_ERR(wsa->npl);
+ }
wsa->fsgen = devm_clk_get(dev, "fsgen");
if (IS_ERR(wsa->fsgen))
@@ -2486,7 +2496,7 @@ err:
}
-static int wsa_macro_remove(struct platform_device *pdev)
+static void wsa_macro_remove(struct platform_device *pdev)
{
struct wsa_macro *wsa = dev_get_drvdata(&pdev->dev);
@@ -2495,8 +2505,6 @@ static int wsa_macro_remove(struct platform_device *pdev)
clk_disable_unprepare(wsa->mclk);
clk_disable_unprepare(wsa->npl);
clk_disable_unprepare(wsa->fsgen);
-
- return 0;
}
static int __maybe_unused wsa_macro_runtime_suspend(struct device *dev)
@@ -2553,10 +2561,21 @@ static const struct dev_pm_ops wsa_macro_pm_ops = {
};
static const struct of_device_id wsa_macro_dt_match[] = {
- {.compatible = "qcom,sc7280-lpass-wsa-macro"},
- {.compatible = "qcom,sm8250-lpass-wsa-macro"},
- {.compatible = "qcom,sm8450-lpass-wsa-macro"},
- {.compatible = "qcom,sc8280xp-lpass-wsa-macro" },
+ {
+ .compatible = "qcom,sc7280-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8250-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8450-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ }, {
+ .compatible = "qcom,sm8550-lpass-wsa-macro",
+ }, {
+ .compatible = "qcom,sc8280xp-lpass-wsa-macro",
+ .data = (void *)LPASS_MACRO_FLAG_HAS_NPL_CLOCK,
+ },
{}
};
MODULE_DEVICE_TABLE(of, wsa_macro_dt_match);
@@ -2568,7 +2587,7 @@ static struct platform_driver wsa_macro_driver = {
.pm = &wsa_macro_pm_ops,
},
.probe = wsa_macro_probe,
- .remove = wsa_macro_remove,
+ .remove_new = wsa_macro_remove,
};
module_platform_driver(wsa_macro_driver);
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
new file mode 100644
index 000000000000..dcce06bff756
--- /dev/null
+++ b/sound/soc/codecs/max98363.c
@@ -0,0 +1,464 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2022, Analog Devices Inc.
+
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/soundwire/sdw_type.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#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},
+ {MAX98363_R2030_TONE_GEN_CFG, 0x0},
+ {MAX98363_R203F_TONE_GEN_EN, 0x0},
+ {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)
+{
+ switch (reg) {
+ case MAX98363_R2001_INTR_RAW:
+ case MAX98363_R2003_INTR_STATE:
+ case MAX98363_R2005_INTR_FALG:
+ case MAX98363_R2007_INTR_EN:
+ case MAX98363_R2009_INTR_CLR:
+ case MAX98363_R2021_ERR_MON_CTRL ... MAX98363_R2023_SPK_MON_DURATION:
+ case MAX98363_R2030_TONE_GEN_CFG:
+ case MAX98363_R203F_TONE_GEN_EN:
+ case MAX98363_R2040_AMP_VOL:
+ case MAX98363_R2041_AMP_GAIN:
+ case MAX98363_R2042_DSP_CFG:
+ case MAX98363_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98363_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98363_R2001_INTR_RAW:
+ case MAX98363_R2003_INTR_STATE:
+ case MAX98363_R2005_INTR_FALG:
+ case MAX98363_R2007_INTR_EN:
+ case MAX98363_R2009_INTR_CLR:
+ case MAX98363_R21FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config max98363_sdw_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .max_register = MAX98363_R21FF_REV_ID,
+ .reg_defaults = max98363_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98363_reg),
+ .readable_reg = max98363_readable_register,
+ .volatile_reg = max98363_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int max98363_suspend(struct device *dev)
+{
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+
+ regcache_cache_only(max98363->regmap, true);
+ regcache_mark_dirty(max98363->regmap);
+
+ return 0;
+}
+
+#define MAX98363_PROBE_TIMEOUT 5000
+
+static int max98363_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!max98363->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(MAX98363_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(dev, "Initialization not complete, timed out\n");
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+
+ slave->unattach_request = 0;
+ regcache_cache_only(max98363->regmap, false);
+ regcache_sync(max98363->regmap);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(max98363_pm, max98363_suspend, max98363_resume, NULL);
+
+static int max98363_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+
+ /* BITMAP: 00000010 Dataport 1 is active */
+ prop->sink_ports = BIT(1);
+ prop->paging_support = true;
+ prop->clk_stop_timeout = 20;
+ prop->simple_clk_stop_capable = true;
+ prop->clock_reg_supported = true;
+
+ 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;
+
+ i = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_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++;
+ }
+
+ return 0;
+}
+
+static int max98363_io_init(struct sdw_slave *slave)
+{
+ struct device *dev = &slave->dev;
+ struct max98363_priv *max98363 = dev_get_drvdata(dev);
+ int ret, reg;
+
+ if (max98363->first_hw_init) {
+ regcache_cache_only(max98363->regmap, false);
+ regcache_cache_bypass(max98363->regmap, true);
+ }
+
+ /*
+ * PM runtime is only enabled when a Slave reports as Attached
+ */
+ if (!max98363->first_hw_init) {
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* update count of parent 'active' children */
+ pm_runtime_set_active(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+ }
+
+ pm_runtime_get_noresume(dev);
+
+ ret = regmap_read(max98363->regmap, MAX98363_R21FF_REV_ID, &reg);
+ if (!ret) {
+ dev_info(dev, "Revision ID: %X\n", reg);
+ return ret;
+ }
+
+ if (max98363->first_hw_init) {
+ regcache_cache_bypass(max98363->regmap, false);
+ regcache_mark_dirty(max98363->regmap);
+ }
+
+ max98363->first_hw_init = true;
+ max98363->hw_init = true;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+#define MAX98363_RATES SNDRV_PCM_RATE_8000_192000
+#define MAX98363_FORMATS (SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98363_sdw_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 max98363_priv *max98363 =
+ 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 *stream;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ int ret;
+
+ stream = snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!stream)
+ return -EINVAL;
+
+ if (!max98363->slave)
+ return -EINVAL;
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+ return -EINVAL;
+
+ direction = SDW_DATA_DIR_RX;
+ port_config.num = 1;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+ stream_config.ch_count = params_channels(params);
+
+ if (stream_config.ch_count > runtime->hw.channels_max) {
+ stream_config.ch_count = runtime->hw.channels_max;
+ dev_info(dai->dev, "Number of channels: %d (requested: %d)\n",
+ stream_config.ch_count, params_channels(params));
+ }
+ port_config.ch_mask = GENMASK((int)stream_config.ch_count - 1, 0);
+
+ ret = sdw_stream_add_slave(max98363->slave, &stream_config,
+ &port_config, 1, stream);
+ if (ret) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return ret;
+ }
+
+ dev_dbg(component->dev, "Format supported %d", params_format(params));
+
+ return 0;
+}
+
+static int max98363_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct max98363_priv *max98363 =
+ snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!max98363->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(max98363->slave, stream);
+
+ return 0;
+}
+
+static int max98363_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 const struct snd_soc_dai_ops max98363_dai_sdw_ops = {
+ .hw_params = max98363_sdw_dai_hw_params,
+ .hw_free = max98363_pcm_hw_free,
+ .set_stream = max98363_set_sdw_stream,
+};
+
+static struct snd_soc_dai_driver max98363_dai[] = {
+ {
+ .name = "max98363-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = MAX98363_RATES,
+ .formats = MAX98363_FORMATS,
+ },
+ .ops = &max98363_dai_sdw_ops,
+ }
+};
+
+static int max98363_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct max98363_priv *max98363 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ max98363->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is SDW_SLAVE_ATTACHED
+ */
+ if (max98363->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return max98363_io_init(slave);
+}
+
+static struct sdw_slave_ops max98363_slave_ops = {
+ .read_prop = max98363_read_prop,
+ .update_status = max98363_update_status,
+};
+
+static DECLARE_TLV_DB_SCALE(max98363_digital_tlv, -6350, 50, 1);
+static const DECLARE_TLV_DB_RANGE(max98363_spk_tlv,
+ 0, 5, TLV_DB_SCALE_ITEM(-300, 300, 0),
+);
+
+static const char * const max98363_tone_cfg_text[] = {
+ "Reserved", "0", "+FS/2", "-FS/2", "1KHz",
+ "12KHz", "8KHz", "6KHz", "4KHz", "3KHz",
+ "2KHz", "1.5KHz", "Reserved", "500Hz", "250Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_tone_cfg_enum,
+ MAX98363_R2030_TONE_GEN_CFG, 0,
+ max98363_tone_cfg_text);
+
+static const char * const max98363_spkmon_duration_text[] = {
+ "8ms", "20ms", "40ms", "60ms",
+ "80ms", "160ms", "240ms", "320ms",
+ "400ms", "480ms", "560ms", "640ms",
+ "720ms", "800ms", "880ms", "960ms"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98363_spkmon_duration_enum,
+ MAX98363_R2023_SPK_MON_DURATION, 0,
+ max98363_spkmon_duration_text);
+
+static const struct snd_kcontrol_new max98363_snd_controls[] = {
+ SOC_SINGLE_TLV("Digital Volume", MAX98363_R2040_AMP_VOL,
+ 0, 0x7F, 1, max98363_digital_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98363_R2041_AMP_GAIN,
+ 0, 10, 0, max98363_spk_tlv),
+ SOC_SINGLE("Tone Generator Switch", MAX98363_R203F_TONE_GEN_EN,
+ 0, 1, 0),
+ SOC_ENUM("Tone Config", max98363_tone_cfg_enum),
+ SOC_SINGLE("Ramp Switch", MAX98363_R2042_DSP_CFG,
+ MAX98363_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+ SOC_SINGLE("CLK Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+ MAX98363_CLOCK_MON_SHIFT, 1, 0),
+ SOC_SINGLE("SPKMON Monitor Switch", MAX98363_R2021_ERR_MON_CTRL,
+ MAX98363_SPKMON_SHIFT, 1, 0),
+ SOC_SINGLE("SPKMON Thresh", MAX98363_R2022_SPK_MON_THRESH, 0, 0xFF, 0),
+ SOC_ENUM("SPKMON Duration", max98363_spkmon_duration_enum),
+};
+
+static const struct snd_soc_dapm_widget max98363_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("AIFIN", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static const struct snd_soc_dapm_route max98363_audio_map[] = {
+ /* Plabyack */
+ {"BE_OUT", NULL, "AIFIN"},
+};
+
+static const struct snd_soc_component_driver soc_codec_dev_max98363 = {
+ .controls = max98363_snd_controls,
+ .num_controls = ARRAY_SIZE(max98363_snd_controls),
+ .dapm_widgets = max98363_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98363_dapm_widgets),
+ .dapm_routes = max98363_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98363_audio_map),
+ .use_pmdown_time = 1,
+ .endianness = 1,
+};
+
+static int max98363_init(struct sdw_slave *slave, struct regmap *regmap)
+{
+ struct max98363_priv *max98363;
+ int ret;
+ struct device *dev = &slave->dev;
+
+ /* Allocate and assign private driver data structure */
+ max98363 = devm_kzalloc(dev, sizeof(*max98363), GFP_KERNEL);
+ if (!max98363)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, max98363);
+ max98363->regmap = regmap;
+ max98363->slave = slave;
+
+ max98363->hw_init = false;
+ max98363->first_hw_init = false;
+
+ /* codec registration */
+ ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363,
+ max98363_dai,
+ ARRAY_SIZE(max98363_dai));
+ if (ret < 0)
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int max98363_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &max98363_sdw_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return max98363_init(slave, regmap);
+}
+
+static const struct sdw_device_id max98363_id[] = {
+ SDW_SLAVE_ENTRY(0x019F, 0x8363, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, max98363_id);
+
+static struct sdw_driver max98363_sdw_driver = {
+ .driver = {
+ .name = "max98363",
+ .pm = pm_ptr(&max98363_pm),
+ },
+ .probe = max98363_sdw_probe,
+ .ops = &max98363_slave_ops,
+ .id_table = max98363_id,
+};
+
+module_sdw_driver(max98363_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC MAX98363 driver SDW");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@analog.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98363.h b/sound/soc/codecs/max98363.h
new file mode 100644
index 000000000000..2b6743d3a2cf
--- /dev/null
+++ b/sound/soc/codecs/max98363.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* Copyright (c) 2022 Analog Devices Inc. */
+
+#ifndef _MAX98363_H
+#define _MAX98363_H
+
+#define MAX98363_R2000_SW_RESET 0x2000
+#define MAX98363_R2001_INTR_RAW 0x2001
+#define MAX98363_R2003_INTR_STATE 0x2003
+#define MAX98363_R2005_INTR_FALG 0x2005
+#define MAX98363_R2007_INTR_EN 0x2007
+#define MAX98363_R2009_INTR_CLR 0x2009
+#define MAX98363_R2021_ERR_MON_CTRL 0x2021
+#define MAX98363_R2022_SPK_MON_THRESH 0x2022
+#define MAX98363_R2023_SPK_MON_DURATION 0x2023
+#define MAX98363_R2030_TONE_GEN_CFG 0x2030
+#define MAX98363_R203F_TONE_GEN_EN 0x203F
+#define MAX98363_R2040_AMP_VOL 0x2040
+#define MAX98363_R2041_AMP_GAIN 0x2041
+#define MAX98363_R2042_DSP_CFG 0x2042
+#define MAX98363_R21FF_REV_ID 0x21FF
+
+/* MAX98363_R2021_ERR_MON_CTRL */
+#define MAX98363_SPKMON_SHIFT (3)
+#define MAX98363_CLOCK_MON_SHIFT (0)
+
+/* MAX98363_R2042_DSP_CFG */
+#define MAX98363_AMP_DSP_CFG_RMP_SHIFT (3)
+
+struct max98363_priv {
+ struct regmap *regmap;
+ struct sdw_slave *slave;
+ bool hw_init;
+ bool first_hw_init;
+};
+#endif
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index c9a2d4dabd3c..df92242af960 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -20,10 +20,6 @@
#include "max98373.h"
#include "max98373-sdw.h"
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
static const u32 max98373_sdw_cache_reg[] = {
MAX98373_R2054_MEAS_ADC_PVDD_CH_READBACK,
MAX98373_R2055_MEAS_ADC_THERM_CH_READBACK,
@@ -536,12 +532,12 @@ static int max98373_sdw_dai_hw_params(struct snd_pcm_substream *substream,
snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int ret, chan_sz, sampling_rate;
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!max98373->slave)
@@ -565,7 +561,7 @@ static int max98373_sdw_dai_hw_params(struct snd_pcm_substream *substream,
}
ret = sdw_stream_add_slave(max98373->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (ret) {
dev_err(dai->dev, "Unable to configure port\n");
return ret;
@@ -664,32 +660,20 @@ static int max98373_pcm_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct max98373_priv *max98373 =
snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!max98373->slave)
return -EINVAL;
- sdw_stream_remove_slave(max98373->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(max98373->slave, sdw_stream);
return 0;
}
static int max98373_set_sdw_stream(struct snd_soc_dai *dai,
void *sdw_stream, int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -697,11 +681,7 @@ static int max98373_set_sdw_stream(struct snd_soc_dai *dai,
static void max98373_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int max98373_sdw_set_tdm_slot(struct snd_soc_dai *dai,
diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c
index f90a6a7ba83b..fde055c6c894 100644
--- a/sound/soc/codecs/max98373.c
+++ b/sound/soc/codecs/max98373.c
@@ -31,7 +31,7 @@ static int max98373_dac_event(struct snd_soc_dapm_widget *w,
MAX98373_GLOBAL_EN_MASK, 1);
usleep_range(30000, 31000);
break;
- case SND_SOC_DAPM_POST_PMD:
+ case SND_SOC_DAPM_PRE_PMD:
regmap_update_bits(max98373->regmap,
MAX98373_R20FF_GLOBAL_SHDN,
MAX98373_GLOBAL_EN_MASK, 0);
@@ -64,7 +64,7 @@ static const struct snd_kcontrol_new max98373_spkfb_control =
static const struct snd_soc_dapm_widget max98373_dapm_widgets[] = {
SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback",
MAX98373_R202B_PCM_RX_EN, 0, 0, max98373_dac_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
&max98373_dai_controls),
SND_SOC_DAPM_OUTPUT("BE_OUT"),
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index e161ab037bf7..ae552d72beec 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -6,6 +6,7 @@
// Copyright 2018 Ladislav Michl <ladis@linux-mips.org>
//
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
@@ -16,6 +17,7 @@
#include "max9867.h"
struct max9867_priv {
+ struct clk *mclk;
struct regmap *regmap;
const struct snd_pcm_hw_constraint_list *constraints;
unsigned int sysclk, pclk;
@@ -577,6 +579,11 @@ static int max9867_set_bias_level(struct snd_soc_component *component,
struct max9867_priv *max9867 = snd_soc_component_get_drvdata(component);
switch (level) {
+ case SND_SOC_BIAS_ON:
+ err = clk_prepare_enable(max9867->mclk);
+ if (err)
+ return err;
+ break;
case SND_SOC_BIAS_STANDBY:
if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) {
err = regcache_sync(max9867->regmap);
@@ -595,6 +602,7 @@ static int max9867_set_bias_level(struct snd_soc_component *component,
return err;
regcache_mark_dirty(max9867->regmap);
+ clk_disable_unprepare(max9867->mclk);
break;
default:
break;
@@ -663,9 +671,16 @@ static int max9867_i2c_probe(struct i2c_client *i2c)
dev_info(&i2c->dev, "device revision: %x\n", reg);
ret = devm_snd_soc_register_component(&i2c->dev, &max9867_component,
max9867_dai, ARRAY_SIZE(max9867_dai));
- if (ret < 0)
+ if (ret < 0) {
dev_err(&i2c->dev, "Failed to register component: %d\n", ret);
- return ret;
+ return ret;
+ }
+
+ max9867->mclk = devm_clk_get(&i2c->dev, NULL);
+ if (IS_ERR(max9867->mclk))
+ return PTR_ERR(max9867->mclk);
+
+ return 0;
}
static const struct i2c_device_id max9867_i2c_id[] = {
diff --git a/sound/soc/codecs/msm8916-wcd-analog.c b/sound/soc/codecs/msm8916-wcd-analog.c
index 78e543eb3c83..cec90cf920ff 100644
--- a/sound/soc/codecs/msm8916-wcd-analog.c
+++ b/sound/soc/codecs/msm8916-wcd-analog.c
@@ -1276,13 +1276,11 @@ err_disable_clk:
return ret;
}
-static int pm8916_wcd_analog_spmi_remove(struct platform_device *pdev)
+static void pm8916_wcd_analog_spmi_remove(struct platform_device *pdev)
{
struct pm8916_wcd_analog_priv *priv = dev_get_drvdata(&pdev->dev);
clk_disable_unprepare(priv->mclk);
-
- return 0;
}
static const struct of_device_id pm8916_wcd_analog_spmi_match_table[] = {
@@ -1298,7 +1296,7 @@ static struct platform_driver pm8916_wcd_analog_spmi_driver = {
.of_match_table = pm8916_wcd_analog_spmi_match_table,
},
.probe = pm8916_wcd_analog_spmi_probe,
- .remove = pm8916_wcd_analog_spmi_remove,
+ .remove_new = pm8916_wcd_analog_spmi_remove,
};
module_platform_driver(pm8916_wcd_analog_spmi_driver);
diff --git a/sound/soc/codecs/msm8916-wcd-digital.c b/sound/soc/codecs/msm8916-wcd-digital.c
index d490a0f18675..978c4d056e81 100644
--- a/sound/soc/codecs/msm8916-wcd-digital.c
+++ b/sound/soc/codecs/msm8916-wcd-digital.c
@@ -1220,14 +1220,12 @@ err_clk:
return ret;
}
-static int msm8916_wcd_digital_remove(struct platform_device *pdev)
+static void msm8916_wcd_digital_remove(struct platform_device *pdev)
{
struct msm8916_wcd_digital_priv *priv = dev_get_drvdata(&pdev->dev);
clk_disable_unprepare(priv->mclk);
clk_disable_unprepare(priv->ahbclk);
-
- return 0;
}
static const struct of_device_id msm8916_wcd_digital_match_table[] = {
@@ -1243,7 +1241,7 @@ static struct platform_driver msm8916_wcd_digital_driver = {
.of_match_table = msm8916_wcd_digital_match_table,
},
.probe = msm8916_wcd_digital_probe,
- .remove = msm8916_wcd_digital_remove,
+ .remove_new = msm8916_wcd_digital_remove,
};
module_platform_driver(msm8916_wcd_digital_driver);
diff --git a/sound/soc/codecs/mt6358.c b/sound/soc/codecs/mt6358.c
index b54610b27906..d7b157ddc9a8 100644
--- a/sound/soc/codecs/mt6358.c
+++ b/sound/soc/codecs/mt6358.c
@@ -429,7 +429,7 @@ static int mt6358_put_volsw(struct snd_kcontrol *kcontrol,
struct mt6358_priv *priv = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg;
+ unsigned int reg = 0;
int ret;
ret = snd_soc_put_volsw(kcontrol, ucontrol);
diff --git a/sound/soc/codecs/mt6359.c b/sound/soc/codecs/mt6359.c
index c9a453ce8a2a..cb487e63615c 100644
--- a/sound/soc/codecs/mt6359.c
+++ b/sound/soc/codecs/mt6359.c
@@ -358,7 +358,7 @@ static int mt6359_put_volsw(struct snd_kcontrol *kcontrol,
struct mt6359_priv *priv = snd_soc_component_get_drvdata(component);
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
- unsigned int reg;
+ unsigned int reg = 0;
int index = ucontrol->value.integer.value[0];
int ret;
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index 4a72b94e8410..fee970427a24 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -322,12 +322,92 @@ static const struct soc_enum nau8821_dac_oversampl_enum =
SOC_ENUM_SINGLE(NAU8821_R2C_DAC_CTRL1, NAU8821_DAC_OVERSAMPLE_SFT,
ARRAY_SIZE(nau8821_dac_oversampl), nau8821_dac_oversampl);
+static const char * const nau8821_adc_drc_noise_gate[] = {
+ "1:1", "2:1", "4:1", "8:1" };
+
+static const struct soc_enum nau8821_adc_drc_noise_gate_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_NG_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_noise_gate),
+ nau8821_adc_drc_noise_gate);
+
+static const char * const nau8821_adc_drc_expansion_slope[] = {
+ "1:1", "2:1", "4:1" };
+
+static const struct soc_enum nau8821_adc_drc_expansion_slope_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES, NAU8821_DRC_EXP_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_expansion_slope),
+ nau8821_adc_drc_expansion_slope);
+
+static const char * const nau8821_adc_drc_lower_region[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_adc_drc_lower_region_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_CMP2_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_adc_drc_lower_region),
+ nau8821_adc_drc_lower_region);
+
+static const char * const nau8821_higher_region[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "", "", "1:1" };
+
+static const struct soc_enum nau8821_higher_region_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_CMP1_SLP_ADC_SFT,
+ ARRAY_SIZE(nau8821_higher_region),
+ nau8821_higher_region);
+
+static const char * const nau8821_limiter_slope[] = {
+ "0", "1:2", "1:4", "1:8", "1:16", "1:32", "1:64", "1:1" };
+
+static const struct soc_enum nau8821_limiter_slope_enum =
+ SOC_ENUM_SINGLE(NAU8821_R38_ADC_DRC_SLOPES,
+ NAU8821_DRC_LMT_SLP_ADC_SFT, ARRAY_SIZE(nau8821_limiter_slope),
+ nau8821_limiter_slope);
+
+static const char * const nau8821_detection_attack_time[] = {
+ "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+ "", "511Ts" };
+
+static const struct soc_enum nau8821_detection_attack_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+ NAU8821_DRC_PK_COEF1_ADC_SFT,
+ ARRAY_SIZE(nau8821_detection_attack_time),
+ nau8821_detection_attack_time);
+
+static const char * const nau8821_detection_release_time[] = {
+ "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+ "8191Ts", "", "16383Ts" };
+
+static const struct soc_enum nau8821_detection_release_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY,
+ NAU8821_DRC_PK_COEF2_ADC_SFT,
+ ARRAY_SIZE(nau8821_detection_release_time),
+ nau8821_detection_release_time);
+
+static const char * const nau8821_attack_time[] = {
+ "Ts", "3Ts", "7Ts", "15Ts", "31Ts", "63Ts", "127Ts", "255Ts",
+ "511Ts", "1023Ts", "2047Ts", "4095Ts", "8191Ts" };
+
+static const struct soc_enum nau8821_attack_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_ATK_ADC_SFT,
+ ARRAY_SIZE(nau8821_attack_time), nau8821_attack_time);
+
+static const char * const nau8821_decay_time[] = {
+ "63Ts", "127Ts", "255Ts", "511Ts", "1023Ts", "2047Ts", "4095Ts",
+ "8191Ts", "16383Ts", "32757Ts", "65535Ts" };
+
+static const struct soc_enum nau8821_decay_time_enum =
+ SOC_ENUM_SINGLE(NAU8821_R39_ADC_DRC_ATKDCY, NAU8821_DRC_DCY_ADC_SFT,
+ ARRAY_SIZE(nau8821_decay_time), nau8821_decay_time);
+
static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -6600, 2400);
static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -900, 0);
static const DECLARE_TLV_DB_SCALE(playback_vol_tlv, -6600, 50, 1);
static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -7000, 2400);
+static const DECLARE_TLV_DB_MINMAX(drc_knee4_tlv, -9800, -3500);
+static const DECLARE_TLV_DB_MINMAX(drc_knee3_tlv, -8100, -1800);
static const struct snd_kcontrol_new nau8821_controls[] = {
SOC_DOUBLE_TLV("Mic Volume", NAU8821_R35_ADC_DGAIN_CTRL1,
@@ -346,6 +426,22 @@ static const struct snd_kcontrol_new nau8821_controls[] = {
SOC_DOUBLE_TLV("Headphone Crosstalk Volume",
NAU8821_R2F_DAC_DGAIN_CTRL,
0, 8, 0xff, 0, crosstalk_vol_tlv),
+ SOC_SINGLE_TLV("ADC DRC KNEE4", NAU8821_R37_ADC_DRC_KNEE_IP34,
+ NAU8821_DRC_KNEE4_IP_ADC_SFT, 0x3f, 1, drc_knee4_tlv),
+ SOC_SINGLE_TLV("ADC DRC KNEE3", NAU8821_R37_ADC_DRC_KNEE_IP34,
+ NAU8821_DRC_KNEE3_IP_ADC_SFT, 0x3f, 1, drc_knee3_tlv),
+
+ SOC_ENUM("ADC DRC Noise Gate", nau8821_adc_drc_noise_gate_enum),
+ SOC_ENUM("ADC DRC Expansion Slope", nau8821_adc_drc_expansion_slope_enum),
+ SOC_ENUM("ADC DRC Lower Region", nau8821_adc_drc_lower_region_enum),
+ SOC_ENUM("ADC DRC Higher Region", nau8821_higher_region_enum),
+ SOC_ENUM("ADC DRC Limiter Slope", nau8821_limiter_slope_enum),
+ SOC_ENUM("ADC DRC Peak Detection Attack Time", nau8821_detection_attack_time_enum),
+ SOC_ENUM("ADC DRC Peak Detection Release Time", nau8821_detection_release_time_enum),
+ SOC_ENUM("ADC DRC Attack Time", nau8821_attack_time_enum),
+ SOC_ENUM("ADC DRC Decay Time", nau8821_decay_time_enum),
+ SOC_SINGLE("DRC Enable Switch", NAU8821_R36_ADC_DRC_KNEE_IP12,
+ NAU8821_DRC_ENA_ADC_SFT, 1, 0),
SOC_ENUM("ADC Decimation Rate", nau8821_adc_decimation_enum),
SOC_ENUM("DAC Oversampling Rate", nau8821_dac_oversampl_enum),
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
index c44251f54d48..d962293c218e 100644
--- a/sound/soc/codecs/nau8821.h
+++ b/sound/soc/codecs/nau8821.h
@@ -350,6 +350,29 @@
#define NAU8821_ADCL_CH_VOL_SFT 0
#define NAU8821_ADCL_CH_VOL_MASK 0xff
+/* ADC_DRC_KNEE_IP12 (0x36) */
+#define NAU8821_DRC_ENA_ADC_SFT 15
+#define NAU8821_DRC_ENA_ADC_EN (0x1 << NAU8821_DRC_ENA_ADC_SFT)
+
+/* ADC_DRC_KNEE_IP34 (0x37) */
+#define NAU8821_DRC_KNEE4_IP_ADC_SFT 8
+#define NAU8821_DRC_KNEE4_IP_ADC_MASK (0xff << NAU8821_DRC_KNEE4_IP_ADC_SFT)
+#define NAU8821_DRC_KNEE3_IP_ADC_SFT 0
+#define NAU8821_DRC_KNEE3_IP_ADC_MASK 0xff
+
+/* ADC_DRC_SLOPES (0x38) */
+#define NAU8821_DRC_NG_SLP_ADC_SFT 12
+#define NAU8821_DRC_EXP_SLP_ADC_SFT 9
+#define NAU8821_DRC_CMP2_SLP_ADC_SFT 6
+#define NAU8821_DRC_CMP1_SLP_ADC_SFT 3
+#define NAU8821_DRC_LMT_SLP_ADC_SFT 0
+
+/* ADC_DRC_ATKDCY (0x39) */
+#define NAU8821_DRC_PK_COEF1_ADC_SFT 12
+#define NAU8821_DRC_PK_COEF2_ADC_SFT 8
+#define NAU8821_DRC_ATK_ADC_SFT 4
+#define NAU8821_DRC_DCY_ADC_SFT 0
+
/* BIQ1_COF10 (0x4a) */
#define NAU8821_BIQ1_DAC_EN_SFT 3
#define NAU8821_BIQ1_DAC_EN_EN (0x1 << NAU8821_BIQ1_DAC_EN_SFT)
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
index 4bffa9c20f2b..f4eb999761a4 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -919,7 +919,7 @@ static int nau8825_adc_event(struct snd_soc_dapm_widget *w,
switch (event) {
case SND_SOC_DAPM_POST_PMU:
- msleep(125);
+ msleep(nau8825->adc_delay);
regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
NAU8825_ENABLE_ADC, NAU8825_ENABLE_ADC);
break;
@@ -2752,6 +2752,7 @@ static void nau8825_print_device_properties(struct nau8825 *nau8825)
dev_dbg(dev, "crosstalk-enable: %d\n",
nau8825->xtalk_enable);
dev_dbg(dev, "adcout-drive-strong: %d\n", nau8825->adcout_ds);
+ dev_dbg(dev, "adc-delay-ms: %d\n", nau8825->adc_delay);
}
static int nau8825_read_device_properties(struct device *dev,
@@ -2819,6 +2820,11 @@ static int nau8825_read_device_properties(struct device *dev,
nau8825->xtalk_enable = device_property_read_bool(dev,
"nuvoton,crosstalk-enable");
nau8825->adcout_ds = device_property_read_bool(dev, "nuvoton,adcout-drive-strong");
+ ret = device_property_read_u32(dev, "nuvoton,adc-delay-ms", &nau8825->adc_delay);
+ if (ret)
+ nau8825->adc_delay = 125;
+ if (nau8825->adc_delay < 125 || nau8825->adc_delay > 500)
+ dev_warn(dev, "Please set the suitable delay time!\n");
nau8825->mclk = devm_clk_get(dev, "mclk");
if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index 0c3a446e0e1a..44b62bc3880f 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -517,6 +517,7 @@ struct nau8825 {
int xtalk_enable;
bool xtalk_baktab_initialized; /* True if initialized. */
bool adcout_ds;
+ int adc_delay;
};
int nau8825_enable_jack_detect(struct snd_soc_component *component,
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
index ebf63ea90a1c..192fee90c971 100644
--- a/sound/soc/codecs/pcm179x-spi.c
+++ b/sound/soc/codecs/pcm179x-spi.c
@@ -29,7 +29,7 @@ static int pcm179x_spi_probe(struct spi_device *spi)
return pcm179x_common_init(&spi->dev, regmap);
}
-static const struct of_device_id pcm179x_of_match[] = {
+static const struct of_device_id pcm179x_of_match[] __maybe_unused = {
{ .compatible = "ti,pcm1792a", },
{ }
};
diff --git a/sound/soc/codecs/rk817_codec.c b/sound/soc/codecs/rk817_codec.c
index 2a5b274bfc0f..d4da98469f8b 100644
--- a/sound/soc/codecs/rk817_codec.c
+++ b/sound/soc/codecs/rk817_codec.c
@@ -518,13 +518,11 @@ err_:
return ret;
}
-static int rk817_platform_remove(struct platform_device *pdev)
+static void rk817_platform_remove(struct platform_device *pdev)
{
struct rk817_codec_priv *rk817 = platform_get_drvdata(pdev);
clk_disable_unprepare(rk817->mclk);
-
- return 0;
}
static struct platform_driver rk817_codec_driver = {
@@ -532,7 +530,7 @@ static struct platform_driver rk817_codec_driver = {
.name = "rk817-codec",
},
.probe = rk817_platform_probe,
- .remove = rk817_platform_remove,
+ .remove_new = rk817_platform_remove,
};
module_platform_driver(rk817_codec_driver);
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
index 49f527c61a7a..dff2596c81eb 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -546,7 +546,7 @@ static const struct i2c_device_id rt1019_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, rt1019_i2c_id);
-static const struct of_device_id rt1019_of_match[] = {
+static const struct of_device_id rt1019_of_match[] __maybe_unused = {
{ .compatible = "realtek,rt1019", },
{},
};
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index 45544b530d3d..1797af824f60 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -496,19 +496,7 @@ static const struct snd_soc_dapm_route rt1308_dapm_routes[] = {
static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -516,11 +504,7 @@ static int rt1308_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt1308_sdw_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt1308_sdw_set_tdm_slot(struct snd_soc_dai *dai,
@@ -553,13 +537,13 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream,
snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt1308->sdw_slave)
@@ -580,7 +564,7 @@ static int rt1308_sdw_hw_params(struct snd_pcm_substream *substream,
}
retval = sdw_stream_add_slave(rt1308->sdw_slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -595,13 +579,13 @@ static int rt1308_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct rt1308_sdw_priv *rt1308 =
snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt1308->sdw_slave)
return -EINVAL;
- sdw_stream_remove_slave(rt1308->sdw_slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt1308->sdw_slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt1308-sdw.h b/sound/soc/codecs/rt1308-sdw.h
index 1eaaef9f351b..04ff18fa18e2 100644
--- a/sound/soc/codecs/rt1308-sdw.h
+++ b/sound/soc/codecs/rt1308-sdw.h
@@ -170,8 +170,4 @@ struct rt1308_sdw_priv {
unsigned int bq_params_cnt;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
#endif /* __RT1308_SDW_H__ */
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index b45121ee7533..2ee5e763e345 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -494,19 +494,7 @@ static const struct snd_soc_dapm_route rt1316_dapm_routes[] = {
static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -514,11 +502,7 @@ static int rt1316_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt1316_sdw_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
@@ -529,13 +513,13 @@ static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt1316->sdw_slave)
@@ -551,7 +535,7 @@ static int rt1316_sdw_hw_params(struct snd_pcm_substream *substream,
port_config.num = 2;
retval = sdw_stream_add_slave(rt1316->sdw_slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -566,13 +550,13 @@ static int rt1316_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct rt1316_sdw_priv *rt1316 =
snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt1316->sdw_slave)
return -EINVAL;
- sdw_stream_remove_slave(rt1316->sdw_slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt1316->sdw_slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt1316-sdw.h b/sound/soc/codecs/rt1316-sdw.h
index 57dbd49993b0..e37121655bc1 100644
--- a/sound/soc/codecs/rt1316-sdw.h
+++ b/sound/soc/codecs/rt1316-sdw.h
@@ -50,8 +50,4 @@ struct rt1316_sdw_priv {
unsigned int bq_params_cnt;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
#endif /* __RT1316_SDW_H__ */
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
index c32d8ae77981..795accedc22c 100644
--- a/sound/soc/codecs/rt1318-sdw.c
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -562,19 +562,7 @@ static const struct snd_soc_dapm_route rt1318_dapm_routes[] = {
static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -582,11 +570,7 @@ static int rt1318_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt1318_sdw_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
@@ -598,14 +582,14 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
struct sdw_stream_config stream_config;
struct sdw_port_config port_config;
enum sdw_data_direction direction;
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval, port, num_channels, ch_mask;
unsigned int sampling_rate;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt1318->sdw_slave)
@@ -633,7 +617,7 @@ static int rt1318_sdw_hw_params(struct snd_pcm_substream *substream,
port_config.num = port;
retval = sdw_stream_add_slave(rt1318->sdw_slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -679,13 +663,13 @@ static int rt1318_sdw_pcm_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_component *component = dai->component;
struct rt1318_sdw_priv *rt1318 =
snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt1318->sdw_slave)
return -EINVAL;
- sdw_stream_remove_slave(rt1318->sdw_slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt1318->sdw_slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt1318-sdw.h b/sound/soc/codecs/rt1318-sdw.h
index 4d7ac9c4bd8d..85918c184f16 100644
--- a/sound/soc/codecs/rt1318-sdw.h
+++ b/sound/soc/codecs/rt1318-sdw.h
@@ -94,8 +94,4 @@ struct rt1318_sdw_priv {
bool first_hw_init;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
#endif /* __RT1318_SDW_H__ */
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index 5f80a5d59b65..23f17f70d7e9 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -88,26 +88,10 @@ static const struct regmap_config rt5682_sdw_indirect_regmap = {
.reg_write = rt5682_sdw_write,
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -115,11 +99,7 @@ static int rt5682_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt5682_sdw_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream,
@@ -130,14 +110,14 @@ static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream,
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
unsigned int val_p = 0, val_c = 0, osr_p = 0, osr_c = 0;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ if (!sdw_stream)
return -ENOMEM;
if (!rt5682->slave)
@@ -152,7 +132,7 @@ static int rt5682_sdw_hw_params(struct snd_pcm_substream *substream,
port_config.num = 2;
retval = sdw_stream_add_slave(rt5682->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -246,13 +226,13 @@ static int rt5682_sdw_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt5682_priv *rt5682 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt5682->slave)
return -EINVAL;
- sdw_stream_remove_slave(rt5682->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt5682->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
index 659ce26e9f3b..a04b9246256b 100644
--- a/sound/soc/codecs/rt700.c
+++ b/sound/soc/codecs/rt700.c
@@ -875,19 +875,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt700 = {
static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -895,11 +883,7 @@ static int rt700_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt700_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt700_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -910,14 +894,14 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream,
struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
unsigned int val = 0;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt700->slave)
@@ -944,7 +928,7 @@ static int rt700_pcm_hw_params(struct snd_pcm_substream *substream,
}
retval = sdw_stream_add_slave(rt700->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -991,13 +975,13 @@ static int rt700_pcm_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt700_priv *rt700 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt700->slave)
return -EINVAL;
- sdw_stream_remove_slave(rt700->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt700->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt700.h b/sound/soc/codecs/rt700.h
index bed9d1de6d5b..93c44005d38c 100644
--- a/sound/soc/codecs/rt700.h
+++ b/sound/soc/codecs/rt700.h
@@ -27,10 +27,6 @@ struct rt700_priv {
bool disable_irq;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
/* NID */
#define RT700_AUDIO_FUNCTION_GROUP 0x01
#define RT700_DAC_OUT1 0x02
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
index c65abe812a4c..07640d2f6e56 100644
--- a/sound/soc/codecs/rt711-sdca.c
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -1237,19 +1237,7 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt711 = {
static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -1257,11 +1245,7 @@ static int rt711_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt711_sdca_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -1272,14 +1256,14 @@ static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
unsigned int sampling_rate;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt711->slave)
@@ -1300,7 +1284,7 @@ static int rt711_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
}
retval = sdw_stream_add_slave(rt711->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -1351,13 +1335,13 @@ static int rt711_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt711_sdca_priv *rt711 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt711->slave)
return -EINVAL;
- sdw_stream_remove_slave(rt711->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt711->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt711-sdca.h b/sound/soc/codecs/rt711-sdca.h
index 10e3c801b813..22076f268577 100644
--- a/sound/soc/codecs/rt711-sdca.h
+++ b/sound/soc/codecs/rt711-sdca.h
@@ -36,10 +36,6 @@ struct rt711_sdca_priv {
bool fu1e_dapm_mute, fu1e_mixer_l_mute, fu1e_mixer_r_mute;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
/* NID */
#define RT711_AUDIO_FUNCTION_GROUP 0x01
#define RT711_DAC_OUT2 0x03
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
index 862f50950565..af53cbcc7bf2 100644
--- a/sound/soc/codecs/rt711.c
+++ b/sound/soc/codecs/rt711.c
@@ -964,19 +964,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt711 = {
static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -984,11 +972,7 @@ static int rt711_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt711_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt711_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -999,14 +983,14 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream,
struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
unsigned int val = 0;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt711->slave)
@@ -1027,7 +1011,7 @@ static int rt711_pcm_hw_params(struct snd_pcm_substream *substream,
}
retval = sdw_stream_add_slave(rt711->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -1075,13 +1059,13 @@ static int rt711_pcm_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt711_priv *rt711 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt711->slave)
return -EINVAL;
- sdw_stream_remove_slave(rt711->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt711->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt711.h b/sound/soc/codecs/rt711.h
index f50f8c8d0934..b31351f11df9 100644
--- a/sound/soc/codecs/rt711.h
+++ b/sound/soc/codecs/rt711.h
@@ -29,10 +29,6 @@ struct rt711_priv {
bool disable_irq;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
/* NID */
#define RT711_AUDIO_FUNCTION_GROUP 0x01
#define RT711_DAC_OUT2 0x03
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
new file mode 100644
index 000000000000..09807b6d6353
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -0,0 +1,983 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt712-sdca-dmic.c -- rt712 SDCA DMIC ALSA SoC audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/pm_runtime.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "rt712-sdca.h"
+#include "rt712-sdca-dmic.h"
+
+static bool rt712_sdca_dmic_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201a ... 0x201f:
+ case 0x2029 ... 0x202a:
+ case 0x202d ... 0x2034:
+ case 0x2230 ... 0x2232:
+ case 0x2f01 ... 0x2f0a:
+ case 0x2f35 ... 0x2f36:
+ case 0x2f52:
+ case 0x2f58 ... 0x2f59:
+ case 0x3201:
+ case 0x320c:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x201b:
+ case 0x201c:
+ case 0x201d:
+ case 0x201f:
+ case 0x202d ... 0x202f:
+ case 0x2230:
+ case 0x2f01:
+ case 0x2f35:
+ case 0x320c:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_mbq_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000 ... 0x200008e:
+ case 0x5300000 ... 0x530000e:
+ case 0x5400000 ... 0x540000e:
+ case 0x5600000 ... 0x5600008:
+ case 0x5700000 ... 0x570000d:
+ case 0x5800000 ... 0x5800021:
+ case 0x5900000 ... 0x5900028:
+ case 0x5a00000 ... 0x5a00009:
+ case 0x5b00000 ... 0x5b00051:
+ case 0x5c00000 ... 0x5c0009a:
+ case 0x5d00000 ... 0x5d00009:
+ case 0x5f00000 ... 0x5f00030:
+ case 0x6100000 ... 0x6100068:
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03):
+ case SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt712_sdca_dmic_mbq_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2000000:
+ case 0x200001a:
+ case 0x2000024:
+ case 0x2000046:
+ case 0x200008a:
+ case 0x5800000:
+ case 0x5800001:
+ case 0x6100008:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rt712_sdca_dmic_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt712_sdca_dmic_readable_register,
+ .volatile_reg = rt712_sdca_dmic_volatile_register,
+ .max_register = 0x40981300,
+ .reg_defaults = rt712_sdca_dmic_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static const struct regmap_config rt712_sdca_dmic_mbq_regmap = {
+ .name = "sdw-mbq",
+ .reg_bits = 32,
+ .val_bits = 16,
+ .readable_reg = rt712_sdca_dmic_mbq_readable_register,
+ .volatile_reg = rt712_sdca_dmic_mbq_volatile_register,
+ .max_register = 0x40800f14,
+ .reg_defaults = rt712_sdca_dmic_mbq_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt712_sdca_dmic_mbq_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt712_sdca_dmic_index_write(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "Failed to set private value: %06x <= %04x ret=%d\n",
+ addr, value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_dmic_index_read(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ struct regmap *regmap = rt712->mbq_regmap;
+ unsigned int addr = (nid << 20) | reg;
+
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ dev_err(&rt712->slave->dev,
+ "Failed to get private value: %06x => %04x ret=%d\n",
+ addr, *value, ret);
+
+ return ret;
+}
+
+static int rt712_sdca_dmic_index_update_bits(struct rt712_sdca_dmic_priv *rt712,
+ unsigned int nid, unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp;
+ int ret;
+
+ ret = rt712_sdca_dmic_index_read(rt712, nid, reg, &tmp);
+ if (ret < 0)
+ return ret;
+
+ set_mask_bits(&tmp, mask, val);
+ return rt712_sdca_dmic_index_write(rt712, nid, reg, tmp);
+}
+
+static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+ if (rt712->hw_init)
+ return 0;
+
+ if (rt712->first_hw_init) {
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_bypass(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_cache_bypass(rt712->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);
+
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0A_08_PDE_FLOAT_CTL, 0x1112);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC0B_11_PDE_FLOAT_CTL, 0x1111);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC1_2_PDE_FLOAT_CTL, 0x1111);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_I2S_IN_OUT_PDE_FLOAT_CTL, 0x1155);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_ENT_FLOAT_CTL, 0x2626);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC_ENT_FLOAT_CTL, 0x1e19);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_GAIN_ENT_FLOAT_CTL0, 0x1515);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_ADC_VOL_CH_FLOAT_CTL2, 0x0304);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_DMIC_GAIN_ENT_FLOAT_CTL2, 0x0304);
+ rt712_sdca_dmic_index_write(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_CONFIG_CTL0, 0x0050);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x01);
+ rt712_sdca_dmic_index_write(rt712, RT712_ULTRA_SOUND_DET,
+ RT712_ULTRA_SOUND_DETECTOR6, 0x3200);
+ regmap_write(rt712->regmap, RT712_RC_CAL, 0x23);
+ regmap_write(rt712->regmap, 0x2f52, 0x00);
+
+ if (rt712->first_hw_init) {
+ regcache_cache_bypass(rt712->regmap, false);
+ regcache_mark_dirty(rt712->regmap);
+ regcache_cache_bypass(rt712->mbq_regmap, false);
+ regcache_mark_dirty(rt712->mbq_regmap);
+ } else
+ rt712->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt712->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;
+}
+
+static int rt712_sdca_dmic_set_gain_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ 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(rt712->mbq_regmap, p->reg_base + i, &regvalue);
+
+ if (!adc_vol_flag) /* boost gain */
+ ctl = regvalue / 0x0a00;
+ else { /* ADC gain */
+ if (adc_vol_flag)
+ ctl = p->max - (((0x1e00 - regvalue) & 0xffff) / interval_offset);
+ else
+ ctl = p->max - (((0 - regvalue) & 0xffff) / interval_offset);
+ }
+
+ ucontrol->value.integer.value[i] = ctl;
+ }
+
+ return 0;
+}
+
+static int rt712_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 rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
+ 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(rt712->mbq_regmap, p->reg_base + i, &regvalue[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] * 0x0a00;
+ else { /* ADC gain */
+ gain_val[i] = 0x1e00 - ((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(rt712->mbq_regmap, p->reg_base + i, gain_val[i]);
+ if (err < 0)
+ dev_err(&rt712->slave->dev, "0x%08x can't be set\n", p->reg_base + i);
+ }
+
+ return changed;
+}
+
+static int rt712_sdca_set_fu1e_capture_ctl(struct rt712_sdca_dmic_priv *rt712)
+{
+ int err, i;
+ unsigned int ch_mute;
+
+ for (i = 0; i < ARRAY_SIZE(rt712->fu1e_mixer_mute); i++) {
+ ch_mute = (rt712->fu1e_dapm_mute || rt712->fu1e_mixer_mute[i]) ? 0x01 : 0x00;
+ err = regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E,
+ RT712_SDCA_CTL_FU_MUTE, CH_01) + i, ch_mute);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ unsigned int i;
+
+ for (i = 0; i < p->count; i++)
+ ucontrol->value.integer.value[i] = !rt712->fu1e_mixer_mute[i];
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_fu1e_capture_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_sdca_dmic_kctrl_priv *)kcontrol->private_value;
+ int err, changed = 0, i;
+
+ for (i = 0; i < p->count; i++) {
+ if (rt712->fu1e_mixer_mute[i] != !ucontrol->value.integer.value[i])
+ changed = 1;
+ rt712->fu1e_mixer_mute[i] = !ucontrol->value.integer.value[i];
+ }
+
+ err = rt712_sdca_set_fu1e_capture_ctl(rt712);
+ if (err < 0)
+ return err;
+
+ return changed;
+}
+
+static int rt712_sdca_fu_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct rt712_sdca_dmic_kctrl_priv *p =
+ (struct rt712_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;
+}
+
+#define RT712_SDCA_PR_VALUE(xreg_base, xcount, xmax, xinvert) \
+ ((unsigned long)&(struct rt712_sdca_dmic_kctrl_priv) \
+ {.reg_base = xreg_base, .count = xcount, .max = xmax, \
+ .invert = xinvert})
+
+#define RT712_SDCA_FU_CTRL(xname, reg_base, xmax, xinvert, xcount) \
+{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+ .info = rt712_sdca_fu_info, \
+ .get = rt712_sdca_dmic_fu1e_capture_get, \
+ .put = rt712_sdca_dmic_fu1e_capture_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, xinvert)}
+
+#define RT712_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 = rt712_sdca_fu_info, \
+ .get = xhandler_get, .put = xhandler_put, \
+ .private_value = RT712_SDCA_PR_VALUE(reg_base, xcount, xmax, 0) }
+
+static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -1725, 75, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_snd_controls[] = {
+ RT712_SDCA_FU_CTRL("FU1E Capture Switch",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01),
+ 1, 1, 4),
+ RT712_SDCA_EXT_TLV("FU1E Capture Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 0x3f, in_vol_tlv),
+ RT712_SDCA_EXT_TLV("FU15 Boost Volume",
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01),
+ rt712_sdca_dmic_set_gain_get, rt712_sdca_dmic_set_gain_put, 4, 3, mic_vol_tlv),
+};
+
+static int rt712_sdca_dmic_mux_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component =
+ snd_soc_dapm_kcontrol_component(kcontrol);
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned int val = 0, mask_sft;
+
+ if (strstr(ucontrol->id.name, "ADC 25 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val);
+
+ ucontrol->value.enumerated.item[0] = (val >> mask_sft) & 0x7;
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_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 rt712_sdca_dmic_priv *rt712 = 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 25 Mux"))
+ mask_sft = 8;
+ else if (strstr(ucontrol->id.name, "ADC 26 Mux"))
+ mask_sft = 4;
+ else
+ return -EINVAL;
+
+ val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+
+ rt712_sdca_dmic_index_read(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_HDA_LEGACY_MUX_CTL0, &val2);
+ val2 = (0x7 << mask_sft) & val2;
+
+ if (val == val2)
+ change = 0;
+ else
+ change = 1;
+
+ if (change)
+ rt712_sdca_dmic_index_update_bits(rt712, RT712_VENDOR_HDA_CTL,
+ RT712_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 adc_mux_text[] = {
+ "DMIC1",
+ "DMIC2",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc25_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static SOC_ENUM_SINGLE_DECL(
+ rt712_adc26_enum, SND_SOC_NOPM, 0, adc_mux_text);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc25_mux =
+ SOC_DAPM_ENUM_EXT("ADC 25 Mux", rt712_adc25_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static const struct snd_kcontrol_new rt712_sdca_dmic_adc26_mux =
+ SOC_DAPM_ENUM_EXT("ADC 26 Mux", rt712_adc26_enum,
+ rt712_sdca_dmic_mux_get, rt712_sdca_dmic_mux_put);
+
+static int rt712_sdca_dmic_fu1e_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ rt712->fu1e_dapm_mute = false;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ rt712->fu1e_dapm_mute = true;
+ rt712_sdca_set_fu1e_capture_ctl(rt712);
+ break;
+ }
+ return 0;
+}
+
+static int rt712_sdca_dmic_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PDE11,
+ RT712_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt712_sdca_dmic_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+
+ SND_SOC_DAPM_SUPPLY("PDE 11", SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_pde11_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_ADC_E("FU 1E", NULL, SND_SOC_NOPM, 0, 0,
+ rt712_sdca_dmic_fu1e_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_MUX("ADC 25 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc25_mux),
+ SND_SOC_DAPM_MUX("ADC 26 Mux", SND_SOC_NOPM, 0, 0,
+ &rt712_sdca_dmic_adc26_mux),
+
+ SND_SOC_DAPM_AIF_OUT("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt712_sdca_dmic_audio_map[] = {
+ {"DP2TX", NULL, "FU 1E"},
+
+ {"FU 1E", NULL, "PDE 11"},
+ {"FU 1E", NULL, "ADC 25 Mux"},
+ {"FU 1E", NULL, "ADC 26 Mux"},
+ {"ADC 25 Mux", "DMIC1", "DMIC1"},
+ {"ADC 25 Mux", "DMIC2", "DMIC2"},
+ {"ADC 26 Mux", "DMIC1", "DMIC1"},
+ {"ADC 26 Mux", "DMIC2", "DMIC2"},
+};
+
+static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
+{
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ rt712->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_rt712_dmic = {
+ .probe = rt712_sdca_dmic_probe,
+ .controls = rt712_sdca_dmic_snd_controls,
+ .num_controls = ARRAY_SIZE(rt712_sdca_dmic_snd_controls),
+ .dapm_widgets = rt712_sdca_dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt712_sdca_dmic_dapm_widgets),
+ .dapm_routes = rt712_sdca_dmic_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(rt712_sdca_dmic_audio_map),
+ .endianness = 1,
+};
+
+static int rt712_sdca_dmic_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 rt712_sdca_dmic_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt712_sdca_dmic_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 rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, 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 (!rt712->slave)
+ 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 = SDW_DATA_DIR_TX;
+
+ num_channels = params_channels(params);
+ port_config.ch_mask = GENMASK(num_channels - 1, 0);
+ port_config.num = 2;
+
+ retval = sdw_stream_add_slave(rt712->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) > 4) {
+ dev_err(component->dev, "Unsupported channels %d\n",
+ params_channels(params));
+ return -EINVAL;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 16000:
+ sampling_rate = RT712_SDCA_RATE_16000HZ;
+ break;
+ case 32000:
+ sampling_rate = RT712_SDCA_RATE_32000HZ;
+ break;
+ case 44100:
+ sampling_rate = RT712_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT712_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT712_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT712_SDCA_RATE_192000HZ;
+ break;
+ default:
+ dev_err(component->dev, "Rate %d is not supported\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+
+ /* set sampling frequency */
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+ regmap_write(rt712->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt712_sdca_dmic_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt712_sdca_dmic_priv *rt712 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt712->slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt712->slave, sdw_stream);
+ return 0;
+}
+
+#define RT712_STEREO_RATES (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT712_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops rt712_sdca_dmic_ops = {
+ .hw_params = rt712_sdca_dmic_hw_params,
+ .hw_free = rt712_sdca_dmic_hw_free,
+ .set_stream = rt712_sdca_dmic_set_sdw_stream,
+ .shutdown = rt712_sdca_dmic_shutdown,
+};
+
+static struct snd_soc_dai_driver rt712_sdca_dmic_dai[] = {
+ {
+ .name = "rt712-sdca-dmic-aif1",
+ .id = RT712_AIF1,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = RT712_STEREO_RATES,
+ .formats = RT712_FORMATS,
+ },
+ .ops = &rt712_sdca_dmic_ops,
+ },
+};
+
+static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap,
+ struct regmap *mbq_regmap, struct sdw_slave *slave)
+{
+ struct rt712_sdca_dmic_priv *rt712;
+ int ret;
+
+ rt712 = devm_kzalloc(dev, sizeof(*rt712), GFP_KERNEL);
+ if (!rt712)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt712);
+ rt712->slave = slave;
+ rt712->regmap = regmap;
+ rt712->mbq_regmap = mbq_regmap;
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt712->hw_init = false;
+ rt712->first_hw_init = false;
+ rt712->fu1e_dapm_mute = true;
+ rt712->fu1e_mixer_mute[0] = rt712->fu1e_mixer_mute[1] =
+ rt712->fu1e_mixer_mute[2] = rt712->fu1e_mixer_mute[3] = true;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_dev_rt712_dmic,
+ rt712_sdca_dmic_dai,
+ ARRAY_SIZE(rt712_sdca_dmic_dai));
+
+ dev_dbg(&slave->dev, "%s\n", __func__);
+
+ return ret;
+}
+
+
+static int rt712_sdca_dmic_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ 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;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt712->hw_init || rt712->status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt712_sdca_dmic_io_init(&slave->dev, slave);
+}
+
+static int rt712_sdca_dmic_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval, i;
+ 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;
+
+ /* first we need to allocate memory for set bits in port lists */
+ prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = 0;
+
+ 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++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 200;
+
+ /* wake-up event */
+ prop->wake_capable = 1;
+
+ return 0;
+}
+
+static const struct sdw_device_id rt712_sdca_dmic_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1712, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1713, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1716, 0x3, 0x1, 0),
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1717, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt712_sdca_dmic_id);
+
+static int __maybe_unused rt712_sdca_dmic_dev_suspend(struct device *dev)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+
+ if (!rt712->hw_init)
+ return 0;
+
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
+ return 0;
+}
+
+static int __maybe_unused rt712_sdca_dmic_dev_system_suspend(struct device *dev)
+{
+ struct rt712_sdca_dmic_priv *rt712_sdca = dev_get_drvdata(dev);
+
+ if (!rt712_sdca->hw_init)
+ return 0;
+
+ return rt712_sdca_dmic_dev_suspend(dev);
+}
+
+#define RT712_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt712_sdca_dmic_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt712->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT712_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_sync(rt712->regmap);
+ regcache_cache_only(rt712->mbq_regmap, false);
+ regcache_sync(rt712->mbq_regmap);
+ return 0;
+}
+
+static const struct dev_pm_ops rt712_sdca_dmic_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt712_sdca_dmic_dev_system_suspend, rt712_sdca_dmic_dev_resume)
+ SET_RUNTIME_PM_OPS(rt712_sdca_dmic_dev_suspend, rt712_sdca_dmic_dev_resume, NULL)
+};
+
+
+static struct sdw_slave_ops rt712_sdca_dmic_slave_ops = {
+ .read_prop = rt712_sdca_dmic_read_prop,
+ .update_status = rt712_sdca_dmic_update_status,
+};
+
+static int rt712_sdca_dmic_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, &rt712_sdca_dmic_mbq_regmap);
+ if (IS_ERR(mbq_regmap))
+ return PTR_ERR(mbq_regmap);
+
+ regmap = devm_regmap_init_sdw(slave, &rt712_sdca_dmic_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt712_sdca_dmic_init(&slave->dev, regmap, mbq_regmap, slave);
+}
+
+static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt712_sdca_dmic_priv *rt712 = dev_get_drvdata(&slave->dev);
+
+ if (rt712->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static struct sdw_driver rt712_sdca_dmic_sdw_driver = {
+ .driver = {
+ .name = "rt712-sdca-dmic",
+ .owner = THIS_MODULE,
+ .pm = &rt712_sdca_dmic_pm,
+ },
+ .probe = rt712_sdca_dmic_sdw_probe,
+ .remove = rt712_sdca_dmic_sdw_remove,
+ .ops = &rt712_sdca_dmic_slave_ops,
+ .id_table = rt712_sdca_dmic_id,
+};
+module_sdw_driver(rt712_sdca_dmic_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT712 SDCA DMIC SDW driver");
+MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt712-sdca-dmic.h b/sound/soc/codecs/rt712-sdca-dmic.h
new file mode 100644
index 000000000000..74c29677c251
--- /dev/null
+++ b/sound/soc/codecs/rt712-sdca-dmic.h
@@ -0,0 +1,108 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt712-sdca-dmic.h -- RT712 SDCA DMIC ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT712_SDW_DMIC_H__
+#define __RT712_SDW_DMIC_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw_registers.h>
+
+struct rt712_sdca_dmic_priv {
+ struct regmap *regmap;
+ 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;
+ bool fu1e_dapm_mute;
+ bool fu1e_mixer_mute[4];
+};
+
+struct rt712_sdca_dmic_kctrl_priv {
+ unsigned int reg_base;
+ unsigned int count;
+ unsigned int max;
+ unsigned int invert;
+};
+
+/* SDCA (Channel) */
+#define CH_01 0x01
+#define CH_02 0x02
+#define CH_03 0x03
+#define CH_04 0x04
+
+static const struct reg_default rt712_sdca_dmic_reg_defaults[] = {
+ { 0x201a, 0x00 },
+ { 0x201b, 0x00 },
+ { 0x201c, 0x00 },
+ { 0x201d, 0x00 },
+ { 0x201e, 0x00 },
+ { 0x201f, 0x00 },
+ { 0x2029, 0x00 },
+ { 0x202a, 0x00 },
+ { 0x202d, 0x00 },
+ { 0x202e, 0x00 },
+ { 0x202f, 0x00 },
+ { 0x2030, 0x00 },
+ { 0x2031, 0x00 },
+ { 0x2032, 0x00 },
+ { 0x2033, 0x00 },
+ { 0x2034, 0x00 },
+ { 0x2230, 0x00 },
+ { 0x2231, 0x2f },
+ { 0x2232, 0x80 },
+ { 0x2f01, 0x00 },
+ { 0x2f02, 0x09 },
+ { 0x2f03, 0x00 },
+ { 0x2f04, 0x00 },
+ { 0x2f05, 0x0b },
+ { 0x2f06, 0x01 },
+ { 0x2f08, 0x00 },
+ { 0x2f09, 0x00 },
+ { 0x2f0a, 0x01 },
+ { 0x2f35, 0x02 },
+ { 0x2f36, 0xcf },
+ { 0x2f52, 0x08 },
+ { 0x2f58, 0x07 },
+ { 0x2f59, 0x07 },
+ { 0x3201, 0x01 },
+ { 0x320c, 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_IT26, RT712_SDCA_CTL_VENDOR_DEF, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_02), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_03), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_MUTE, CH_04), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1F, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_CS1C, RT712_SDCA_CTL_SAMPLE_FREQ_INDEX, 0), 0x09 },
+};
+
+static const struct reg_default rt712_sdca_dmic_mbq_defaults[] = {
+ { 0x0590001e, 0x0020 },
+ { 0x06100000, 0x0010 },
+ { 0x06100006, 0x0055 },
+ { 0x06100010, 0x2630 },
+ { 0x06100011, 0x152f },
+ { 0x06100013, 0x0102 },
+ { 0x06100015, 0x2219 },
+ { 0x06100018, 0x0102 },
+ { 0x06100026, 0x2c29 },
+ { 0x06100027, 0x2d2b },
+ { 0x0610002b, 0x2a32 },
+ { 0x0610002f, 0x3355 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_PLATFORM_FU15, RT712_SDCA_CTL_FU_CH_GAIN, CH_04), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_01), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_02), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_03), 0x0000 },
+ { SDW_SDCA_CTL(FUNC_NUM_MIC_ARRAY, RT712_SDCA_ENT_USER_FU1E, RT712_SDCA_CTL_FU_VOLUME, CH_04), 0x0000 },
+};
+
+#endif /* __RT712_SDW_DMIC_H__ */
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
index 8d2fa769bb2e..89d245655ca4 100644
--- a/sound/soc/codecs/rt712-sdca.c
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -992,19 +992,7 @@ static const struct snd_soc_component_driver soc_sdca_dev_rt712 = {
static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -1012,11 +1000,7 @@ static int rt712_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void rt712_sdca_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -1028,14 +1012,14 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
struct sdw_stream_config stream_config;
struct sdw_port_config port_config;
enum sdw_data_direction direction;
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval, port, num_channels;
unsigned int sampling_rate;
dev_dbg(dai->dev, "%s %s", __func__, dai->name);
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt712->slave)
@@ -1068,7 +1052,7 @@ static int rt712_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
port_config.num = port;
retval = sdw_stream_add_slave(rt712->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -1128,13 +1112,13 @@ static int rt712_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt712_sdca_priv *rt712 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt712->slave)
return -EINVAL;
- sdw_stream_remove_slave(rt712->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt712->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt712-sdca.h b/sound/soc/codecs/rt712-sdca.h
index cf647162f9da..c6a94a23f46e 100644
--- a/sound/soc/codecs/rt712-sdca.h
+++ b/sound/soc/codecs/rt712-sdca.h
@@ -40,10 +40,6 @@ struct rt712_sdca_priv {
bool fu0f_mixer_r_mute;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
/* NID */
#define RT712_VENDOR_REG 0x20
#define RT712_VENDOR_CALI 0x58
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
index 920510365fd7..b989f907784b 100644
--- a/sound/soc/codecs/rt715-sdca.c
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -784,16 +784,7 @@ static const struct snd_soc_component_driver soc_codec_dev_rt715_sdca = {
static int rt715_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct rt715_sdw_stream_data *stream;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -802,14 +793,7 @@ static void rt715_sdca_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct rt715_sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
- return;
-
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -820,13 +804,13 @@ static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct rt715_sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
unsigned int val;
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt715->slave)
@@ -851,7 +835,7 @@ static int rt715_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
}
retval = sdw_stream_add_slave(rt715->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(component->dev, "Unable to configure port, retval:%d\n",
retval);
@@ -922,13 +906,13 @@ static int rt715_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
- struct rt715_sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt715->slave)
return -EINVAL;
- sdw_stream_remove_slave(rt715->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt715->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt715-sdca.h b/sound/soc/codecs/rt715-sdca.h
index 90881b455ece..7577f3151934 100644
--- a/sound/soc/codecs/rt715-sdca.h
+++ b/sound/soc/codecs/rt715-sdca.h
@@ -37,10 +37,6 @@ struct rt715_sdca_priv {
int kctl_8ch_orig[8];
};
-struct rt715_sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
struct rt715_sdca_kcontrol_private {
unsigned int reg_base;
unsigned int count;
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
index c6dd9df7be45..6c2e165dd621 100644
--- a/sound/soc/codecs/rt715.c
+++ b/sound/soc/codecs/rt715.c
@@ -765,19 +765,7 @@ static int rt715_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -786,11 +774,7 @@ static void rt715_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -801,13 +785,13 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream;
int retval;
unsigned int val = 0;
- stream = snd_soc_dai_get_dma_data(dai, substream);
+ sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!rt715->slave)
@@ -830,7 +814,7 @@ static int rt715_pcm_hw_params(struct snd_pcm_substream *substream,
}
retval = sdw_stream_add_slave(rt715->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (retval) {
dev_err(dai->dev, "Unable to configure port\n");
return retval;
@@ -893,13 +877,13 @@ static int rt715_pcm_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
+ struct sdw_stream_runtime *sdw_stream =
snd_soc_dai_get_dma_data(dai, substream);
if (!rt715->slave)
return -EINVAL;
- sdw_stream_remove_slave(rt715->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(rt715->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h
index 25dba61f1760..17a8d041c1c3 100644
--- a/sound/soc/codecs/rt715.h
+++ b/sound/soc/codecs/rt715.h
@@ -27,10 +27,6 @@ struct rt715_priv {
unsigned int kctl_8ch_vol_ori[8];
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
/* NID */
#define RT715_AUDIO_FUNCTION_GROUP 0x01
#define RT715_MIC_ADC 0x07
diff --git a/sound/soc/codecs/sdw-mockup.c b/sound/soc/codecs/sdw-mockup.c
index 62b02d764f09..5498ff027c58 100644
--- a/sound/soc/codecs/sdw-mockup.c
+++ b/sound/soc/codecs/sdw-mockup.c
@@ -23,10 +23,6 @@ struct sdw_mockup_priv {
struct sdw_slave *slave;
};
-struct sdw_stream_data {
- struct sdw_stream_runtime *sdw_stream;
-};
-
static int sdw_mockup_component_probe(struct snd_soc_component *component)
{
return 0;
@@ -45,19 +41,7 @@ static const struct snd_soc_component_driver snd_soc_sdw_mockup_component = {
static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
int direction)
{
- struct sdw_stream_data *stream;
-
- if (!sdw_stream)
- return 0;
-
- stream = kzalloc(sizeof(*stream), GFP_KERNEL);
- if (!stream)
- return -ENOMEM;
-
- stream->sdw_stream = sdw_stream;
-
- /* Use tx_mask or rx_mask to configure stream tag and set dma_data */
- snd_soc_dai_dma_data_set(dai, direction, stream);
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
return 0;
}
@@ -65,11 +49,7 @@ static int sdw_mockup_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
static void sdw_mockup_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
- struct sdw_stream_data *stream;
-
- stream = snd_soc_dai_get_dma_data(dai, substream);
snd_soc_dai_set_dma_data(dai, substream, NULL);
- kfree(stream);
}
static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
@@ -80,11 +60,10 @@ static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
struct sdw_stream_config stream_config = {0};
struct sdw_port_config port_config = {0};
- struct sdw_stream_data *stream;
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
int ret;
- stream = snd_soc_dai_get_dma_data(dai, substream);
- if (!stream)
+ if (!sdw_stream)
return -EINVAL;
if (!sdw_mockup->slave)
@@ -99,7 +78,7 @@ static int sdw_mockup_pcm_hw_params(struct snd_pcm_substream *substream,
port_config.num = 8;
ret = sdw_stream_add_slave(sdw_mockup->slave, &stream_config,
- &port_config, 1, stream->sdw_stream);
+ &port_config, 1, sdw_stream);
if (ret)
dev_err(dai->dev, "Unable to configure port\n");
@@ -111,13 +90,12 @@ static int sdw_mockup_pcm_hw_free(struct snd_pcm_substream *substream,
{
struct snd_soc_component *component = dai->component;
struct sdw_mockup_priv *sdw_mockup = snd_soc_component_get_drvdata(component);
- struct sdw_stream_data *stream =
- snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
if (!sdw_mockup->slave)
return -EINVAL;
- sdw_stream_remove_slave(sdw_mockup->slave, stream->sdw_stream);
+ sdw_stream_remove_slave(sdw_mockup->slave, sdw_stream);
return 0;
}
diff --git a/sound/soc/codecs/sma1303.c b/sound/soc/codecs/sma1303.c
index fa4b0a60f8a9..b6c132edf3bd 100644
--- a/sound/soc/codecs/sma1303.c
+++ b/sound/soc/codecs/sma1303.c
@@ -1591,7 +1591,7 @@ static const struct snd_soc_component_driver sma1303_component = {
.num_dapm_routes = ARRAY_SIZE(sma1303_audio_map),
};
-const struct regmap_config sma_i2c_regmap = {
+static const struct regmap_config sma_i2c_regmap = {
.reg_bits = 8,
.val_bits = 8,
diff --git a/sound/soc/codecs/src4xxx-i2c.c b/sound/soc/codecs/src4xxx-i2c.c
index 27026030704a..a40fd20df984 100644
--- a/sound/soc/codecs/src4xxx-i2c.c
+++ b/sound/soc/codecs/src4xxx-i2c.c
@@ -24,7 +24,7 @@ static const struct i2c_device_id src4xxx_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, src4xxx_i2c_ids);
-static const struct of_device_id src4xxx_of_match[] = {
+static const struct of_device_id src4xxx_of_match[] __maybe_unused = {
{ .compatible = "ti,src4392", },
{ }
};
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index cbbe83b85ada..00b60369b029 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -280,9 +280,12 @@ static inline int ssm2602_get_coeff(int mclk, int rate)
int i;
for (i = 0; i < ARRAY_SIZE(ssm2602_coeff_table); i++) {
- if (ssm2602_coeff_table[i].rate == rate &&
- ssm2602_coeff_table[i].mclk == mclk)
- return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].rate == rate) {
+ if (ssm2602_coeff_table[i].mclk == mclk)
+ return ssm2602_coeff_table[i].srate;
+ if (ssm2602_coeff_table[i].mclk == mclk / 2)
+ return ssm2602_coeff_table[i].srate | SRATE_CORECLK_DIV2;
+ }
}
return -EINVAL;
}
@@ -365,18 +368,24 @@ static int ssm2602_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (freq) {
case 12288000:
case 18432000:
+ case 24576000:
+ case 36864000:
ssm2602->sysclk_constraints = &ssm2602_constraints_12288000;
break;
case 11289600:
case 16934400:
+ case 22579200:
+ case 33868800:
ssm2602->sysclk_constraints = &ssm2602_constraints_11289600;
break;
case 12000000:
+ case 24000000:
ssm2602->sysclk_constraints = NULL;
break;
default:
return -EINVAL;
}
+
ssm2602->sysclk = freq;
} else {
unsigned int mask;
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 8c86b578eba8..29af9595dac1 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -1054,35 +1054,32 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x)
of_property_read_u8(np, "st,ch3-output-mapping",
&pdata->ch3_output_mapping);
- if (of_get_property(np, "st,fault-detect-recovery", NULL))
- pdata->fault_detect_recovery = 1;
- if (of_get_property(np, "st,thermal-warning-recovery", NULL))
- pdata->thermal_warning_recovery = 1;
- if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
- pdata->thermal_warning_adjustment = 1;
- if (of_get_property(np, "st,needs_esd_watchdog", NULL))
- pdata->needs_esd_watchdog = 1;
+ pdata->fault_detect_recovery =
+ of_property_read_bool(np, "st,fault-detect-recovery");
+ pdata->thermal_warning_recovery =
+ of_property_read_bool(np, "st,thermal-warning-recovery");
+ pdata->thermal_warning_adjustment =
+ of_property_read_bool(np, "st,thermal-warning-adjustment");
+ pdata->needs_esd_watchdog =
+ of_property_read_bool(np, "st,needs_esd_watchdog");
tmp = 140;
of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
/* CONFE */
- if (of_get_property(np, "st,max-power-use-mpcc", NULL))
- pdata->max_power_use_mpcc = 1;
-
- if (of_get_property(np, "st,max-power-correction", NULL))
- pdata->max_power_correction = 1;
-
- if (of_get_property(np, "st,am-reduction-mode", NULL))
- pdata->am_reduction_mode = 1;
-
- if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
- pdata->odd_pwm_speed_mode = 1;
+ pdata->max_power_use_mpcc =
+ of_property_read_bool(np, "st,max-power-use-mpcc");
+ pdata->max_power_correction =
+ of_property_read_bool(np, "st,max-power-correction");
+ pdata->am_reduction_mode =
+ of_property_read_bool(np, "st,am-reduction-mode");
+ pdata->odd_pwm_speed_mode =
+ of_property_read_bool(np, "st,odd-pwm-speed-mode");
/* CONFF */
- if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
- pdata->invalid_input_detect_mute = 1;
+ pdata->invalid_input_detect_mute =
+ of_property_read_bool(np, "st,invalid-input-detect-mute");
sta32x->pdata = pdata;
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 9ed13aeb3cbd..b033a5fcd6c0 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -1106,12 +1106,12 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
of_property_read_u8(np, "st,ch3-output-mapping",
&pdata->ch3_output_mapping);
- if (of_get_property(np, "st,thermal-warning-recovery", NULL))
- pdata->thermal_warning_recovery = 1;
- if (of_get_property(np, "st,thermal-warning-adjustment", NULL))
- pdata->thermal_warning_adjustment = 1;
- if (of_get_property(np, "st,fault-detect-recovery", NULL))
- pdata->fault_detect_recovery = 1;
+ pdata->thermal_warning_recovery =
+ of_property_read_bool(np, "st,thermal-warning-recovery");
+ pdata->thermal_warning_adjustment =
+ of_property_read_bool(np, "st,thermal-warning-adjustment");
+ pdata->fault_detect_recovery =
+ of_property_read_bool(np, "st,fault-detect-recovery");
pdata->ffx_power_output_mode = STA350_FFX_PM_VARIABLE_DROP_COMP;
if (!of_property_read_string(np, "st,ffx-power-output-mode",
@@ -1133,41 +1133,34 @@ static int sta350_probe_dt(struct device *dev, struct sta350_priv *sta350)
of_property_read_u16(np, "st,drop-compensation-ns", &tmp);
pdata->drop_compensation_ns = clamp_t(u16, tmp, 0, 300) / 20;
- if (of_get_property(np, "st,overcurrent-warning-adjustment", NULL))
- pdata->oc_warning_adjustment = 1;
+ pdata->oc_warning_adjustment =
+ of_property_read_bool(np, "st,overcurrent-warning-adjustment");
/* CONFE */
- if (of_get_property(np, "st,max-power-use-mpcc", NULL))
- pdata->max_power_use_mpcc = 1;
-
- if (of_get_property(np, "st,max-power-correction", NULL))
- pdata->max_power_correction = 1;
-
- if (of_get_property(np, "st,am-reduction-mode", NULL))
- pdata->am_reduction_mode = 1;
-
- if (of_get_property(np, "st,odd-pwm-speed-mode", NULL))
- pdata->odd_pwm_speed_mode = 1;
-
- if (of_get_property(np, "st,distortion-compensation", NULL))
- pdata->distortion_compensation = 1;
+ pdata->max_power_use_mpcc =
+ of_property_read_bool(np, "st,max-power-use-mpcc");
+ pdata->max_power_correction =
+ of_property_read_bool(np, "st,max-power-correction");
+ pdata->am_reduction_mode =
+ of_property_read_bool(np, "st,am-reduction-mode");
+ pdata->odd_pwm_speed_mode =
+ of_property_read_bool(np, "st,odd-pwm-speed-mode");
+ pdata->distortion_compensation =
+ of_property_read_bool(np, "st,distortion-compensation");
/* CONFF */
- if (of_get_property(np, "st,invalid-input-detect-mute", NULL))
- pdata->invalid_input_detect_mute = 1;
+ pdata->invalid_input_detect_mute =
+ of_property_read_bool(np, "st,invalid-input-detect-mute");
/* MISC */
- if (of_get_property(np, "st,activate-mute-output", NULL))
- pdata->activate_mute_output = 1;
-
- if (of_get_property(np, "st,bridge-immediate-off", NULL))
- pdata->bridge_immediate_off = 1;
-
- if (of_get_property(np, "st,noise-shape-dc-cut", NULL))
- pdata->noise_shape_dc_cut = 1;
-
- if (of_get_property(np, "st,powerdown-master-volume", NULL))
- pdata->powerdown_master_vol = 1;
+ pdata->activate_mute_output =
+ of_property_read_bool(np, "st,activate-mute-output");
+ pdata->bridge_immediate_off =
+ of_property_read_bool(np, "st,bridge-immediate-off");
+ pdata->noise_shape_dc_cut =
+ of_property_read_bool(np, "st,noise-shape-dc-cut");
+ pdata->powerdown_master_vol =
+ of_property_read_bool(np, "st,powerdown-master-volume");
if (!of_property_read_u8(np, "st,powerdown-delay-divider", &tmp8)) {
if (is_power_of_2(tmp8) && tmp8 >= 1 && tmp8 <= 128)
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index 22143cc5afa7..f9e7122894bd 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -840,7 +840,7 @@ static int tas5086_probe(struct snd_soc_component *component)
snprintf(name, sizeof(name),
"ti,mid-z-channel-%d", i + 1);
- if (of_get_property(of_node, name, NULL) != NULL)
+ if (of_property_read_bool(of_node, name))
priv->pwm_start_mid_z |= 1 << i;
}
}
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index 84ec1b527646..f39c3273b2fd 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -718,6 +718,63 @@ static const struct regmap_config tas5721_regmap_config = {
.volatile_table = &tas571x_volatile_regs,
};
+static const char *const tas5733_supply_names[] = {
+ "AVDD",
+ "DVDD",
+ "PVDD",
+};
+
+static const struct reg_default tas5733_reg_defaults[] = {
+ {TAS571X_CLK_CTRL_REG, 0x6c},
+ {TAS571X_DEV_ID_REG, 0x00},
+ {TAS571X_ERR_STATUS_REG, 0x00},
+ {TAS571X_SYS_CTRL_1_REG, 0xa0},
+ {TAS571X_SDI_REG, 0x05},
+ {TAS571X_SYS_CTRL_2_REG, 0x40},
+ {TAS571X_SOFT_MUTE_REG, 0x07},
+ {TAS571X_MVOL_REG, 0x03ff},
+ {TAS571X_CH1_VOL_REG, 0x00c0},
+ {TAS571X_CH2_VOL_REG, 0x00c0},
+ {TAS571X_CH3_VOL_REG, 0x00c0},
+ {TAS571X_VOL_CFG_REG, 0xf0},
+ {TAS571X_MODULATION_LIMIT_REG, 0x07},
+ {TAS571X_IC_DELAY_CH1_REG, 0xb8},
+ {TAS571X_IC_DELAY_CH2_REG, 0x60},
+ {TAS571X_IC_DELAY_CH3_REG, 0xa0},
+ {TAS571X_IC_DELAY_CH4_REG, 0x48},
+ {TAS571X_PWM_CH_SDN_GROUP_REG, 0x30},
+ {TAS571X_START_STOP_PERIOD_REG, 0x68},
+ {TAS571X_OSC_TRIM_REG, 0x82},
+ {TAS571X_BKND_ERR_REG, 0x02},
+ {TAS571X_INPUT_MUX_REG, 0x00897772},
+ {TAS571X_PWM_MUX_REG, 0x01021345},
+ {TAS5717_CH1_RIGHT_CH_MIX_REG, 0x00},
+ {TAS5717_CH1_LEFT_CH_MIX_REG, 0x800000},
+ {TAS5717_CH2_LEFT_CH_MIX_REG, 0x00},
+ {TAS5717_CH2_RIGHT_CH_MIX_REG, 0x800000},
+};
+
+static const struct regmap_config tas5733_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 32,
+ .max_register = 0xff,
+ .reg_read = tas571x_reg_read,
+ .reg_write = tas571x_reg_write,
+ .reg_defaults = tas5733_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(tas5733_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .wr_table = &tas571x_write_regs,
+ .volatile_table = &tas571x_volatile_regs,
+};
+
+static const struct tas571x_chip tas5733_chip = {
+ .supply_names = tas5733_supply_names,
+ .num_supply_names = ARRAY_SIZE(tas5733_supply_names),
+ .controls = tas5717_controls,
+ .num_controls = ARRAY_SIZE(tas5717_controls),
+ .regmap_config = &tas5733_regmap_config,
+ .vol_reg_size = 2,
+};
static const struct tas571x_chip tas5721_chip = {
.supply_names = tas5721_supply_names,
@@ -897,6 +954,7 @@ static const struct of_device_id tas571x_of_match[] __maybe_unused = {
{ .compatible = "ti,tas5717", .data = &tas5717_chip, },
{ .compatible = "ti,tas5719", .data = &tas5717_chip, },
{ .compatible = "ti,tas5721", .data = &tas5721_chip, },
+ { .compatible = "ti,tas5733", .data = &tas5733_chip, },
{ }
};
MODULE_DEVICE_TABLE(of, tas571x_of_match);
@@ -907,6 +965,7 @@ static const struct i2c_device_id tas571x_i2c_id[] = {
{ "tas5717", (kernel_ulong_t) &tas5717_chip },
{ "tas5719", (kernel_ulong_t) &tas5717_chip },
{ "tas5721", (kernel_ulong_t) &tas5721_chip },
+ { "tas5733", (kernel_ulong_t) &tas5733_chip },
{ }
};
MODULE_DEVICE_TABLE(i2c, tas571x_i2c_id);
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
index de6d01c8fdd3..4d27b60bd804 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -339,7 +339,8 @@ static int tas5720_codec_probe(struct snd_soc_component *component)
break;
default:
dev_err(component->dev, "unexpected private driver data\n");
- return -EINVAL;
+ ret = -EINVAL;
+ goto probe_fail;
}
if (device_id != expected_device_id)
diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c
index d2548fdf9ae5..8bf3510a3ea3 100644
--- a/sound/soc/codecs/wcd9335.c
+++ b/sound/soc/codecs/wcd9335.c
@@ -5138,20 +5138,17 @@ static int wcd9335_irq_init(struct wcd9335_codec *wcd)
* INTR2 is a subset of first interrupt sources MAD, VBAT, and SVA
*/
wcd->intr1 = of_irq_get_byname(wcd->dev->of_node, "intr1");
- if (wcd->intr1 < 0) {
- if (wcd->intr1 != -EPROBE_DEFER)
- dev_err(wcd->dev, "Unable to configure IRQ\n");
-
- return wcd->intr1;
- }
+ if (wcd->intr1 < 0)
+ return dev_err_probe(wcd->dev, wcd->intr1,
+ "Unable to configure IRQ\n");
ret = devm_regmap_add_irq_chip(wcd->dev, wcd->regmap, wcd->intr1,
IRQF_TRIGGER_HIGH, 0,
&wcd9335_regmap_irq1_chip, &wcd->irq_data);
if (ret)
- dev_err(wcd->dev, "Failed to register IRQ chip: %d\n", ret);
+ return dev_err_probe(wcd->dev, ret, "Failed to register IRQ chip\n");
- return ret;
+ return 0;
}
static int wcd9335_slim_probe(struct slim_device *slim)
@@ -5207,17 +5204,15 @@ static int wcd9335_slim_status(struct slim_device *sdev,
slim_get_logical_addr(wcd->slim_ifc_dev);
wcd->regmap = regmap_init_slimbus(sdev, &wcd9335_regmap_config);
- if (IS_ERR(wcd->regmap)) {
- dev_err(dev, "Failed to allocate slim register map\n");
- return PTR_ERR(wcd->regmap);
- }
+ if (IS_ERR(wcd->regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->regmap),
+ "Failed to allocate slim register map\n");
wcd->if_regmap = regmap_init_slimbus(wcd->slim_ifc_dev,
&wcd9335_ifc_regmap_config);
- if (IS_ERR(wcd->if_regmap)) {
- dev_err(dev, "Failed to allocate ifc register map\n");
- return PTR_ERR(wcd->if_regmap);
- }
+ if (IS_ERR(wcd->if_regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
+ "Failed to allocate ifc register map\n");
ret = wcd9335_bring_up(wcd);
if (ret) {
diff --git a/sound/soc/codecs/wcd934x.c b/sound/soc/codecs/wcd934x.c
index 783479a4d535..c0d1fa36d841 100644
--- a/sound/soc/codecs/wcd934x.c
+++ b/sound/soc/codecs/wcd934x.c
@@ -5868,10 +5868,9 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
slim_get_logical_addr(wcd->sidev);
wcd->if_regmap = regmap_init_slimbus(wcd->sidev,
&wcd934x_ifc_regmap_config);
- if (IS_ERR(wcd->if_regmap)) {
- dev_err(dev, "Failed to allocate ifc register map\n");
- return PTR_ERR(wcd->if_regmap);
- }
+ if (IS_ERR(wcd->if_regmap))
+ return dev_err_probe(dev, PTR_ERR(wcd->if_regmap),
+ "Failed to allocate ifc register map\n");
of_property_read_u32(dev->parent->of_node, "qcom,dmic-sample-rate",
&wcd->dmic_sample_rate);
@@ -5893,12 +5892,12 @@ static int wcd934x_codec_parse_data(struct wcd934x_codec *wcd)
static int wcd934x_codec_probe(struct platform_device *pdev)
{
- struct wcd934x_ddata *data = dev_get_drvdata(pdev->dev.parent);
- struct wcd934x_codec *wcd;
struct device *dev = &pdev->dev;
+ struct wcd934x_ddata *data = dev_get_drvdata(dev->parent);
+ struct wcd934x_codec *wcd;
int ret, irq;
- wcd = devm_kzalloc(&pdev->dev, sizeof(*wcd), GFP_KERNEL);
+ wcd = devm_kzalloc(dev, sizeof(*wcd), GFP_KERNEL);
if (!wcd)
return -ENOMEM;
@@ -5923,19 +5922,15 @@ static int wcd934x_codec_probe(struct platform_device *pdev)
memcpy(wcd->tx_chs, wcd934x_tx_chs, sizeof(wcd934x_tx_chs));
irq = regmap_irq_get_virq(data->irq_data, WCD934X_IRQ_SLIMBUS);
- if (irq < 0) {
- dev_err(wcd->dev, "Failed to get SLIM IRQ\n");
- return irq;
- }
+ if (irq < 0)
+ return dev_err_probe(wcd->dev, irq, "Failed to get SLIM IRQ\n");
ret = devm_request_threaded_irq(dev, irq, NULL,
wcd934x_slim_irq_handler,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"slim", wcd);
- if (ret) {
- dev_err(dev, "Failed to request slimbus irq\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to request slimbus irq\n");
wcd934x_register_mclk_output(wcd);
platform_set_drvdata(pdev, wcd);
diff --git a/sound/soc/codecs/wcd938x.c b/sound/soc/codecs/wcd938x.c
index fcac763b04d1..11b264a63b04 100644
--- a/sound/soc/codecs/wcd938x.c
+++ b/sound/soc/codecs/wcd938x.c
@@ -4235,18 +4235,15 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
int ret;
wcd938x->reset_gpio = of_get_named_gpio(dev->of_node, "reset-gpios", 0);
- if (wcd938x->reset_gpio < 0) {
- dev_err(dev, "Failed to get reset gpio: err = %d\n",
- wcd938x->reset_gpio);
- return wcd938x->reset_gpio;
- }
+ if (wcd938x->reset_gpio < 0)
+ return dev_err_probe(dev, wcd938x->reset_gpio,
+ "Failed to get reset gpio\n");
wcd938x->us_euro_gpio = devm_gpiod_get_optional(dev, "us-euro",
GPIOD_OUT_LOW);
- if (IS_ERR(wcd938x->us_euro_gpio)) {
- dev_err(dev, "us-euro swap Control GPIO not found\n");
- return PTR_ERR(wcd938x->us_euro_gpio);
- }
+ if (IS_ERR(wcd938x->us_euro_gpio))
+ return dev_err_probe(dev, PTR_ERR(wcd938x->us_euro_gpio),
+ "us-euro swap Control GPIO not found\n");
cfg->swap_gnd_mic = wcd938x_swap_gnd_mic;
@@ -4256,16 +4253,12 @@ static int wcd938x_populate_dt_data(struct wcd938x_priv *wcd938x, struct device
wcd938x->supplies[3].supply = "vdd-mic-bias";
ret = regulator_bulk_get(dev, WCD938X_MAX_SUPPLY, wcd938x->supplies);
- if (ret) {
- dev_err(dev, "Failed to get supplies: err = %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get supplies\n");
ret = regulator_bulk_enable(WCD938X_MAX_SUPPLY, wcd938x->supplies);
- if (ret) {
- dev_err(dev, "Failed to enable supplies: err = %d\n", ret);
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable supplies\n");
wcd938x_dt_parse_micbias_info(dev, wcd938x);
@@ -4529,11 +4522,9 @@ static int wcd938x_probe(struct platform_device *pdev)
return 0;
}
-static int wcd938x_remove(struct platform_device *pdev)
+static void wcd938x_remove(struct platform_device *pdev)
{
component_master_del(&pdev->dev, &wcd938x_comp_ops);
-
- return 0;
}
#if defined(CONFIG_OF)
@@ -4547,7 +4538,7 @@ MODULE_DEVICE_TABLE(of, wcd938x_dt_match);
static struct platform_driver wcd938x_codec_driver = {
.probe = wcd938x_probe,
- .remove = wcd938x_remove,
+ .remove_new = wcd938x_remove,
.driver = {
.name = "wcd938x_codec",
.of_match_table = of_match_ptr(wcd938x_dt_match),
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index adaf886b0a9d..3bdbdf3770b5 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -2148,7 +2148,7 @@ err_jack_codec_dev:
return ret;
}
-static int wm5102_remove(struct platform_device *pdev)
+static void wm5102_remove(struct platform_device *pdev)
{
struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
struct arizona *arizona = wm5102->core.arizona;
@@ -2163,8 +2163,6 @@ static int wm5102_remove(struct platform_device *pdev)
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
arizona_jack_codec_dev_remove(&wm5102->core);
-
- return 0;
}
static struct platform_driver wm5102_codec_driver = {
@@ -2172,7 +2170,7 @@ static struct platform_driver wm5102_codec_driver = {
.name = "wm5102-codec",
},
.probe = wm5102_probe,
- .remove = wm5102_remove,
+ .remove_new = wm5102_remove,
};
module_platform_driver(wm5102_codec_driver);
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index e0b971620d0f..ad670300de8d 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -2506,7 +2506,7 @@ err_jack_codec_dev:
return ret;
}
-static int wm5110_remove(struct platform_device *pdev)
+static void wm5110_remove(struct platform_device *pdev)
{
struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
struct arizona *arizona = wm5110->core.arizona;
@@ -2523,8 +2523,6 @@ static int wm5110_remove(struct platform_device *pdev)
arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
arizona_jack_codec_dev_remove(&wm5110->core);
-
- return 0;
}
static struct platform_driver wm5110_codec_driver = {
@@ -2532,7 +2530,7 @@ static struct platform_driver wm5110_codec_driver = {
.name = "wm5110-codec",
},
.probe = wm5110_probe,
- .remove = wm5110_remove,
+ .remove_new = wm5110_remove,
};
module_platform_driver(wm5110_codec_driver);
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 41346e5ec5ad..1dc8e20bdace 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -9,7 +9,6 @@
*
* TODO:
* - TDM mode configuration.
- * - Digital microphone support.
*/
#include <linux/module.h>
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 8fe9a75d1235..bca3ebe0dac4 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4657,11 +4657,9 @@ static int wm8994_probe(struct platform_device *pdev)
return ret;
}
-static int wm8994_remove(struct platform_device *pdev)
+static void wm8994_remove(struct platform_device *pdev)
{
pm_runtime_disable(&pdev->dev);
-
- return 0;
}
#ifdef CONFIG_PM_SLEEP
@@ -4701,7 +4699,7 @@ static struct platform_driver wm8994_codec_driver = {
.pm = &wm8994_pm_ops,
},
.probe = wm8994_probe,
- .remove = wm8994_remove,
+ .remove_new = wm8994_remove,
};
module_platform_driver(wm8994_codec_driver);
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index c0207e9a7d53..87442840f0af 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -1193,7 +1193,7 @@ err_jack_codec_dev:
return ret;
}
-static int wm8997_remove(struct platform_device *pdev)
+static void wm8997_remove(struct platform_device *pdev)
{
struct wm8997_priv *wm8997 = platform_get_drvdata(pdev);
struct arizona *arizona = wm8997->core.arizona;
@@ -1203,8 +1203,6 @@ static int wm8997_remove(struct platform_device *pdev)
arizona_free_spk_irqs(arizona);
arizona_jack_codec_dev_remove(&wm8997->core);
-
- return 0;
}
static struct platform_driver wm8997_codec_driver = {
@@ -1212,7 +1210,7 @@ static struct platform_driver wm8997_codec_driver = {
.name = "wm8997-codec",
},
.probe = wm8997_probe,
- .remove = wm8997_remove,
+ .remove_new = wm8997_remove,
};
module_platform_driver(wm8997_codec_driver);
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 79fc6bbaa3aa..3c2c4d12c08e 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -1409,7 +1409,7 @@ err_pm_disable:
return ret;
}
-static int wm8998_remove(struct platform_device *pdev)
+static void wm8998_remove(struct platform_device *pdev)
{
struct wm8998_priv *wm8998 = platform_get_drvdata(pdev);
struct arizona *arizona = wm8998->core.arizona;
@@ -1419,8 +1419,6 @@ static int wm8998_remove(struct platform_device *pdev)
arizona_free_spk_irqs(arizona);
arizona_jack_codec_dev_remove(&wm8998->core);
-
- return 0;
}
static struct platform_driver wm8998_codec_driver = {
@@ -1428,7 +1426,7 @@ static struct platform_driver wm8998_codec_driver = {
.name = "wm8998-codec",
},
.probe = wm8998_probe,
- .remove = wm8998_remove,
+ .remove_new = wm8998_remove,
};
module_platform_driver(wm8998_codec_driver);
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index ea0dbc634ecf..216120b68b64 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -787,6 +787,8 @@ static int wm_adsp_request_firmware_file(struct wm_adsp *dsp,
adsp_dbg(dsp, "Failed to request '%s'\n", *filename);
kfree(*filename);
*filename = NULL;
+ } else {
+ adsp_dbg(dsp, "Found '%s'\n", *filename);
}
return ret;
@@ -807,7 +809,6 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
cirrus_dir, system_name,
asoc_component_prefix, "wmfw")) {
- adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
cirrus_dir, system_name,
asoc_component_prefix, "bin");
@@ -819,7 +820,6 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
cirrus_dir, system_name,
NULL, "wmfw")) {
- adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
if (asoc_component_prefix)
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
cirrus_dir, system_name,
@@ -835,7 +835,6 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
if (!wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
"", NULL, NULL, "wmfw")) {
- adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
"", NULL, NULL, "bin");
return 0;
@@ -844,12 +843,35 @@ static int wm_adsp_request_firmware_files(struct wm_adsp *dsp,
ret = wm_adsp_request_firmware_file(dsp, wmfw_firmware, wmfw_filename,
cirrus_dir, NULL, NULL, "wmfw");
if (!ret) {
- adsp_dbg(dsp, "Found '%s'\n", *wmfw_filename);
wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
cirrus_dir, NULL, NULL, "bin");
return 0;
}
+ if (dsp->wmfw_optional) {
+ if (system_name) {
+ if (asoc_component_prefix)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ asoc_component_prefix, "bin");
+
+ if (!*coeff_firmware)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, system_name,
+ NULL, "bin");
+ }
+
+ if (!*coeff_firmware)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ "", NULL, NULL, "bin");
+
+ if (!*coeff_firmware)
+ wm_adsp_request_firmware_file(dsp, coeff_firmware, coeff_filename,
+ cirrus_dir, NULL, NULL, "bin");
+
+ return 0;
+ }
+
adsp_err(dsp, "Failed to request firmware <%s>%s-%s-%s<-%s<%s>>.wmfw\n",
cirrus_dir, dsp->part, dsp->fwf_name, wm_adsp_fw[dsp->fw].file,
system_name, asoc_component_prefix);
@@ -995,11 +1017,8 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
-static void wm_adsp_boot_work(struct work_struct *work)
+int wm_adsp_power_up(struct wm_adsp *dsp)
{
- struct wm_adsp *dsp = container_of(work,
- struct wm_adsp,
- boot_work);
int ret = 0;
char *wmfw_filename = NULL;
const struct firmware *wmfw_firmware = NULL;
@@ -1010,16 +1029,28 @@ static void wm_adsp_boot_work(struct work_struct *work)
&wmfw_firmware, &wmfw_filename,
&coeff_firmware, &coeff_filename);
if (ret)
- return;
+ return ret;
- cs_dsp_power_up(&dsp->cs_dsp,
- wmfw_firmware, wmfw_filename,
- coeff_firmware, coeff_filename,
- wm_adsp_fw_text[dsp->fw]);
+ ret = cs_dsp_power_up(&dsp->cs_dsp,
+ wmfw_firmware, wmfw_filename,
+ coeff_firmware, coeff_filename,
+ wm_adsp_fw_text[dsp->fw]);
wm_adsp_release_firmware_files(dsp,
wmfw_firmware, wmfw_filename,
coeff_firmware, coeff_filename);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp_power_up);
+
+static void wm_adsp_boot_work(struct work_struct *work)
+{
+ struct wm_adsp *dsp = container_of(work,
+ struct wm_adsp,
+ boot_work);
+
+ wm_adsp_power_up(dsp);
}
int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
@@ -1102,8 +1133,10 @@ int wm_adsp2_component_probe(struct wm_adsp *dsp, struct snd_soc_component *comp
{
char preload[32];
- snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
- snd_soc_component_disable_pin(component, preload);
+ if (!dsp->cs_dsp.no_core_startstop) {
+ snprintf(preload, ARRAY_SIZE(preload), "%s Preload", dsp->cs_dsp.name);
+ snd_soc_component_disable_pin(component, preload);
+ }
cs_dsp_init_debugfs(&dsp->cs_dsp, component->debugfs_root);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index dc2f7a096e26..769904d34a87 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -34,6 +34,7 @@ struct wm_adsp {
unsigned int sys_config_size;
int fw;
+ bool wmfw_optional;
struct work_struct boot_work;
int (*pre_run)(struct wm_adsp *dsp);
@@ -90,6 +91,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
+int wm_adsp_power_up(struct wm_adsp *dsp);
+
irqreturn_t wm_adsp2_bus_error(int irq, void *data);
irqreturn_t wm_halo_bus_error(int irq, void *data);
irqreturn_t wm_halo_wdt_expire(int irq, void *data);
diff --git a/sound/soc/codecs/zl38060.c b/sound/soc/codecs/zl38060.c
index c3d0a2a7c36f..28c92d90299e 100644
--- a/sound/soc/codecs/zl38060.c
+++ b/sound/soc/codecs/zl38060.c
@@ -608,7 +608,7 @@ static int zl38_spi_probe(struct spi_device *spi)
&zl38_dai, 1);
}
-static const struct of_device_id zl38_dt_ids[] = {
+static const struct of_device_id zl38_dt_ids[] __maybe_unused = {
{ .compatible = "mscc,zl38060", },
{ /* sentinel */ }
};