summaryrefslogtreecommitdiff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/88pm860x-codec.c4
-rw-r--r--sound/soc/codecs/Kconfig45
-rw-r--r--sound/soc/codecs/Makefile12
-rw-r--r--sound/soc/codecs/ak4613.c11
-rw-r--r--sound/soc/codecs/aw88261.c1300
-rw-r--r--sound/soc/codecs/aw88261.h459
-rw-r--r--sound/soc/codecs/aw88395/aw88395_lib.c193
-rw-r--r--sound/soc/codecs/aw88395/aw88395_reg.h1
-rw-r--r--sound/soc/codecs/cs35l56.c52
-rw-r--r--sound/soc/codecs/cs42l43-jack.c946
-rw-r--r--sound/soc/codecs/cs42l43-sdw.c74
-rw-r--r--sound/soc/codecs/cs42l43.c2278
-rw-r--r--sound/soc/codecs/cs42l43.h131
-rw-r--r--sound/soc/codecs/cs42l51-i2c.c2
-rw-r--r--sound/soc/codecs/cs47l15.c6
-rw-r--r--sound/soc/codecs/cs47l24.c8
-rw-r--r--sound/soc/codecs/cs47l35.c8
-rw-r--r--sound/soc/codecs/cs47l85.c8
-rw-r--r--sound/soc/codecs/cs47l90.c8
-rw-r--r--sound/soc/codecs/cs47l92.c6
-rw-r--r--sound/soc/codecs/cx2072x.c11
-rw-r--r--sound/soc/codecs/es8326.c312
-rw-r--r--sound/soc/codecs/es8326.h23
-rw-r--r--sound/soc/codecs/hdmi-codec.c44
-rw-r--r--sound/soc/codecs/jz4740.c2
-rw-r--r--sound/soc/codecs/l3.c132
-rw-r--r--sound/soc/codecs/max98363.c41
-rw-r--r--sound/soc/codecs/max98373-sdw.c47
-rw-r--r--sound/soc/codecs/max98926.c3
-rw-r--r--sound/soc/codecs/max98927.c357
-rw-r--r--sound/soc/codecs/nau8821.c42
-rw-r--r--sound/soc/codecs/nau8821.h25
-rw-r--r--sound/soc/codecs/rt1011.c2
-rw-r--r--sound/soc/codecs/rt1015.c1
-rw-r--r--sound/soc/codecs/rt1015p.c1
-rw-r--r--sound/soc/codecs/rt1016.c1
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.c824
-rw-r--r--sound/soc/codecs/rt1017-sdca-sdw.h183
-rw-r--r--sound/soc/codecs/rt1019.c1
-rw-r--r--sound/soc/codecs/rt1305.c2
-rw-r--r--sound/soc/codecs/rt1308-sdw.c54
-rw-r--r--sound/soc/codecs/rt1308.c2
-rw-r--r--sound/soc/codecs/rt1316-sdw.c56
-rw-r--r--sound/soc/codecs/rt1318-sdw.c43
-rw-r--r--sound/soc/codecs/rt5514-spi.c1
-rw-r--r--sound/soc/codecs/rt5514.c1
-rw-r--r--sound/soc/codecs/rt5640.c59
-rw-r--r--sound/soc/codecs/rt5640.h2
-rw-r--r--sound/soc/codecs/rt5645.c19
-rw-r--r--sound/soc/codecs/rt5659.c1
-rw-r--r--sound/soc/codecs/rt5660.c2
-rw-r--r--sound/soc/codecs/rt5665.c17
-rw-r--r--sound/soc/codecs/rt5668.c17
-rw-r--r--sound/soc/codecs/rt5682-i2c.c11
-rw-r--r--sound/soc/codecs/rt5682-sdw.c63
-rw-r--r--sound/soc/codecs/rt5682.c23
-rw-r--r--sound/soc/codecs/rt5682.h3
-rw-r--r--sound/soc/codecs/rt5682s.c16
-rw-r--r--sound/soc/codecs/rt5682s.h2
-rw-r--r--sound/soc/codecs/rt700-sdw.c7
-rw-r--r--sound/soc/codecs/rt700.c46
-rw-r--r--sound/soc/codecs/rt711-sdca-sdw.c3
-rw-r--r--sound/soc/codecs/rt711-sdca.c48
-rw-r--r--sound/soc/codecs/rt711-sdw.c7
-rw-r--r--sound/soc/codecs/rt711.c47
-rw-r--r--sound/soc/codecs/rt712-sdca-dmic.c47
-rw-r--r--sound/soc/codecs/rt712-sdca-sdw.c3
-rw-r--r--sound/soc/codecs/rt712-sdca.c45
-rw-r--r--sound/soc/codecs/rt715-sdca-sdw.c5
-rw-r--r--sound/soc/codecs/rt715-sdca.c41
-rw-r--r--sound/soc/codecs/rt715-sdw.c9
-rw-r--r--sound/soc/codecs/rt715.c100
-rw-r--r--sound/soc/codecs/rt715.h7
-rw-r--r--sound/soc/codecs/ssm3515.c2
-rw-r--r--sound/soc/codecs/sta32x.c2
-rw-r--r--sound/soc/codecs/sta350.c2
-rw-r--r--sound/soc/codecs/sta529.c2
-rw-r--r--sound/soc/codecs/stac9766.c2
-rw-r--r--sound/soc/codecs/sti-sas.c2
-rw-r--r--sound/soc/codecs/tas5805m.c3
-rw-r--r--sound/soc/codecs/tlv320aic32x4-clk.c16
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c2
-rw-r--r--sound/soc/codecs/tlv320aic32x4.h5
-rw-r--r--sound/soc/codecs/uda134x.c587
-rw-r--r--sound/soc/codecs/uda134x.h33
-rw-r--r--sound/soc/codecs/wm2200.c12
-rw-r--r--sound/soc/codecs/wm5102.c6
-rw-r--r--sound/soc/codecs/wm5110.c8
-rw-r--r--sound/soc/codecs/wm8904.c2
-rw-r--r--sound/soc/codecs/wm8960.c42
-rw-r--r--sound/soc/codecs/wm8960.h4
-rw-r--r--sound/soc/codecs/wm8994.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c24
-rw-r--r--sound/soc/codecs/wm_adsp.h3
94 files changed, 7641 insertions, 1533 deletions
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 3574c68e0dda..d99b674d574b 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -143,7 +143,7 @@ struct pm860x_priv {
struct pm860x_det det;
int irq[4];
- unsigned char name[4][MAX_NAME_LEN+1];
+ unsigned char name[4][MAX_NAME_LEN];
};
/* -9450dB to 0dB in 150dB steps ( mute instead of -9450dB) */
@@ -1373,7 +1373,7 @@ static int pm860x_codec_probe(struct platform_device *pdev)
return -EINVAL;
}
pm860x->irq[i] = res->start + chip->irq_base;
- strncpy(pm860x->name[i], res->name, MAX_NAME_LEN);
+ strscpy(pm860x->name[i], res->name, MAX_NAME_LEN);
}
ret = devm_snd_soc_register_component(&pdev->dev,
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 88c3dbe47d71..95b5bd883215 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -15,7 +15,6 @@ config SND_SOC_ALL_CODECS
tristate "Build all ASoC CODEC drivers"
depends on COMPILE_TEST
imply SND_SOC_88PM860X
- imply SND_SOC_L3
imply SND_SOC_AB8500_CODEC
imply SND_SOC_AC97_CODEC
imply SND_SOC_AD1836
@@ -56,6 +55,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_AUDIO_IIO_AUX
imply SND_SOC_AW8738
imply SND_SOC_AW88395
+ imply SND_SOC_AW88261
imply SND_SOC_BT_SCO
imply SND_SOC_BD28623
imply SND_SOC_CHV3_CODEC
@@ -75,6 +75,8 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_CS35L56_SDW
imply SND_SOC_CS42L42
imply SND_SOC_CS42L42_SDW
+ imply SND_SOC_CS42L43
+ imply SND_SOC_CS42L43_SDW
imply SND_SOC_CS42L51_I2C
imply SND_SOC_CS42L52
imply SND_SOC_CS42L56
@@ -184,6 +186,7 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_RT1015
imply SND_SOC_RT1015P
imply SND_SOC_RT1016
+ imply SND_SOC_RT1017_SDCA_SDW
imply SND_SOC_RT1019
imply SND_SOC_RT1305
imply SND_SOC_RT1308
@@ -266,7 +269,6 @@ config SND_SOC_ALL_CODECS
imply SND_SOC_TWL4030
imply SND_SOC_TWL6040
imply SND_SOC_UDA1334
- imply SND_SOC_UDA134X
imply SND_SOC_UDA1380
imply SND_SOC_WCD9335
imply SND_SOC_WCD934X
@@ -652,6 +654,20 @@ config SND_SOC_AW88395
digital Smart K audio amplifier with an integrated 10V
smart boost convert.
+config SND_SOC_AW88261
+ tristate "Soc Audio for awinic aw88261"
+ depends on I2C
+ select CRC8
+ select REGMAP_I2C
+ select GPIOLIB
+ select SND_SOC_AW88395_LIB
+ help
+ This option enables support for aw88261 Smart PA.
+ The awinic AW88261 is an I2S/TDM input, high efficiency
+ digital Smart K audio amplifier. The output voltage of
+ boost converter can be adjusted smartly according to
+ the input amplitude.
+
config SND_SOC_BD28623
tristate "ROHM BD28623 CODEC"
help
@@ -801,6 +817,20 @@ config SND_SOC_CS42L42_SDW
help
Enable support for Cirrus Logic CS42L42 codec with Soundwire control
+config SND_SOC_CS42L43
+ tristate "Cirrus Logic CS42L43 CODEC"
+ depends on MFD_CS42L43
+ help
+ Select this to support the audio functions of the Cirrus Logic
+ CS42L43 PC CODEC.
+
+config SND_SOC_CS42L43_SDW
+ tristate "Cirrus Logic CS42L43 CODEC (SoundWire)"
+ depends on SND_SOC_CS42L43 && MFD_CS42L43_SDW
+ help
+ Select this to support the audio functions of the Cirrus Logic
+ CS42L43 PC CODEC over SoundWire.
+
config SND_SOC_CS42L51
tristate
@@ -978,9 +1008,6 @@ config SND_SOC_JZ4770_CODEC
This driver can also be built as a module. If so, the module
will be called snd-soc-jz4770-codec.
-config SND_SOC_L3
- tristate
-
config SND_SOC_DA7210
tristate
depends on SND_SOC_I2C_AND_SPI
@@ -1428,6 +1455,11 @@ config SND_SOC_RT1016
tristate
depends on I2C
+config SND_SOC_RT1017_SDCA_SDW
+ tristate "Realtek RT1017 SDCA Codec - SDW"
+ depends on SOUNDWIRE
+ select REGMAP_SOUNDWIRE
+
config SND_SOC_RT1019
tristate
depends on I2C
@@ -1932,9 +1964,6 @@ config SND_SOC_UDA1334
and has basic features such as de-emphasis (at 44.1 kHz sampling
rate) and mute.
-config SND_SOC_UDA134X
- tristate
-
config SND_SOC_UDA1380
tristate
depends on I2C
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 32dcc6de58bd..c8502a49b40a 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -50,6 +50,7 @@ snd-soc-aw8738-objs := aw8738.o
snd-soc-aw88395-lib-objs := aw88395/aw88395_lib.o
snd-soc-aw88395-objs := aw88395/aw88395.o \
aw88395/aw88395_device.o
+snd-soc-aw88261-objs := aw88261.o
snd-soc-bd28623-objs := bd28623.o
snd-soc-bt-sco-objs := bt-sco.o
snd-soc-chv3-codec-objs := chv3-codec.o
@@ -76,6 +77,8 @@ 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
+snd-soc-cs42l43-objs := cs42l43.o cs42l43-jack.o
+snd-soc-cs42l43-sdw-objs := cs42l43-sdw.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
@@ -128,7 +131,6 @@ snd-soc-jz4740-codec-objs := jz4740.o
snd-soc-jz4725b-codec-objs := jz4725b.o
snd-soc-jz4760-codec-objs := jz4760.o
snd-soc-jz4770-codec-objs := jz4770.o
-snd-soc-l3-objs := l3.o
snd-soc-lm4857-objs := lm4857.o
snd-soc-lm49453-objs := lm49453.o
snd-soc-lochnagar-sc-objs := lochnagar-sc.o
@@ -205,6 +207,7 @@ snd-soc-rt1011-objs := rt1011.o
snd-soc-rt1015-objs := rt1015.o
snd-soc-rt1015p-objs := rt1015p.o
snd-soc-rt1016-objs := rt1016.o
+snd-soc-rt1017-sdca-objs := rt1017-sdca-sdw.o
snd-soc-rt1019-objs := rt1019.o
snd-soc-rt1305-objs := rt1305.o
snd-soc-rt1308-objs := rt1308.o
@@ -299,7 +302,6 @@ snd-soc-ts3a227e-objs := ts3a227e.o
snd-soc-twl4030-objs := twl4030.o
snd-soc-twl6040-objs := twl6040.o
snd-soc-uda1334-objs := uda1334.o
-snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wcd-mbhc-objs := wcd-mbhc-v2.o
snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o
@@ -433,6 +435,7 @@ obj-$(CONFIG_SND_SOC_AUDIO_IIO_AUX) += snd-soc-audio-iio-aux.o
obj-$(CONFIG_SND_SOC_AW8738) += snd-soc-aw8738.o
obj-$(CONFIG_SND_SOC_AW88395_LIB) += snd-soc-aw88395-lib.o
obj-$(CONFIG_SND_SOC_AW88395) +=snd-soc-aw88395.o
+obj-$(CONFIG_SND_SOC_AW88261) +=snd-soc-aw88261.o
obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_CHV3_CODEC) += snd-soc-chv3-codec.o
@@ -459,6 +462,8 @@ 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
+obj-$(CONFIG_SND_SOC_CS42L43) += snd-soc-cs42l43.o
+obj-$(CONFIG_SND_SOC_CS42L43_SDW) += snd-soc-cs42l43-sdw.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
obj-$(CONFIG_SND_SOC_CS42L52) += snd-soc-cs42l52.o
@@ -511,7 +516,6 @@ obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o
obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o
obj-$(CONFIG_SND_SOC_JZ4760_CODEC) += snd-soc-jz4760-codec.o
obj-$(CONFIG_SND_SOC_JZ4770_CODEC) += snd-soc-jz4770-codec.o
-obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o
obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o
obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o
obj-$(CONFIG_SND_SOC_LOCHNAGAR_SC) += snd-soc-lochnagar-sc.o
@@ -583,6 +587,7 @@ obj-$(CONFIG_SND_SOC_RT1011) += snd-soc-rt1011.o
obj-$(CONFIG_SND_SOC_RT1015) += snd-soc-rt1015.o
obj-$(CONFIG_SND_SOC_RT1015P) += snd-soc-rt1015p.o
obj-$(CONFIG_SND_SOC_RT1016) += snd-soc-rt1016.o
+obj-$(CONFIG_SND_SOC_RT1017_SDCA_SDW) += snd-soc-rt1017-sdca.o
obj-$(CONFIG_SND_SOC_RT1019) += snd-soc-rt1019.o
obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o
obj-$(CONFIG_SND_SOC_RT1308) += snd-soc-rt1308.o
@@ -679,7 +684,6 @@ obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o
obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o
obj-$(CONFIG_SND_SOC_UDA1334) += snd-soc-uda1334.o
-obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o
obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o
obj-$(CONFIG_SND_SOC_WCD_MBHC) += snd-soc-wcd-mbhc.o
obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index ad56caec9dac..619a817ee91c 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -880,20 +880,11 @@ static void ak4613_parse_of(struct ak4613_priv *priv,
static int ak4613_i2c_probe(struct i2c_client *i2c)
{
struct device *dev = &i2c->dev;
- struct device_node *np = dev->of_node;
const struct regmap_config *regmap_cfg;
struct regmap *regmap;
struct ak4613_priv *priv;
- regmap_cfg = NULL;
- if (np)
- regmap_cfg = of_device_get_match_data(dev);
- else {
- const struct i2c_device_id *id =
- i2c_match_id(ak4613_i2c_id, i2c);
- regmap_cfg = (const struct regmap_config *)id->driver_data;
- }
-
+ regmap_cfg = i2c_get_match_data(i2c);
if (!regmap_cfg)
return -EINVAL;
diff --git a/sound/soc/codecs/aw88261.c b/sound/soc/codecs/aw88261.c
new file mode 100644
index 000000000000..a697b5006b45
--- /dev/null
+++ b/sound/soc/codecs/aw88261.c
@@ -0,0 +1,1300 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.c -- AW88261 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#include <linux/i2c.h>
+#include <linux/firmware.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "aw88261.h"
+#include "aw88395/aw88395_data_type.h"
+#include "aw88395/aw88395_device.h"
+
+static const struct regmap_config aw88261_remap_config = {
+ .val_bits = 16,
+ .reg_bits = 8,
+ .max_register = AW88261_REG_MAX - 1,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+};
+
+static void aw88261_dev_set_volume(struct aw_device *aw_dev, unsigned int value)
+{
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int real_value, volume;
+ unsigned int reg_value;
+
+ volume = min((value + vol_desc->init_volume), (unsigned int)AW88261_MUTE_VOL);
+ real_value = DB_TO_REG_VAL(volume);
+
+ regmap_read(aw_dev->regmap, AW88261_SYSCTRL2_REG, &reg_value);
+
+ real_value = (real_value | (reg_value & AW88261_VOL_START_MASK));
+
+ dev_dbg(aw_dev->dev, "value 0x%x , real_value:0x%x", value, real_value);
+
+ regmap_write(aw_dev->regmap, AW88261_SYSCTRL2_REG, real_value);
+}
+
+static void aw88261_dev_fade_in(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_in_vol = desc->ctl_volume;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_in_time == 0) {
+ aw88261_dev_set_volume(aw_dev, fade_in_vol);
+ return;
+ }
+
+ for (i = AW88261_MUTE_VOL; i >= fade_in_vol; i -= fade_step) {
+ aw88261_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_in_time,
+ aw_dev->fade_in_time + 10);
+ }
+
+ if (i != fade_in_vol)
+ aw88261_dev_set_volume(aw_dev, fade_in_vol);
+}
+
+static void aw88261_dev_fade_out(struct aw_device *aw_dev)
+{
+ struct aw_volume_desc *desc = &aw_dev->volume_desc;
+ int fade_step = aw_dev->fade_step;
+ int i;
+
+ if (fade_step == 0 || aw_dev->fade_out_time == 0) {
+ aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+ return;
+ }
+
+ for (i = desc->ctl_volume; i <= AW88261_MUTE_VOL; i += fade_step) {
+ aw88261_dev_set_volume(aw_dev, i);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+
+ if (i != AW88261_MUTE_VOL) {
+ aw88261_dev_set_volume(aw_dev, AW88261_MUTE_VOL);
+ usleep_range(aw_dev->fade_out_time, aw_dev->fade_out_time + 10);
+ }
+}
+
+static void aw88261_dev_i2s_tx_enable(struct aw_device *aw_dev, bool flag)
+{
+ if (flag)
+ regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+ ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_I2SCFG1_REG,
+ ~AW88261_I2STXEN_MASK, AW88261_I2STXEN_DISABLE_VALUE);
+}
+
+static void aw88261_dev_pwd(struct aw_device *aw_dev, bool pwd)
+{
+ if (pwd)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_PWDN_MASK, AW88261_PWDN_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_PWDN_MASK, AW88261_PWDN_WORKING_VALUE);
+}
+
+static void aw88261_dev_amppd(struct aw_device *aw_dev, bool amppd)
+{
+ if (amppd)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_AMPPD_MASK, AW88261_AMPPD_POWER_DOWN_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_AMPPD_MASK, AW88261_AMPPD_WORKING_VALUE);
+}
+
+static void aw88261_dev_mute(struct aw_device *aw_dev, bool is_mute)
+{
+ if (is_mute) {
+ aw88261_dev_fade_out(aw_dev);
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_HMUTE_MASK, AW88261_HMUTE_ENABLE_VALUE);
+ } else {
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_HMUTE_MASK, AW88261_HMUTE_DISABLE_VALUE);
+ aw88261_dev_fade_in(aw_dev);
+ }
+}
+
+static void aw88261_dev_clear_int_status(struct aw_device *aw_dev)
+{
+ unsigned int int_status;
+
+ /* read int status and clear */
+ regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
+ /* make sure int status is clear */
+ regmap_read(aw_dev->regmap, AW88261_SYSINT_REG, &int_status);
+
+ dev_dbg(aw_dev->dev, "read interrupt reg = 0x%04x", int_status);
+}
+
+static int aw88261_dev_get_iis_status(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+ if ((reg_val & AW88261_BIT_PLL_CHECK) != AW88261_BIT_PLL_CHECK) {
+ dev_err(aw_dev->dev, "check pll lock fail,reg_val:0x%04x", reg_val);
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_mode1_pll(struct aw_device *aw_dev)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode1 iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ return ret;
+ }
+ }
+
+ return -EPERM;
+}
+
+static int aw88261_dev_check_mode2_pll(struct aw_device *aw_dev)
+{
+ unsigned int reg_val;
+ int ret, i;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_PLLCTRL1_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_val &= (~AW88261_CCO_MUX_MASK);
+ if (reg_val == AW88261_CCO_MUX_DIVIDED_VALUE) {
+ dev_dbg(aw_dev->dev, "CCO_MUX is already divider");
+ return -EPERM;
+ }
+
+ /* change mode2 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+ ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_DIVIDED_VALUE);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_get_iis_status(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+
+ /* change mode1 */
+ ret = regmap_update_bits(aw_dev->regmap, AW88261_PLLCTRL1_REG,
+ ~AW88261_CCO_MUX_MASK, AW88261_CCO_MUX_BYPASS_VALUE);
+ if (ret == 0) {
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = aw88261_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 switch to mode1, iis signal check error");
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ break;
+ }
+ }
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_syspll(struct aw_device *aw_dev)
+{
+ int ret;
+
+ ret = aw88261_dev_check_mode1_pll(aw_dev);
+ if (ret) {
+ dev_dbg(aw_dev->dev, "mode1 check iis failed try switch to mode2 check");
+ ret = aw88261_dev_check_mode2_pll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "mode2 check iis failed");
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static int aw88261_dev_check_sysst(struct aw_device *aw_dev)
+{
+ unsigned int check_val;
+ unsigned int reg_val;
+ int ret, i;
+
+ for (i = 0; i < AW88261_DEV_SYSST_CHECK_MAX; i++) {
+ ret = regmap_read(aw_dev->regmap, AW88261_SYSST_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ check_val = reg_val & (~AW88261_BIT_SYSST_CHECK_MASK)
+ & AW88261_BIT_SYSST_CHECK;
+ if (check_val != AW88261_BIT_SYSST_CHECK) {
+ dev_err(aw_dev->dev, "check sysst fail, reg_val=0x%04x, check:0x%x",
+ reg_val, AW88261_BIT_SYSST_CHECK);
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+ } else {
+ return 0;
+ }
+ }
+
+ return -EPERM;
+}
+
+static void aw88261_dev_uls_hmute(struct aw_device *aw_dev, bool uls_hmute)
+{
+ if (uls_hmute)
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_ULS_HMUTE_MASK,
+ AW88261_ULS_HMUTE_ENABLE_VALUE);
+ else
+ regmap_update_bits(aw_dev->regmap, AW88261_SYSCTRL_REG,
+ ~AW88261_ULS_HMUTE_MASK,
+ AW88261_ULS_HMUTE_DISABLE_VALUE);
+}
+
+static void aw88261_reg_force_set(struct aw88261 *aw88261)
+{
+ if (aw88261->frcset_en == AW88261_FRCSET_ENABLE) {
+ /* set FORCE_PWM */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL3_REG,
+ AW88261_FORCE_PWM_MASK, AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE);
+ /* set BOOST_OS_WIDTH */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL5_REG,
+ AW88261_BST_OS_WIDTH_MASK, AW88261_BST_OS_WIDTH_50NS_VALUE);
+ /* set BURST_LOOPR */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL6_REG,
+ AW88261_BST_LOOPR_MASK, AW88261_BST_LOOPR_340K_VALUE);
+ /* set RSQN_DLY */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL7_REG,
+ AW88261_RSQN_DLY_MASK, AW88261_RSQN_DLY_35NS_VALUE);
+ /* set BURST_SSMODE */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL8_REG,
+ AW88261_BURST_SSMODE_MASK, AW88261_BURST_SSMODE_FAST_VALUE);
+ /* set BST_BURST */
+ regmap_update_bits(aw88261->regmap, AW88261_BSTCTRL9_REG,
+ AW88261_BST_BURST_MASK, AW88261_BST_BURST_30MA_VALUE);
+ } else {
+ dev_dbg(aw88261->aw_pa->dev, "needn't set reg value");
+ }
+}
+
+static int aw88261_dev_get_icalk(struct aw_device *aw_dev, int16_t *icalk)
+{
+ u16 reg_icalk, reg_icalkl;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRH4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalk = reg_val & (~AW88261_EF_ISN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRL4_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_icalkl = reg_val & (~AW88261_EF_ISN_GESLP_L_MASK);
+
+ reg_icalk = (reg_icalk >> AW88261_ICALK_SHIFT) & (reg_icalkl >> AW88261_ICALKL_SHIFT);
+
+ if (reg_icalk & (~AW88261_EF_ISN_GESLP_SIGN_MASK))
+ reg_icalk = reg_icalk | ~AW88261_EF_ISN_GESLP_NEG;
+
+ *icalk = (int16_t)reg_icalk;
+
+ return ret;
+}
+
+static int aw88261_dev_get_vcalk(struct aw_device *aw_dev, int16_t *vcalk)
+{
+ u16 reg_vcalk, reg_vcalkl;
+ unsigned int reg_val;
+ int ret;
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalk = (u16)reg_val & (~AW88261_EF_VSN_GESLP_H_MASK);
+
+ ret = regmap_read(aw_dev->regmap, AW88261_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+
+ reg_vcalkl = (u16)reg_val & (~AW88261_EF_VSN_GESLP_L_MASK);
+
+ reg_vcalk = (reg_vcalk >> AW88261_VCALK_SHIFT) & (reg_vcalkl >> AW88261_VCALKL_SHIFT);
+
+ if (reg_vcalk & AW88261_EF_VSN_GESLP_SIGN_MASK)
+ reg_vcalk = reg_vcalk | (~AW88261_EF_VSN_GESLP_NEG);
+ *vcalk = (int16_t)reg_vcalk;
+
+ return ret;
+}
+
+static int aw88261_dev_set_vcalb(struct aw_device *aw_dev)
+{
+ int16_t icalk_val, vcalk_val;
+ int icalk, vcalk, vcalb;
+ u32 reg_val;
+ int ret;
+
+ ret = aw88261_dev_get_icalk(aw_dev, &icalk_val);
+ if (ret)
+ return ret;
+
+ ret = aw88261_dev_get_vcalk(aw_dev, &vcalk_val);
+ if (ret)
+ return ret;
+
+ icalk = AW88261_CABL_BASE_VALUE + AW88261_ICABLK_FACTOR * icalk_val;
+ vcalk = AW88261_CABL_BASE_VALUE + AW88261_VCABLK_FACTOR * vcalk_val;
+ if (!vcalk)
+ return -EINVAL;
+
+ vcalb = AW88261_VCAL_FACTOR * icalk / vcalk;
+ reg_val = (unsigned int)vcalb;
+
+ dev_dbg(aw_dev->dev, "icalk=%d, vcalk=%d, vcalb=%d, reg_val=0x%04x",
+ icalk, vcalk, vcalb, reg_val);
+ ret = regmap_write(aw_dev->regmap, AW88261_VSNTM1_REG, reg_val);
+
+ return ret;
+}
+
+static int aw88261_dev_reg_update(struct aw88261 *aw88261,
+ unsigned char *data, unsigned int len)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct aw_volume_desc *vol_desc = &aw_dev->volume_desc;
+ unsigned int read_val, efcheck_val, read_vol;
+ int data_len, i, ret;
+ int16_t *reg_data;
+ u16 reg_val;
+ u8 reg_addr;
+
+ if (!len || !data) {
+ dev_err(aw_dev->dev, "reg data is null or len is 0");
+ return -EINVAL;
+ }
+
+ reg_data = (int16_t *)data;
+ data_len = len >> 1;
+
+ if (data_len & 0x1) {
+ dev_err(aw_dev->dev, "data len:%d unsupported", data_len);
+ return -EINVAL;
+ }
+
+ for (i = 0; i < data_len; i += 2) {
+ reg_addr = reg_data[i];
+ reg_val = reg_data[i + 1];
+
+ if (reg_addr == AW88261_SYSCTRL_REG) {
+ aw88261->amppd_st = reg_val & (~AW88261_AMPPD_MASK);
+ ret = regmap_read(aw_dev->regmap, reg_addr, &read_val);
+ if (ret)
+ break;
+
+ read_val &= (~AW88261_AMPPD_MASK) | (~AW88261_PWDN_MASK) |
+ (~AW88261_HMUTE_MASK);
+ reg_val &= (AW88261_AMPPD_MASK | AW88261_PWDN_MASK | AW88261_HMUTE_MASK);
+ reg_val |= read_val;
+
+ /* enable uls hmute */
+ reg_val &= AW88261_ULS_HMUTE_MASK;
+ reg_val |= AW88261_ULS_HMUTE_ENABLE_VALUE;
+ }
+
+ if (reg_addr == AW88261_DBGCTRL_REG) {
+ efcheck_val = reg_val & (~AW88261_EF_DBMD_MASK);
+ if (efcheck_val == AW88261_OR_VALUE)
+ aw88261->efuse_check = AW88261_EF_OR_CHECK;
+ else
+ aw88261->efuse_check = AW88261_EF_AND_CHECK;
+ }
+
+ /* i2stxen */
+ if (reg_addr == AW88261_I2SCTRL3_REG) {
+ /* close tx */
+ reg_val &= AW88261_I2STXEN_MASK;
+ reg_val |= AW88261_I2STXEN_DISABLE_VALUE;
+ }
+
+ if (reg_addr == AW88261_SYSCTRL2_REG) {
+ read_vol = (reg_val & (~AW88261_VOL_MASK)) >>
+ AW88261_VOL_START_BIT;
+ aw_dev->volume_desc.init_volume =
+ REG_VAL_TO_DB(read_vol);
+ }
+
+ if (reg_addr == AW88261_VSNTM1_REG)
+ continue;
+
+ ret = regmap_write(aw_dev->regmap, reg_addr, reg_val);
+ if (ret)
+ break;
+ }
+
+ ret = aw88261_dev_set_vcalb(aw_dev);
+ if (ret)
+ return ret;
+
+ if (aw_dev->prof_cur != aw_dev->prof_index)
+ vol_desc->ctl_volume = 0;
+
+ /* keep min volume */
+ aw88261_dev_set_volume(aw_dev, vol_desc->mute_volume);
+
+ return ret;
+}
+
+static char *aw88261_dev_get_prof_name(struct aw_device *aw_dev, int index)
+{
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ struct aw_prof_desc *prof_desc;
+
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "index[%d] overflow count[%d]",
+ index, aw_dev->prof_info.count);
+ return NULL;
+ }
+
+ prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return prof_info->prof_name_list[prof_desc->id];
+}
+
+static int aw88261_dev_get_prof_data(struct aw_device *aw_dev, int index,
+ struct aw_prof_desc **prof_desc)
+{
+ if ((index >= aw_dev->prof_info.count) || (index < 0)) {
+ dev_err(aw_dev->dev, "%s: index[%d] overflow count[%d]\n",
+ __func__, index, aw_dev->prof_info.count);
+ return -EINVAL;
+ }
+
+ *prof_desc = &aw_dev->prof_info.prof_desc[index];
+
+ return 0;
+}
+
+static int aw88261_dev_fw_update(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct aw_prof_desc *prof_index_desc;
+ struct aw_sec_data_desc *sec_desc;
+ char *prof_name;
+ int ret;
+
+ prof_name = aw88261_dev_get_prof_name(aw_dev, aw_dev->prof_index);
+ if (!prof_name) {
+ dev_err(aw_dev->dev, "get prof name failed");
+ return -EINVAL;
+ }
+
+ dev_dbg(aw_dev->dev, "start update %s", prof_name);
+
+ ret = aw88261_dev_get_prof_data(aw_dev, aw_dev->prof_index, &prof_index_desc);
+ if (ret)
+ return ret;
+
+ /* update reg */
+ sec_desc = prof_index_desc->sec_desc;
+ ret = aw88261_dev_reg_update(aw88261, sec_desc[AW88395_DATA_TYPE_REG].data,
+ sec_desc[AW88395_DATA_TYPE_REG].len);
+ if (ret) {
+ dev_err(aw_dev->dev, "update reg failed");
+ return ret;
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return ret;
+}
+
+static int aw88261_dev_start(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ if (aw_dev->status == AW88261_DEV_PW_ON) {
+ dev_info(aw_dev->dev, "already power on");
+ return 0;
+ }
+
+ /* power on */
+ aw88261_dev_pwd(aw_dev, false);
+ usleep_range(AW88261_2000_US, AW88261_2000_US + 10);
+
+ ret = aw88261_dev_check_syspll(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "pll check failed cannot start");
+ goto pll_check_fail;
+ }
+
+ /* amppd on */
+ aw88261_dev_amppd(aw_dev, false);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 50);
+
+ /* check i2s status */
+ ret = aw88261_dev_check_sysst(aw_dev);
+ if (ret) {
+ dev_err(aw_dev->dev, "sysst check failed");
+ goto sysst_check_fail;
+ }
+
+ /* enable tx feedback */
+ aw88261_dev_i2s_tx_enable(aw_dev, true);
+
+ if (aw88261->amppd_st)
+ aw88261_dev_amppd(aw_dev, true);
+
+ aw88261_reg_force_set(aw88261);
+
+ /* close uls mute */
+ aw88261_dev_uls_hmute(aw_dev, false);
+
+ /* close mute */
+ if (!aw88261->mute_st)
+ aw88261_dev_mute(aw_dev, false);
+
+ /* clear inturrupt */
+ aw88261_dev_clear_int_status(aw_dev);
+ aw_dev->status = AW88261_DEV_PW_ON;
+
+ return 0;
+
+sysst_check_fail:
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+ aw88261_dev_clear_int_status(aw_dev);
+ aw88261_dev_amppd(aw_dev, true);
+pll_check_fail:
+ aw88261_dev_pwd(aw_dev, true);
+ aw_dev->status = AW88261_DEV_PW_OFF;
+
+ return ret;
+}
+
+static int aw88261_dev_stop(struct aw_device *aw_dev)
+{
+ if (aw_dev->status == AW88261_DEV_PW_OFF) {
+ dev_info(aw_dev->dev, "already power off");
+ return 0;
+ }
+
+ aw_dev->status = AW88261_DEV_PW_OFF;
+
+ /* clear inturrupt */
+ aw88261_dev_clear_int_status(aw_dev);
+
+ aw88261_dev_uls_hmute(aw_dev, true);
+ /* set mute */
+ aw88261_dev_mute(aw_dev, true);
+
+ /* close tx feedback */
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+ /* enable amppd */
+ aw88261_dev_amppd(aw_dev, true);
+
+ /* set power down */
+ aw88261_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88261_reg_update(struct aw88261 *aw88261, bool force)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ if (force) {
+ ret = regmap_write(aw_dev->regmap,
+ AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret)
+ return ret;
+ } else {
+ if (aw_dev->prof_cur != aw_dev->prof_index) {
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret)
+ return ret;
+ } else {
+ ret = 0;
+ }
+ }
+
+ aw_dev->prof_cur = aw_dev->prof_index;
+
+ return ret;
+}
+
+static void aw88261_start_pa(struct aw88261 *aw88261)
+{
+ int ret, i;
+
+ for (i = 0; i < AW88261_START_RETRIES; i++) {
+ ret = aw88261_reg_update(aw88261, aw88261->phase_sync);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "fw update failed, cnt:%d\n", i);
+ continue;
+ }
+ ret = aw88261_dev_start(aw88261);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "aw88261 device start failed. retry = %d", i);
+ continue;
+ } else {
+ dev_info(aw88261->aw_pa->dev, "start success\n");
+ break;
+ }
+ }
+}
+
+static void aw88261_startup_work(struct work_struct *work)
+{
+ struct aw88261 *aw88261 =
+ container_of(work, struct aw88261, start_work.work);
+
+ mutex_lock(&aw88261->lock);
+ aw88261_start_pa(aw88261);
+ mutex_unlock(&aw88261->lock);
+}
+
+static void aw88261_start(struct aw88261 *aw88261, bool sync_start)
+{
+ if (aw88261->aw_pa->fw_status != AW88261_DEV_FW_OK)
+ return;
+
+ if (aw88261->aw_pa->status == AW88261_DEV_PW_ON)
+ return;
+
+ if (sync_start == AW88261_SYNC_START)
+ aw88261_start_pa(aw88261);
+ else
+ queue_delayed_work(system_wq,
+ &aw88261->start_work,
+ AW88261_START_WORK_DELAY_MS);
+}
+
+static struct snd_soc_dai_driver aw88261_dai[] = {
+ {
+ .name = "aw88261-aif",
+ .id = 1,
+ .playback = {
+ .stream_name = "Speaker_Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Speaker_Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = AW88261_RATES,
+ .formats = AW88261_FORMATS,
+ },
+ },
+};
+
+static int aw88261_get_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88261->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_in_time;
+
+ return 0;
+}
+
+static int aw88261_set_fade_in_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_in_time) {
+ aw_dev->fade_in_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88261_get_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct aw_device *aw_dev = aw88261->aw_pa;
+
+ ucontrol->value.integer.value[0] = aw_dev->fade_out_time;
+
+ return 0;
+}
+
+static int aw88261_set_fade_out_time(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int time;
+
+ time = ucontrol->value.integer.value[0];
+ if (time < mc->min || time > mc->max)
+ return -EINVAL;
+
+ if (time != aw_dev->fade_out_time) {
+ aw_dev->fade_out_time = time;
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88261_dev_set_profile_index(struct aw_device *aw_dev, int index)
+{
+ /* check the index whether is valid */
+ if ((index >= aw_dev->prof_info.count) || (index < 0))
+ return -EINVAL;
+ /* check the index whether change */
+ if (aw_dev->prof_index == index)
+ return -EPERM;
+
+ aw_dev->prof_index = index;
+
+ return 0;
+}
+
+static int aw88261_profile_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ const char *prof_name;
+ char *name;
+ int count;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+ uinfo->count = 1;
+
+ count = aw88261->aw_pa->prof_info.count;
+ if (count <= 0) {
+ uinfo->value.enumerated.items = 0;
+ return 0;
+ }
+
+ uinfo->value.enumerated.items = count;
+
+ if (uinfo->value.enumerated.item >= count)
+ uinfo->value.enumerated.item = count - 1;
+
+ name = uinfo->value.enumerated.name;
+ count = uinfo->value.enumerated.item;
+
+ prof_name = aw88261_dev_get_prof_name(aw88261->aw_pa, count);
+ if (!prof_name) {
+ strscpy(uinfo->value.enumerated.name, "null",
+ strlen("null") + 1);
+ return 0;
+ }
+
+ strscpy(name, prof_name, sizeof(uinfo->value.enumerated.name));
+
+ return 0;
+}
+
+static int aw88261_profile_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88261->aw_pa->prof_index;
+
+ return 0;
+}
+
+static int aw88261_profile_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ int ret;
+
+ /* pa stop or stopping just set profile */
+ mutex_lock(&aw88261->lock);
+ ret = aw88261_dev_set_profile_index(aw88261->aw_pa, ucontrol->value.integer.value[0]);
+ if (ret) {
+ dev_dbg(codec->dev, "profile index does not change");
+ mutex_unlock(&aw88261->lock);
+ return 0;
+ }
+
+ if (aw88261->aw_pa->status) {
+ aw88261_dev_stop(aw88261->aw_pa);
+ aw88261_start(aw88261, AW88261_SYNC_START);
+ }
+
+ mutex_unlock(&aw88261->lock);
+
+ return 1;
+}
+
+static int aw88261_volume_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
+
+ ucontrol->value.integer.value[0] = vol_desc->ctl_volume;
+
+ return 0;
+}
+
+static int aw88261_volume_set(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct aw_volume_desc *vol_desc = &aw88261->aw_pa->volume_desc;
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (vol_desc->ctl_volume != value) {
+ vol_desc->ctl_volume = value;
+ aw88261_dev_set_volume(aw88261->aw_pa, vol_desc->ctl_volume);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static int aw88261_get_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = aw88261->aw_pa->fade_step;
+
+ return 0;
+}
+
+static int aw88261_set_fade_step(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *codec = snd_soc_kcontrol_component(kcontrol);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(codec);
+ struct soc_mixer_control *mc =
+ (struct soc_mixer_control *)kcontrol->private_value;
+ int value;
+
+ value = ucontrol->value.integer.value[0];
+ if (value < mc->min || value > mc->max)
+ return -EINVAL;
+
+ if (aw88261->aw_pa->fade_step != value) {
+ aw88261->aw_pa->fade_step = value;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new aw88261_controls[] = {
+ SOC_SINGLE_EXT("PCM Playback Volume", AW88261_SYSCTRL2_REG,
+ 6, AW88261_MUTE_VOL, 0, aw88261_volume_get,
+ aw88261_volume_set),
+ SOC_SINGLE_EXT("Fade Step", 0, 0, AW88261_MUTE_VOL, 0,
+ aw88261_get_fade_step, aw88261_set_fade_step),
+ SOC_SINGLE_EXT("Volume Ramp Up Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88261_get_fade_in_time, aw88261_set_fade_in_time),
+ SOC_SINGLE_EXT("Volume Ramp Down Step", 0, 0, FADE_TIME_MAX, FADE_TIME_MIN,
+ aw88261_get_fade_out_time, aw88261_set_fade_out_time),
+ AW88261_PROFILE_EXT("Profile Set", aw88261_profile_info,
+ aw88261_profile_get, aw88261_profile_set),
+};
+
+static int aw88261_playback_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&aw88261->lock);
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ aw88261_start(aw88261, AW88261_ASYNC_START);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ aw88261_dev_stop(aw88261->aw_pa);
+ break;
+ default:
+ break;
+ }
+ mutex_unlock(&aw88261->lock);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget aw88261_dapm_widgets[] = {
+ /* playback */
+ SND_SOC_DAPM_AIF_IN_E("AIF_RX", "Speaker_Playback", 0, 0, 0, 0,
+ aw88261_playback_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("DAC Output"),
+
+ /* capture */
+ SND_SOC_DAPM_AIF_OUT("AIF_TX", "Speaker_Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_INPUT("ADC Input"),
+};
+
+static const struct snd_soc_dapm_route aw88261_audio_map[] = {
+ {"DAC Output", NULL, "AIF_RX"},
+ {"AIF_TX", NULL, "ADC Input"},
+};
+
+static int aw88261_frcset_check(struct aw88261 *aw88261)
+{
+ unsigned int reg_val;
+ u16 temh, teml, tem;
+ int ret;
+
+ ret = regmap_read(aw88261->regmap, AW88261_EFRH3_REG, &reg_val);
+ if (ret)
+ return ret;
+ temh = ((u16)reg_val & (~AW88261_TEMH_MASK));
+
+ ret = regmap_read(aw88261->regmap, AW88261_EFRL3_REG, &reg_val);
+ if (ret)
+ return ret;
+ teml = ((u16)reg_val & (~AW88261_TEML_MASK));
+
+ if (aw88261->efuse_check == AW88261_EF_OR_CHECK)
+ tem = (temh | teml);
+ else
+ tem = (temh & teml);
+
+ if (tem == AW88261_DEFAULT_CFG)
+ aw88261->frcset_en = AW88261_FRCSET_ENABLE;
+ else
+ aw88261->frcset_en = AW88261_FRCSET_DISABLE;
+
+ dev_dbg(aw88261->aw_pa->dev, "tem is 0x%04x, frcset_en is %d",
+ tem, aw88261->frcset_en);
+
+ return ret;
+}
+
+static int aw88261_dev_init(struct aw88261 *aw88261, struct aw_container *aw_cfg)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ int ret;
+
+ ret = aw88395_dev_cfg_load(aw_dev, aw_cfg);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw_dev acf parse failed");
+ return -EINVAL;
+ }
+
+ ret = regmap_write(aw_dev->regmap, AW88261_ID_REG, AW88261_SOFT_RESET_VALUE);
+ if (ret)
+ return ret;
+
+ aw_dev->fade_in_time = AW88261_500_US;
+ aw_dev->fade_out_time = AW88261_500_US;
+ aw_dev->prof_cur = AW88261_INIT_PROFILE;
+ aw_dev->prof_index = AW88261_INIT_PROFILE;
+
+ ret = aw88261_dev_fw_update(aw88261);
+ if (ret) {
+ dev_err(aw_dev->dev, "fw update failed ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = aw88261_frcset_check(aw88261);
+ if (ret) {
+ dev_err(aw_dev->dev, "aw88261_frcset_check ret = %d\n", ret);
+ return ret;
+ }
+
+ aw88261_dev_clear_int_status(aw_dev);
+
+ aw88261_dev_uls_hmute(aw_dev, true);
+
+ aw88261_dev_mute(aw_dev, true);
+
+ aw88261_dev_i2s_tx_enable(aw_dev, false);
+
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 100);
+
+ aw88261_dev_amppd(aw_dev, true);
+
+ aw88261_dev_pwd(aw_dev, true);
+
+ return 0;
+}
+
+static int aw88261_request_firmware_file(struct aw88261 *aw88261)
+{
+ const struct firmware *cont = NULL;
+ int ret;
+
+ aw88261->aw_pa->fw_status = AW88261_DEV_FW_FAILED;
+
+ ret = request_firmware(&cont, AW88261_ACF_FILE, aw88261->aw_pa->dev);
+ if (ret)
+ return dev_err_probe(aw88261->aw_pa->dev, ret,
+ "load [%s] failed!", AW88261_ACF_FILE);
+
+ dev_info(aw88261->aw_pa->dev, "loaded %s - size: %zu\n",
+ AW88261_ACF_FILE, cont ? cont->size : 0);
+
+ aw88261->aw_cfg = devm_kzalloc(aw88261->aw_pa->dev, cont->size + sizeof(int), GFP_KERNEL);
+ if (!aw88261->aw_cfg) {
+ release_firmware(cont);
+ return -ENOMEM;
+ }
+ aw88261->aw_cfg->len = (int)cont->size;
+ memcpy(aw88261->aw_cfg->data, cont->data, cont->size);
+ release_firmware(cont);
+
+ ret = aw88395_dev_load_acf_check(aw88261->aw_pa, aw88261->aw_cfg);
+ if (ret) {
+ dev_err(aw88261->aw_pa->dev, "load [%s] failed !", AW88261_ACF_FILE);
+ return ret;
+ }
+
+ mutex_lock(&aw88261->lock);
+ /* aw device init */
+ ret = aw88261_dev_init(aw88261, aw88261->aw_cfg);
+ if (ret)
+ dev_err(aw88261->aw_pa->dev, "dev init failed");
+ mutex_unlock(&aw88261->lock);
+
+ return ret;
+}
+
+static int aw88261_codec_probe(struct snd_soc_component *component)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ INIT_DELAYED_WORK(&aw88261->start_work, aw88261_startup_work);
+
+ ret = aw88261_request_firmware_file(aw88261);
+ if (ret)
+ return dev_err_probe(aw88261->aw_pa->dev, ret,
+ "aw88261_request_firmware_file failed\n");
+
+ /* add widgets */
+ ret = snd_soc_dapm_new_controls(dapm, aw88261_dapm_widgets,
+ ARRAY_SIZE(aw88261_dapm_widgets));
+ if (ret)
+ return ret;
+
+ /* add route */
+ ret = snd_soc_dapm_add_routes(dapm, aw88261_audio_map,
+ ARRAY_SIZE(aw88261_audio_map));
+ if (ret)
+ return ret;
+
+ ret = snd_soc_add_component_controls(component, aw88261_controls,
+ ARRAY_SIZE(aw88261_controls));
+
+ return ret;
+}
+
+static void aw88261_codec_remove(struct snd_soc_component *aw_codec)
+{
+ struct aw88261 *aw88261 = snd_soc_component_get_drvdata(aw_codec);
+
+ cancel_delayed_work_sync(&aw88261->start_work);
+}
+
+static const struct snd_soc_component_driver soc_codec_dev_aw88261 = {
+ .probe = aw88261_codec_probe,
+ .remove = aw88261_codec_remove,
+};
+
+static void aw88261_hw_reset(struct aw88261 *aw88261)
+{
+ gpiod_set_value_cansleep(aw88261->reset_gpio, 0);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
+ gpiod_set_value_cansleep(aw88261->reset_gpio, 1);
+ usleep_range(AW88261_1000_US, AW88261_1000_US + 10);
+}
+
+static void aw88261_parse_channel_dt(struct aw88261 *aw88261)
+{
+ struct aw_device *aw_dev = aw88261->aw_pa;
+ struct device_node *np = aw_dev->dev->of_node;
+ u32 channel_value = AW88261_DEV_DEFAULT_CH;
+ u32 sync_enable = false;
+
+ of_property_read_u32(np, "sound-channel", &channel_value);
+ of_property_read_u32(np, "sync-flag", &sync_enable);
+
+ aw_dev->channel = channel_value;
+ aw88261->phase_sync = sync_enable;
+}
+
+static int aw88261_init(struct aw88261 **aw88261, struct i2c_client *i2c, struct regmap *regmap)
+{
+ struct aw_device *aw_dev;
+ unsigned int chip_id;
+ int ret;
+
+ /* read chip id */
+ ret = regmap_read(regmap, AW88261_ID_REG, &chip_id);
+ if (ret) {
+ dev_err(&i2c->dev, "%s read chipid error. ret = %d", __func__, ret);
+ return ret;
+ }
+ if (chip_id != AW88261_CHIP_ID) {
+ dev_err(&i2c->dev, "unsupported device");
+ return -ENXIO;
+ }
+
+ dev_info(&i2c->dev, "chip id = %x\n", chip_id);
+
+ aw_dev = devm_kzalloc(&i2c->dev, sizeof(*aw_dev), GFP_KERNEL);
+ if (!aw_dev)
+ return -ENOMEM;
+
+ (*aw88261)->aw_pa = aw_dev;
+ aw_dev->i2c = i2c;
+ aw_dev->regmap = regmap;
+ aw_dev->dev = &i2c->dev;
+ aw_dev->chip_id = AW88261_CHIP_ID;
+ aw_dev->acf = NULL;
+ aw_dev->prof_info.prof_desc = NULL;
+ aw_dev->prof_info.count = 0;
+ aw_dev->prof_info.prof_type = AW88395_DEV_NONE_TYPE_ID;
+ aw_dev->channel = 0;
+ aw_dev->fw_status = AW88261_DEV_FW_FAILED;
+ aw_dev->fade_step = AW88261_VOLUME_STEP_DB;
+ aw_dev->volume_desc.ctl_volume = AW88261_VOL_DEFAULT_VALUE;
+ aw_dev->volume_desc.mute_volume = AW88261_MUTE_VOL;
+ aw88261_parse_channel_dt(*aw88261);
+
+ return ret;
+}
+
+static int aw88261_i2c_probe(struct i2c_client *i2c)
+{
+ struct aw88261 *aw88261;
+ int ret;
+
+ ret = i2c_check_functionality(i2c->adapter, I2C_FUNC_I2C);
+ if (!ret)
+ return dev_err_probe(&i2c->dev, -ENXIO, "check_functionality failed");
+
+ aw88261 = devm_kzalloc(&i2c->dev, sizeof(*aw88261), GFP_KERNEL);
+ if (!aw88261)
+ return -ENOMEM;
+
+ mutex_init(&aw88261->lock);
+
+ i2c_set_clientdata(i2c, aw88261);
+
+ aw88261->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(aw88261->reset_gpio))
+ dev_info(&i2c->dev, "reset gpio not defined\n");
+ else
+ aw88261_hw_reset(aw88261);
+
+ aw88261->regmap = devm_regmap_init_i2c(i2c, &aw88261_remap_config);
+ if (IS_ERR(aw88261->regmap)) {
+ ret = PTR_ERR(aw88261->regmap);
+ return dev_err_probe(&i2c->dev, ret, "failed to init regmap: %d\n", ret);
+ }
+
+ /* aw pa init */
+ ret = aw88261_init(&aw88261, i2c, aw88261->regmap);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&i2c->dev,
+ &soc_codec_dev_aw88261,
+ aw88261_dai, ARRAY_SIZE(aw88261_dai));
+ if (ret)
+ dev_err(&i2c->dev, "failed to register aw88261: %d", ret);
+
+ return ret;
+}
+
+static const struct i2c_device_id aw88261_i2c_id[] = {
+ { AW88261_I2C_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, aw88261_i2c_id);
+
+static struct i2c_driver aw88261_i2c_driver = {
+ .driver = {
+ .name = AW88261_I2C_NAME,
+ },
+ .probe = aw88261_i2c_probe,
+ .id_table = aw88261_i2c_id,
+};
+module_i2c_driver(aw88261_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC AW88261 Smart PA Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/aw88261.h b/sound/soc/codecs/aw88261.h
new file mode 100644
index 000000000000..4f3dbf438510
--- /dev/null
+++ b/sound/soc/codecs/aw88261.h
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// aw88261.h -- AW88261 ALSA SoC Audio driver
+//
+// Copyright (c) 2023 awinic Technology CO., LTD
+//
+// Author: Jimmy Zhang <zhangjianming@awinic.com>
+// Author: Weidong Wang <wangweidong.a@awinic.com>
+//
+
+#ifndef __AW88261_H__
+#define __AW88261_H__
+
+#define AW88261_ID_REG (0x00)
+#define AW88261_SYSST_REG (0x01)
+#define AW88261_SYSINT_REG (0x02)
+#define AW88261_SYSINTM_REG (0x03)
+#define AW88261_SYSCTRL_REG (0x04)
+#define AW88261_SYSCTRL2_REG (0x05)
+#define AW88261_I2SCTRL1_REG (0x06)
+#define AW88261_I2SCTRL2_REG (0x07)
+#define AW88261_I2SCTRL3_REG (0x08)
+#define AW88261_DACCFG1_REG (0x09)
+#define AW88261_DACCFG2_REG (0x0A)
+#define AW88261_DACCFG3_REG (0x0B)
+#define AW88261_DACCFG4_REG (0x0C)
+#define AW88261_DACCFG5_REG (0x0D)
+#define AW88261_DACCFG6_REG (0x0E)
+#define AW88261_DACCFG7_REG (0x0F)
+#define AW88261_DACCFG8_REG (0x10)
+#define AW88261_PWMCTRL1_REG (0x11)
+#define AW88261_PWMCTRL2_REG (0x12)
+#define AW88261_I2SCFG1_REG (0x13)
+#define AW88261_DBGCTRL_REG (0x14)
+#define AW88261_DACCFG9_REG (0x15)
+#define AW88261_DACCFG10_REG (0x16)
+#define AW88261_DACST_REG (0x20)
+#define AW88261_VBAT_REG (0x21)
+#define AW88261_TEMP_REG (0x22)
+#define AW88261_PVDD_REG (0x23)
+#define AW88261_ISNDAT_REG (0x24)
+#define AW88261_VSNDAT_REG (0x25)
+#define AW88261_I2SINT_REG (0x26)
+#define AW88261_I2SCAPCNT_REG (0x27)
+#define AW88261_ANASTA1_REG (0x28)
+#define AW88261_ANASTA2_REG (0x29)
+#define AW88261_ANASTA3_REG (0x2A)
+#define AW88261_TESTDET_REG (0x2B)
+#define AW88261_DSMCFG1_REG (0x30)
+#define AW88261_DSMCFG2_REG (0x31)
+#define AW88261_DSMCFG3_REG (0x32)
+#define AW88261_DSMCFG4_REG (0x33)
+#define AW88261_DSMCFG5_REG (0x34)
+#define AW88261_DSMCFG6_REG (0x35)
+#define AW88261_DSMCFG7_REG (0x36)
+#define AW88261_DSMCFG8_REG (0x37)
+#define AW88261_TESTIN_REG (0x38)
+#define AW88261_TESTOUT_REG (0x39)
+#define AW88261_SADCCTRL1_REG (0x3A)
+#define AW88261_SADCCTRL2_REG (0x3B)
+#define AW88261_SADCCTRL3_REG (0x3C)
+#define AW88261_SADCCTRL4_REG (0x3D)
+#define AW88261_SADCCTRL5_REG (0x3E)
+#define AW88261_SADCCTRL6_REG (0x3F)
+#define AW88261_SADCCTRL7_REG (0x40)
+#define AW88261_VSNTM1_REG (0x50)
+#define AW88261_VSNTM2_REG (0x51)
+#define AW88261_ISNCTRL1_REG (0x52)
+#define AW88261_ISNCTRL2_REG (0x53)
+#define AW88261_PLLCTRL1_REG (0x54)
+#define AW88261_PLLCTRL2_REG (0x55)
+#define AW88261_PLLCTRL3_REG (0x56)
+#define AW88261_CDACTRL1_REG (0x57)
+#define AW88261_CDACTRL2_REG (0x58)
+#define AW88261_DITHERCFG1_REG (0x59)
+#define AW88261_DITHERCFG2_REG (0x5A)
+#define AW88261_DITHERCFG3_REG (0x5B)
+#define AW88261_CPCTRL_REG (0x5C)
+#define AW88261_BSTCTRL1_REG (0x60)
+#define AW88261_BSTCTRL2_REG (0x61)
+#define AW88261_BSTCTRL3_REG (0x62)
+#define AW88261_BSTCTRL4_REG (0x63)
+#define AW88261_BSTCTRL5_REG (0x64)
+#define AW88261_BSTCTRL6_REG (0x65)
+#define AW88261_BSTCTRL7_REG (0x66)
+#define AW88261_BSTCTRL8_REG (0x67)
+#define AW88261_BSTCTRL9_REG (0x68)
+#define AW88261_TM_REG (0x6F)
+#define AW88261_TESTCTRL1_REG (0x70)
+#define AW88261_TESTCTRL2_REG (0x71)
+#define AW88261_EFCTRL1_REG (0x72)
+#define AW88261_EFCTRL2_REG (0x73)
+#define AW88261_EFWH_REG (0x74)
+#define AW88261_EFWM2_REG (0x75)
+#define AW88261_EFWM1_REG (0x76)
+#define AW88261_EFWL_REG (0x77)
+#define AW88261_EFRH4_REG (0x78)
+#define AW88261_EFRH3_REG (0x79)
+#define AW88261_EFRH2_REG (0x7A)
+#define AW88261_EFRH1_REG (0x7B)
+#define AW88261_EFRL4_REG (0x7C)
+#define AW88261_EFRL3_REG (0x7D)
+#define AW88261_EFRL2_REG (0x7E)
+#define AW88261_EFRL1_REG (0x7F)
+
+#define AW88261_REG_MAX (0x80)
+#define AW88261_EF_DBMD_MASK (0xfff7)
+#define AW88261_OR_VALUE (0x0008)
+
+#define AW88261_TEMH_MASK (0x83ff)
+#define AW88261_TEML_MASK (0x83ff)
+#define AW88261_DEFAULT_CFG (0x0000)
+
+#define AW88261_ICALK_SHIFT (0)
+#define AW88261_ICALKL_SHIFT (0)
+#define AW88261_VCALK_SHIFT (0)
+#define AW88261_VCALKL_SHIFT (0)
+
+#define AW88261_AMPPD_START_BIT (1)
+#define AW88261_AMPPD_BITS_LEN (1)
+#define AW88261_AMPPD_MASK \
+ (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_UVLS_START_BIT (14)
+#define AW88261_UVLS_NORMAL (0)
+#define AW88261_UVLS_NORMAL_VALUE \
+ (AW88261_UVLS_NORMAL << AW88261_UVLS_START_BIT)
+
+#define AW88261_BSTOCS_START_BIT (11)
+#define AW88261_BSTOCS_OVER_CURRENT (1)
+#define AW88261_BSTOCS_OVER_CURRENT_VALUE \
+ (AW88261_BSTOCS_OVER_CURRENT << AW88261_BSTOCS_START_BIT)
+
+#define AW88261_BSTS_START_BIT (9)
+#define AW88261_BSTS_FINISHED (1)
+#define AW88261_BSTS_FINISHED_VALUE \
+ (AW88261_BSTS_FINISHED << AW88261_BSTS_START_BIT)
+
+#define AW88261_SWS_START_BIT (8)
+#define AW88261_SWS_SWITCHING (1)
+#define AW88261_SWS_SWITCHING_VALUE \
+ (AW88261_SWS_SWITCHING << AW88261_SWS_START_BIT)
+
+#define AW88261_NOCLKS_START_BIT (5)
+#define AW88261_NOCLKS_NO_CLOCK (1)
+#define AW88261_NOCLKS_NO_CLOCK_VALUE \
+ (AW88261_NOCLKS_NO_CLOCK << AW88261_NOCLKS_START_BIT)
+
+#define AW88261_CLKS_START_BIT (4)
+#define AW88261_CLKS_STABLE (1)
+#define AW88261_CLKS_STABLE_VALUE \
+ (AW88261_CLKS_STABLE << AW88261_CLKS_START_BIT)
+
+#define AW88261_OCDS_START_BIT (3)
+#define AW88261_OCDS_OC (1)
+#define AW88261_OCDS_OC_VALUE \
+ (AW88261_OCDS_OC << AW88261_OCDS_START_BIT)
+
+#define AW88261_OTHS_START_BIT (1)
+#define AW88261_OTHS_OT (1)
+#define AW88261_OTHS_OT_VALUE \
+ (AW88261_OTHS_OT << AW88261_OTHS_START_BIT)
+
+#define AW88261_PLLS_START_BIT (0)
+#define AW88261_PLLS_LOCKED (1)
+#define AW88261_PLLS_LOCKED_VALUE \
+ (AW88261_PLLS_LOCKED << AW88261_PLLS_START_BIT)
+
+#define AW88261_BIT_PLL_CHECK \
+ (AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_BIT_SYSST_CHECK_MASK \
+ (~(AW88261_UVLS_NORMAL_VALUE | \
+ AW88261_BSTOCS_OVER_CURRENT_VALUE | \
+ AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_NOCLKS_NO_CLOCK_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_OCDS_OC_VALUE | \
+ AW88261_OTHS_OT_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE))
+
+#define AW88261_BIT_SYSST_CHECK \
+ (AW88261_BSTS_FINISHED_VALUE | \
+ AW88261_SWS_SWITCHING_VALUE | \
+ AW88261_CLKS_STABLE_VALUE | \
+ AW88261_PLLS_LOCKED_VALUE)
+
+#define AW88261_ULS_HMUTE_START_BIT (14)
+#define AW88261_ULS_HMUTE_BITS_LEN (1)
+#define AW88261_ULS_HMUTE_MASK \
+ (~(((1<<AW88261_ULS_HMUTE_BITS_LEN)-1) << AW88261_ULS_HMUTE_START_BIT))
+
+#define AW88261_ULS_HMUTE_DISABLE (0)
+#define AW88261_ULS_HMUTE_DISABLE_VALUE \
+ (AW88261_ULS_HMUTE_DISABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_ULS_HMUTE_ENABLE (1)
+#define AW88261_ULS_HMUTE_ENABLE_VALUE \
+ (AW88261_ULS_HMUTE_ENABLE << AW88261_ULS_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_START_BIT (8)
+#define AW88261_HMUTE_BITS_LEN (1)
+#define AW88261_HMUTE_MASK \
+ (~(((1<<AW88261_HMUTE_BITS_LEN)-1) << AW88261_HMUTE_START_BIT))
+
+#define AW88261_HMUTE_DISABLE (0)
+#define AW88261_HMUTE_DISABLE_VALUE \
+ (AW88261_HMUTE_DISABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_HMUTE_ENABLE (1)
+#define AW88261_HMUTE_ENABLE_VALUE \
+ (AW88261_HMUTE_ENABLE << AW88261_HMUTE_START_BIT)
+
+#define AW88261_AMPPD_START_BIT (1)
+#define AW88261_AMPPD_BITS_LEN (1)
+#define AW88261_AMPPD_MASK \
+ (~(((1<<AW88261_AMPPD_BITS_LEN)-1) << AW88261_AMPPD_START_BIT))
+
+#define AW88261_AMPPD_WORKING (0)
+#define AW88261_AMPPD_WORKING_VALUE \
+ (AW88261_AMPPD_WORKING << AW88261_AMPPD_START_BIT)
+
+#define AW88261_AMPPD_POWER_DOWN (1)
+#define AW88261_AMPPD_POWER_DOWN_VALUE \
+ (AW88261_AMPPD_POWER_DOWN << AW88261_AMPPD_START_BIT)
+
+#define AW88261_PWDN_START_BIT (0)
+#define AW88261_PWDN_BITS_LEN (1)
+#define AW88261_PWDN_MASK \
+ (~(((1<<AW88261_PWDN_BITS_LEN)-1) << AW88261_PWDN_START_BIT))
+
+#define AW88261_PWDN_WORKING (0)
+#define AW88261_PWDN_WORKING_VALUE \
+ (AW88261_PWDN_WORKING << AW88261_PWDN_START_BIT)
+
+#define AW88261_PWDN_POWER_DOWN (1)
+#define AW88261_PWDN_POWER_DOWN_VALUE \
+ (AW88261_PWDN_POWER_DOWN << AW88261_PWDN_START_BIT)
+
+#define AW88261_MUTE_VOL (90 * 8)
+#define AW88261_VOLUME_STEP_DB (6 * 8)
+
+#define AW88261_VOL_6DB_START (6)
+
+#define AW88261_VOL_START_BIT (0)
+#define AW88261_VOL_BITS_LEN (10)
+#define AW88261_VOL_MASK \
+ (~(((1<<AW88261_VOL_BITS_LEN)-1) << AW88261_VOL_START_BIT))
+
+#define AW88261_VOL_DEFAULT_VALUE (0)
+
+#define AW88261_I2STXEN_START_BIT (6)
+#define AW88261_I2STXEN_BITS_LEN (1)
+#define AW88261_I2STXEN_MASK \
+ (~(((1<<AW88261_I2STXEN_BITS_LEN)-1) << AW88261_I2STXEN_START_BIT))
+
+#define AW88261_I2STXEN_DISABLE (0)
+#define AW88261_I2STXEN_DISABLE_VALUE \
+ (AW88261_I2STXEN_DISABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_I2STXEN_ENABLE (1)
+#define AW88261_I2STXEN_ENABLE_VALUE \
+ (AW88261_I2STXEN_ENABLE << AW88261_I2STXEN_START_BIT)
+
+#define AW88261_CCO_MUX_START_BIT (14)
+#define AW88261_CCO_MUX_BITS_LEN (1)
+#define AW88261_CCO_MUX_MASK \
+ (~(((1<<AW88261_CCO_MUX_BITS_LEN)-1) << AW88261_CCO_MUX_START_BIT))
+
+#define AW88261_CCO_MUX_DIVIDED (0)
+#define AW88261_CCO_MUX_DIVIDED_VALUE \
+ (AW88261_CCO_MUX_DIVIDED << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_CCO_MUX_BYPASS (1)
+#define AW88261_CCO_MUX_BYPASS_VALUE \
+ (AW88261_CCO_MUX_BYPASS << AW88261_CCO_MUX_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_H_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_H_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_H_START_BIT))
+
+#define AW88261_EF_VSN_GESLP_L_START_BIT (0)
+#define AW88261_EF_VSN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_VSN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_VSN_GESLP_L_BITS_LEN)-1) << AW88261_EF_VSN_GESLP_L_START_BIT))
+
+#define AW88261_FORCE_PWM_START_BIT (12)
+#define AW88261_FORCE_PWM_BITS_LEN (1)
+#define AW88261_FORCE_PWM_MASK \
+ (~(((1<<AW88261_FORCE_PWM_BITS_LEN)-1) << AW88261_FORCE_PWM_START_BIT))
+
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM (1)
+#define AW88261_FORCE_PWM_FORCEMINUS_PWM_VALUE \
+ (AW88261_FORCE_PWM_FORCEMINUS_PWM << AW88261_FORCE_PWM_START_BIT)
+
+#define AW88261_BST_OS_WIDTH_START_BIT (0)
+#define AW88261_BST_OS_WIDTH_BITS_LEN (3)
+#define AW88261_BST_OS_WIDTH_MASK \
+ (~(((1<<AW88261_BST_OS_WIDTH_BITS_LEN)-1) << AW88261_BST_OS_WIDTH_START_BIT))
+
+#define AW88261_BST_OS_WIDTH_50NS (4)
+#define AW88261_BST_OS_WIDTH_50NS_VALUE \
+ (AW88261_BST_OS_WIDTH_50NS << AW88261_BST_OS_WIDTH_START_BIT)
+
+/* BST_LOOPR bit 1:0 (BSTCTRL6 0x65) */
+#define AW88261_BST_LOOPR_START_BIT (0)
+#define AW88261_BST_LOOPR_BITS_LEN (2)
+#define AW88261_BST_LOOPR_MASK \
+ (~(((1<<AW88261_BST_LOOPR_BITS_LEN)-1) << AW88261_BST_LOOPR_START_BIT))
+
+#define AW88261_BST_LOOPR_340K (2)
+#define AW88261_BST_LOOPR_340K_VALUE \
+ (AW88261_BST_LOOPR_340K << AW88261_BST_LOOPR_START_BIT)
+
+/* RSQN_DLY bit 15:14 (BSTCTRL7 0x66) */
+#define AW88261_RSQN_DLY_START_BIT (14)
+#define AW88261_RSQN_DLY_BITS_LEN (2)
+#define AW88261_RSQN_DLY_MASK \
+ (~(((1<<AW88261_RSQN_DLY_BITS_LEN)-1) << AW88261_RSQN_DLY_START_BIT))
+
+#define AW88261_RSQN_DLY_35NS (2)
+#define AW88261_RSQN_DLY_35NS_VALUE \
+ (AW88261_RSQN_DLY_35NS << AW88261_RSQN_DLY_START_BIT)
+
+/* BURST_SSMODE bit 3 (BSTCTRL8 0x67) */
+#define AW88261_BURST_SSMODE_START_BIT (3)
+#define AW88261_BURST_SSMODE_BITS_LEN (1)
+#define AW88261_BURST_SSMODE_MASK \
+ (~(((1<<AW88261_BURST_SSMODE_BITS_LEN)-1) << AW88261_BURST_SSMODE_START_BIT))
+
+#define AW88261_BURST_SSMODE_FAST (0)
+#define AW88261_BURST_SSMODE_FAST_VALUE \
+ (AW88261_BURST_SSMODE_FAST << AW88261_BURST_SSMODE_START_BIT)
+
+/* BST_BURST bit 9:7 (BSTCTRL9 0x68) */
+#define AW88261_BST_BURST_START_BIT (7)
+#define AW88261_BST_BURST_BITS_LEN (3)
+#define AW88261_BST_BURST_MASK \
+ (~(((1<<AW88261_BST_BURST_BITS_LEN)-1) << AW88261_BST_BURST_START_BIT))
+
+#define AW88261_BST_BURST_30MA (2)
+#define AW88261_BST_BURST_30MA_VALUE \
+ (AW88261_BST_BURST_30MA << AW88261_BST_BURST_START_BIT)
+
+#define AW88261_EF_VSN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_VSN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_SIGN_MASK (~0x0200)
+#define AW88261_EF_ISN_GESLP_NEG (~0xfc00)
+
+#define AW88261_EF_ISN_GESLP_H_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_H_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_H_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_H_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_H_START_BIT))
+
+#define AW88261_EF_ISN_GESLP_L_START_BIT (0)
+#define AW88261_EF_ISN_GESLP_L_BITS_LEN (10)
+#define AW88261_EF_ISN_GESLP_L_MASK \
+ (~(((1<<AW88261_EF_ISN_GESLP_L_BITS_LEN)-1) << AW88261_EF_ISN_GESLP_L_START_BIT))
+
+#define AW88261_CABL_BASE_VALUE (1000)
+#define AW88261_ICABLK_FACTOR (1)
+#define AW88261_VCABLK_FACTOR (1)
+
+#define AW88261_VCAL_FACTOR (1<<13)
+
+#define AW88261_START_RETRIES (5)
+#define AW88261_START_WORK_DELAY_MS (0)
+
+#define AW88261_I2C_NAME "aw88261_smartpa"
+
+#define AW88261_RATES (SNDRV_PCM_RATE_8000_48000 | \
+ SNDRV_PCM_RATE_96000)
+#define AW88261_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#define FADE_TIME_MAX 100000
+#define FADE_TIME_MIN 0
+
+#define AW88261_DEV_DEFAULT_CH (0)
+#define AW88261_ACF_FILE "aw88261_acf.bin"
+#define AW88261_DEV_SYSST_CHECK_MAX (10)
+#define AW88261_SOFT_RESET_VALUE (0x55aa)
+#define AW88261_REG_TO_DB (0x3f)
+#define AW88261_VOL_START_MASK (0xfc00)
+#define AW88261_INIT_PROFILE (0)
+
+#define REG_VAL_TO_DB(value) ((((value) >> AW88261_VOL_6DB_START) * \
+ AW88261_VOLUME_STEP_DB) + \
+ ((value) & AW88261_REG_TO_DB))
+#define DB_TO_REG_VAL(value) ((((value) / AW88261_VOLUME_STEP_DB) << \
+ AW88261_VOL_6DB_START) + \
+ ((value) % AW88261_VOLUME_STEP_DB))
+
+#define AW88261_PROFILE_EXT(xname, profile_info, profile_get, profile_set) \
+{ \
+ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+ .name = xname, \
+ .info = profile_info, \
+ .get = profile_get, \
+ .put = profile_set, \
+}
+
+enum {
+ AW88261_SYNC_START = 0,
+ AW88261_ASYNC_START,
+};
+
+enum aw88261_id {
+ AW88261_CHIP_ID = 0x2113,
+};
+
+enum {
+ AW88261_500_US = 500,
+ AW88261_1000_US = 1000,
+ AW88261_2000_US = 2000,
+};
+
+enum {
+ AW88261_DEV_PW_OFF = 0,
+ AW88261_DEV_PW_ON,
+};
+
+enum {
+ AW88261_DEV_FW_FAILED = 0,
+ AW88261_DEV_FW_OK,
+};
+
+enum {
+ AW88261_EF_AND_CHECK = 0,
+ AW88261_EF_OR_CHECK,
+};
+
+enum {
+ AW88261_FRCSET_DISABLE = 0,
+ AW88261_FRCSET_ENABLE,
+};
+
+struct aw88261 {
+ struct aw_device *aw_pa;
+ struct mutex lock;
+ struct gpio_desc *reset_gpio;
+ struct delayed_work start_work;
+ struct regmap *regmap;
+ struct aw_container *aw_cfg;
+
+ int efuse_check;
+ int frcset_en;
+ unsigned int mute_st;
+ unsigned int amppd_st;
+
+ unsigned char phase_sync;
+};
+
+#endif
diff --git a/sound/soc/codecs/aw88395/aw88395_lib.c b/sound/soc/codecs/aw88395/aw88395_lib.c
index 05bcf49da857..8ee1baa03269 100644
--- a/sound/soc/codecs/aw88395/aw88395_lib.c
+++ b/sound/soc/codecs/aw88395/aw88395_lib.c
@@ -11,6 +11,7 @@
#include <linux/i2c.h>
#include "aw88395_lib.h"
#include "aw88395_device.h"
+#include "aw88395_reg.h"
#define AW88395_CRC8_POLYNOMIAL 0x8C
DECLARE_CRC8_TABLE(aw_crc8_table);
@@ -429,6 +430,53 @@ parse_bin_failed:
return ret;
}
+static int aw_dev_parse_reg_bin_with_hdr(struct aw_device *aw_dev,
+ uint8_t *data, uint32_t data_len, struct aw_prof_desc *prof_desc)
+{
+ struct aw_bin *aw_bin;
+ int ret;
+
+ aw_bin = devm_kzalloc(aw_dev->dev, data_len + sizeof(*aw_bin), GFP_KERNEL);
+ if (!aw_bin)
+ return -ENOMEM;
+
+ aw_bin->info.len = data_len;
+ memcpy(aw_bin->info.data, data, data_len);
+
+ ret = aw_parsing_bin_file(aw_dev, aw_bin);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse bin failed");
+ goto parse_bin_failed;
+ }
+
+ if ((aw_bin->all_bin_parse_num != 1) ||
+ (aw_bin->header_info[0].bin_data_type != DATA_TYPE_REGISTER)) {
+ dev_err(aw_dev->dev, "bin num or type error");
+ goto parse_bin_failed;
+ }
+
+ if (aw_bin->header_info[0].valid_data_len % 4) {
+ dev_err(aw_dev->dev, "bin data len get error!");
+ goto parse_bin_failed;
+ }
+
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].data =
+ data + aw_bin->header_info[0].valid_data_addr;
+ prof_desc->sec_desc[AW88395_DATA_TYPE_REG].len =
+ aw_bin->header_info[0].valid_data_len;
+ prof_desc->prof_st = AW88395_PROFILE_OK;
+
+ devm_kfree(aw_dev->dev, aw_bin);
+ aw_bin = NULL;
+
+ return 0;
+
+parse_bin_failed:
+ devm_kfree(aw_dev->dev, aw_bin);
+ aw_bin = NULL;
+ return ret;
+}
+
static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg_hdr *cfg_hdr,
struct aw_cfg_dde *cfg_dde, struct aw_prof_desc *scene_prof_desc)
{
@@ -447,6 +495,9 @@ static int aw_dev_parse_data_by_sec_type(struct aw_device *aw_dev, struct aw_cfg
return aw_dev_prof_parse_multi_bin(
aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset,
cfg_dde->data_size, scene_prof_desc);
+ case ACF_SEC_TYPE_HDR_REG:
+ return aw_dev_parse_reg_bin_with_hdr(aw_dev, (u8 *)cfg_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, scene_prof_desc);
default:
dev_err(aw_dev->dev, "%s cfg_dde->data_type = %d\n", __func__, cfg_dde->data_type);
break;
@@ -527,7 +578,49 @@ static int aw_dev_parse_dev_default_type(struct aw_device *aw_dev,
return 0;
}
-static int aw_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
+static int aw88261_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
+ struct aw_all_prof_info all_prof_info)
+{
+ struct aw_prof_desc *prof_desc = all_prof_info.prof_desc;
+ struct aw_prof_info *prof_info = &aw_dev->prof_info;
+ int num = 0;
+ int i;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK)
+ prof_info->count++;
+ }
+
+ dev_dbg(aw_dev->dev, "get valid profile:%d", aw_dev->prof_info.count);
+
+ if (!prof_info->count) {
+ dev_err(aw_dev->dev, "no profile data");
+ return -EPERM;
+ }
+
+ prof_info->prof_desc = devm_kcalloc(aw_dev->dev,
+ prof_info->count, sizeof(struct aw_prof_desc),
+ GFP_KERNEL);
+ if (!prof_info->prof_desc)
+ return -ENOMEM;
+
+ for (i = 0; i < AW88395_PROFILE_MAX; i++) {
+ if (prof_desc[i].prof_st == AW88395_PROFILE_OK) {
+ if (num >= prof_info->count) {
+ dev_err(aw_dev->dev, "overflow count[%d]",
+ prof_info->count);
+ return -EINVAL;
+ }
+ prof_info->prof_desc[num] = prof_desc[i];
+ prof_info->prof_desc[num].id = i;
+ num++;
+ }
+ }
+
+ return 0;
+}
+
+static int aw88395_dev_cfg_get_valid_prof(struct aw_device *aw_dev,
struct aw_all_prof_info all_prof_info)
{
struct aw_prof_desc *prof_desc = all_prof_info.prof_desc;
@@ -606,9 +699,22 @@ static int aw_dev_load_cfg_by_hdr(struct aw_device *aw_dev,
goto exit;
}
- ret = aw_dev_cfg_get_valid_prof(aw_dev, *all_prof_info);
- if (ret < 0)
- goto exit;
+ switch (aw_dev->chip_id) {
+ case AW88395_CHIP_ID:
+ ret = aw88395_dev_cfg_get_valid_prof(aw_dev, *all_prof_info);
+ if (ret < 0)
+ goto exit;
+ break;
+ case AW88261_CHIP_ID:
+ ret = aw88261_dev_cfg_get_valid_prof(aw_dev, *all_prof_info);
+ if (ret < 0)
+ goto exit;
+ break;
+ default:
+ dev_err(aw_dev->dev, "valid prof unsupported");
+ ret = -EINVAL;
+ break;
+ }
aw_dev->prof_info.prof_name_list = profile_name;
@@ -679,16 +785,37 @@ static int aw_get_dev_scene_count_v1(struct aw_device *aw_dev, struct aw_contain
struct aw_cfg_dde_v1 *cfg_dde =
(struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
unsigned int i;
+ int ret;
- for (i = 0; i < cfg_hdr->ddt_num; ++i) {
- if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
- (aw_dev->chip_id == cfg_dde[i].chip_id) &&
- (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
- (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
- (*scene_num)++;
+ switch (aw_dev->chip_id) {
+ case AW88395_CHIP_ID:
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
+ (*scene_num)++;
+ }
+ ret = 0;
+ break;
+ case AW88261_CHIP_ID:
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->i2c->adapter->nr == cfg_dde[i].dev_bus) &&
+ (aw_dev->i2c->addr == cfg_dde[i].dev_addr))
+ (*scene_num)++;
+ }
+ ret = 0;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported device");
+ ret = -EINVAL;
+ break;
}
- return 0;
+ return ret;
}
static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
@@ -699,15 +826,35 @@ static int aw_get_default_scene_count_v1(struct aw_device *aw_dev,
struct aw_cfg_dde_v1 *cfg_dde =
(struct aw_cfg_dde_v1 *)(aw_cfg->data + cfg_hdr->hdr_offset);
unsigned int i;
+ int ret;
- for (i = 0; i < cfg_hdr->ddt_num; ++i) {
- if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
- (aw_dev->chip_id == cfg_dde[i].chip_id) &&
- (aw_dev->channel == cfg_dde[i].dev_index))
- (*scene_num)++;
+ switch (aw_dev->chip_id) {
+ case AW88395_CHIP_ID:
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if ((cfg_dde[i].data_type == ACF_SEC_TYPE_MULTIPLE_BIN) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->channel == cfg_dde[i].dev_index))
+ (*scene_num)++;
+ }
+ ret = 0;
+ break;
+ case AW88261_CHIP_ID:
+ for (i = 0; i < cfg_hdr->ddt_num; ++i) {
+ if (((cfg_dde[i].data_type == ACF_SEC_TYPE_REG) ||
+ (cfg_dde[i].data_type == ACF_SEC_TYPE_HDR_REG)) &&
+ (aw_dev->chip_id == cfg_dde[i].chip_id) &&
+ (aw_dev->channel == cfg_dde[i].dev_index))
+ (*scene_num)++;
+ }
+ ret = 0;
+ break;
+ default:
+ dev_err(aw_dev->dev, "unsupported device");
+ ret = -EINVAL;
+ break;
}
- return 0;
+ return ret;
}
static int aw_dev_parse_scene_count_v1(struct aw_device *aw_dev,
@@ -756,6 +903,18 @@ static int aw_dev_parse_data_by_sec_type_v1(struct aw_device *aw_dev,
prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
(*cur_scene_id)++;
break;
+ case ACF_SEC_TYPE_HDR_REG:
+ ret = aw_dev_parse_reg_bin_with_hdr(aw_dev,
+ (uint8_t *)prof_hdr + cfg_dde->data_offset,
+ cfg_dde->data_size, &prof_info->prof_desc[*cur_scene_id]);
+ if (ret < 0) {
+ dev_err(aw_dev->dev, "parse reg bin with hdr failed");
+ return ret;
+ }
+ prof_info->prof_desc[*cur_scene_id].prf_str = cfg_dde->dev_profile_str;
+ prof_info->prof_desc[*cur_scene_id].id = cfg_dde->dev_profile;
+ (*cur_scene_id)++;
+ break;
default:
dev_err(aw_dev->dev, "unsupported SEC_TYPE [%d]", cfg_dde->data_type);
return -EINVAL;
diff --git a/sound/soc/codecs/aw88395/aw88395_reg.h b/sound/soc/codecs/aw88395/aw88395_reg.h
index e64f24e97150..e7a7c02efaf3 100644
--- a/sound/soc/codecs/aw88395/aw88395_reg.h
+++ b/sound/soc/codecs/aw88395/aw88395_reg.h
@@ -96,6 +96,7 @@
enum aw88395_id {
AW88395_CHIP_ID = 0x2049,
+ AW88261_CHIP_ID = 0x2113,
};
#define AW88395_REG_MAX (0x7D)
diff --git a/sound/soc/codecs/cs35l56.c b/sound/soc/codecs/cs35l56.c
index ea3a4557619a..600b79c62ec4 100644
--- a/sound/soc/codecs/cs35l56.c
+++ b/sound/soc/codecs/cs35l56.c
@@ -357,22 +357,11 @@ static int cs35l56_asp_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int f
return 0;
}
-static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56,
- unsigned int reg, unsigned long mask)
+static unsigned int cs35l56_make_tdm_config_word(unsigned int reg_val, unsigned long mask)
{
- unsigned int reg_val, channel_shift;
+ unsigned int 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) {
@@ -381,7 +370,7 @@ static void cs35l56_set_asp_slot_positions(struct cs35l56_private *cs35l56,
channel_shift += 8;
}
- regmap_write(cs35l56->base.regmap, reg, reg_val);
+ return reg_val;
}
static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
@@ -417,8 +406,11 @@ static int cs35l56_asp_dai_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx
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);
+ /* Default unused slots to 63 */
+ regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL1,
+ cs35l56_make_tdm_config_word(0x3f3f3f3f, rx_mask));
+ regmap_write(cs35l56->base.regmap, CS35L56_ASP1_FRAME_CONTROL5,
+ cs35l56_make_tdm_config_word(0x3f3f3f, tx_mask));
dev_dbg(cs35l56->base.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);
@@ -663,7 +655,7 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
int ret;
/* Use wm_adsp to load and apply the firmware patch and coefficient files */
- ret = wm_adsp_power_up(&cs35l56->dsp);
+ ret = wm_adsp_power_up(&cs35l56->dsp, true);
if (ret)
dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
else
@@ -672,8 +664,17 @@ static void cs35l56_secure_patch(struct cs35l56_private *cs35l56)
static void cs35l56_patch(struct cs35l56_private *cs35l56)
{
+ unsigned int firmware_missing;
int ret;
+ ret = regmap_read(cs35l56->base.regmap, CS35L56_PROTECTION_STATUS, &firmware_missing);
+ if (ret) {
+ dev_err(cs35l56->base.dev, "Failed to read PROTECTION_STATUS: %d\n", ret);
+ return;
+ }
+
+ firmware_missing &= CS35L56_FIRMWARE_MISSING;
+
/*
* Disable SoundWire interrupts to prevent race with IRQ work.
* Setting sdw_irq_no_unmask prevents the handler re-enabling
@@ -692,8 +693,12 @@ static void cs35l56_patch(struct cs35l56_private *cs35l56)
if (ret)
goto err;
- /* Use wm_adsp to load and apply the firmware patch and coefficient files */
- ret = wm_adsp_power_up(&cs35l56->dsp);
+ /*
+ * Use wm_adsp to load and apply the firmware patch and coefficient files,
+ * but only if firmware is missing. If firmware is already patched just
+ * power-up wm_adsp without downloading firmware.
+ */
+ ret = wm_adsp_power_up(&cs35l56->dsp, !!firmware_missing);
if (ret) {
dev_dbg(cs35l56->base.dev, "%s: wm_adsp_power_up ret %d\n", __func__, ret);
goto err;
@@ -959,6 +964,12 @@ int cs35l56_system_resume(struct device *dev)
dev_dbg(dev, "system_resume\n");
+ /*
+ * We might have done a hard reset or the CS35L56 was power-cycled
+ * so wait for control port to be ready.
+ */
+ cs35l56_wait_control_port_ready();
+
/* Undo pm_runtime_force_suspend() before re-enabling the irq */
ret = pm_runtime_force_resume(dev);
if (cs35l56->base.irq)
@@ -977,6 +988,7 @@ int cs35l56_system_resume(struct device *dev)
return ret;
cs35l56->base.fw_patched = false;
+ wm_adsp_power_down(&cs35l56->dsp);
queue_work(cs35l56->dsp_wq, &cs35l56->dsp_work);
/*
@@ -1072,6 +1084,8 @@ int cs35l56_common_probe(struct cs35l56_private *cs35l56)
return dev_err_probe(cs35l56->base.dev, ret, "Failed to enable supplies\n");
if (cs35l56->base.reset_gpio) {
+ /* ACPI can override GPIOD_OUT_LOW flag so force it to start low */
+ gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 0);
cs35l56_wait_min_reset_pulse();
gpiod_set_value_cansleep(cs35l56->base.reset_gpio, 1);
}
diff --git a/sound/soc/codecs/cs42l43-jack.c b/sound/soc/codecs/cs42l43-jack.c
new file mode 100644
index 000000000000..92e37bc1df9d
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-jack.c
@@ -0,0 +1,946 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver jack handling
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/build_bug.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <sound/control.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc.h>
+
+#include "cs42l43.h"
+
+static const unsigned int cs42l43_accdet_us[] = {
+ 20, 100, 1000, 10000, 50000, 75000, 100000, 200000
+};
+
+static const unsigned int cs42l43_accdet_db_ms[] = {
+ 0, 125, 250, 500, 750, 1000, 1250, 1500
+};
+
+static const unsigned int cs42l43_accdet_ramp_ms[] = { 10, 40, 90, 170 };
+
+static const unsigned int cs42l43_accdet_bias_sense[] = {
+ 14, 23, 41, 50, 60, 68, 86, 95, 0,
+};
+
+static int cs42l43_find_index(struct cs42l43_codec *priv, const char * const prop,
+ unsigned int defval, unsigned int *val,
+ const unsigned int *values, const int nvalues)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ int i, ret;
+
+ ret = device_property_read_u32(cs42l43->dev, prop, &defval);
+ if (ret != -EINVAL && ret < 0) {
+ dev_err(priv->dev, "Property %s malformed: %d\n", prop, ret);
+ return ret;
+ }
+
+ if (val)
+ *val = defval;
+
+ for (i = 0; i < nvalues; i++)
+ if (defval == values[i])
+ return i;
+
+ dev_err(priv->dev, "Invalid value for property %s: %d\n", prop, defval);
+ return -EINVAL;
+}
+
+int cs42l43_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ /* This tip sense invert is always set, HW wants an inverted signal */
+ unsigned int tip_deb = CS42L43_TIPSENSE_INV_MASK;
+ unsigned int hs2 = 0x2 << CS42L43_HSDET_MODE_SHIFT;
+ unsigned int autocontrol = 0, pdncntl = 0;
+ int ret;
+
+ dev_dbg(priv->dev, "Configure accessory detect\n");
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for jack config: %d\n", ret);
+ return ret;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ priv->jack_hp = jack;
+
+ if (!jack)
+ goto done;
+
+ ret = device_property_count_u32(cs42l43->dev, "cirrus,buttons-ohms");
+ if (ret != -EINVAL) {
+ if (ret < 0) {
+ dev_err(priv->dev, "Property cirrus,buttons-ohms malformed: %d\n",
+ ret);
+ goto error;
+ }
+
+ if (ret > CS42L43_N_BUTTONS) {
+ ret = -EINVAL;
+ dev_err(priv->dev, "Property cirrus,buttons-ohms too many entries\n");
+ goto error;
+ }
+
+ device_property_read_u32_array(cs42l43->dev, "cirrus,buttons-ohms",
+ priv->buttons, ret);
+ } else {
+ priv->buttons[0] = 70;
+ priv->buttons[1] = 185;
+ priv->buttons[2] = 355;
+ priv->buttons[3] = 735;
+ }
+
+ ret = cs42l43_find_index(priv, "cirrus,detect-us", 10000, &priv->detect_us,
+ cs42l43_accdet_us, ARRAY_SIZE(cs42l43_accdet_us));
+ if (ret < 0)
+ goto error;
+
+ hs2 |= ret << CS42L43_AUTO_HSDET_TIME_SHIFT;
+
+ priv->bias_low = device_property_read_bool(cs42l43->dev, "cirrus,bias-low");
+
+ ret = cs42l43_find_index(priv, "cirrus,bias-ramp-ms", 170,
+ &priv->bias_ramp_ms, cs42l43_accdet_ramp_ms,
+ ARRAY_SIZE(cs42l43_accdet_ramp_ms));
+ if (ret < 0)
+ goto error;
+
+ hs2 |= ret << CS42L43_HSBIAS_RAMP_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,bias-sense-microamp", 0,
+ &priv->bias_sense_ua, cs42l43_accdet_bias_sense,
+ ARRAY_SIZE(cs42l43_accdet_bias_sense));
+ if (ret < 0)
+ goto error;
+
+ if (priv->bias_sense_ua)
+ autocontrol |= ret << CS42L43_HSBIAS_SENSE_TRIP_SHIFT;
+
+ if (!device_property_read_bool(cs42l43->dev, "cirrus,button-automute"))
+ autocontrol |= CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK;
+
+ ret = device_property_read_u32(cs42l43->dev, "cirrus,tip-debounce-ms",
+ &priv->tip_debounce_ms);
+ if (ret < 0 && ret != -EINVAL) {
+ dev_err(priv->dev, "Property cirrus,tip-debounce-ms malformed: %d\n", ret);
+ goto error;
+ }
+
+ /* This tip sense invert is set normally, as TIPSENSE_INV already inverted */
+ if (device_property_read_bool(cs42l43->dev, "cirrus,tip-invert"))
+ autocontrol |= 0x1 << CS42L43_JACKDET_INV_SHIFT;
+
+ if (device_property_read_bool(cs42l43->dev, "cirrus,tip-disable-pullup"))
+ autocontrol |= 0x1 << CS42L43_JACKDET_MODE_SHIFT;
+ else
+ autocontrol |= 0x3 << CS42L43_JACKDET_MODE_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,tip-fall-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ tip_deb |= ret << CS42L43_TIPSENSE_FALLING_DB_TIME_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,tip-rise-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ tip_deb |= ret << CS42L43_TIPSENSE_RISING_DB_TIME_SHIFT;
+
+ if (device_property_read_bool(cs42l43->dev, "cirrus,use-ring-sense")) {
+ unsigned int ring_deb = 0;
+
+ priv->use_ring_sense = true;
+
+ /* HW wants an inverted signal, so invert the invert */
+ if (!device_property_read_bool(cs42l43->dev, "cirrus,ring-invert"))
+ ring_deb |= CS42L43_RINGSENSE_INV_MASK;
+
+ if (!device_property_read_bool(cs42l43->dev,
+ "cirrus,ring-disable-pullup"))
+ ring_deb |= CS42L43_RINGSENSE_PULLUP_PDNB_MASK;
+
+ ret = cs42l43_find_index(priv, "cirrus,ring-fall-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ ring_deb |= ret << CS42L43_RINGSENSE_FALLING_DB_TIME_SHIFT;
+
+ ret = cs42l43_find_index(priv, "cirrus,ring-rise-db-ms", 500,
+ NULL, cs42l43_accdet_db_ms,
+ ARRAY_SIZE(cs42l43_accdet_db_ms));
+ if (ret < 0)
+ goto error;
+
+ ring_deb |= ret << CS42L43_RINGSENSE_RISING_DB_TIME_SHIFT;
+ pdncntl |= CS42L43_RING_SENSE_EN_MASK;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_RINGSENSE_DEB_CTRL,
+ CS42L43_RINGSENSE_INV_MASK |
+ CS42L43_RINGSENSE_PULLUP_PDNB_MASK |
+ CS42L43_RINGSENSE_FALLING_DB_TIME_MASK |
+ CS42L43_RINGSENSE_RISING_DB_TIME_MASK,
+ ring_deb);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_TIPSENSE_DEB_CTRL,
+ CS42L43_TIPSENSE_INV_MASK |
+ CS42L43_TIPSENSE_FALLING_DB_TIME_MASK |
+ CS42L43_TIPSENSE_RISING_DB_TIME_MASK, tip_deb);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSBIAS_RAMP_MASK | CS42L43_HSDET_MODE_MASK |
+ CS42L43_AUTO_HSDET_TIME_MASK, hs2);
+
+done:
+ ret = 0;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_JACKDET_MODE_MASK | CS42L43_S0_AUTO_ADCMUTE_DISABLE_MASK |
+ CS42L43_HSBIAS_SENSE_TRIP_MASK, autocontrol);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PDNCNTL,
+ CS42L43_RING_SENSE_EN_MASK, pdncntl);
+
+ dev_dbg(priv->dev, "Successfully configured accessory detect\n");
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return ret;
+}
+
+static void cs42l43_start_hs_bias(struct cs42l43_codec *priv, bool force_high)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0x3 << CS42L43_HSBIAS_MODE_SHIFT;
+
+ dev_dbg(priv->dev, "Start headset bias\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK);
+
+ if (!force_high && priv->bias_low)
+ val = 0x2 << CS42L43_HSBIAS_MODE_SHIFT;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, val);
+
+ msleep(priv->bias_ramp_ms);
+}
+
+static void cs42l43_stop_hs_bias(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop headset bias\n");
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, 0);
+}
+
+irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+
+ queue_delayed_work(system_wq, &priv->bias_sense_timeout,
+ msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+#define CS42L43_JACK_PRESENT 0x3
+#define CS42L43_JACK_ABSENT 0x0
+
+#define CS42L43_JACK_OPTICAL (SND_JACK_MECHANICAL | SND_JACK_AVOUT)
+#define CS42L43_JACK_HEADPHONE (SND_JACK_MECHANICAL | SND_JACK_HEADPHONE)
+#define CS42L43_JACK_HEADSET (SND_JACK_MECHANICAL | SND_JACK_HEADSET)
+#define CS42L43_JACK_LINEOUT (SND_JACK_MECHANICAL | SND_JACK_LINEOUT)
+#define CS42L43_JACK_LINEIN (SND_JACK_MECHANICAL | SND_JACK_LINEIN)
+#define CS42L43_JACK_EXTENSION (SND_JACK_MECHANICAL)
+#define CS42L43_JACK_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | \
+ SND_JACK_BTN_3 | SND_JACK_BTN_4 | SND_JACK_BTN_5)
+
+static inline bool cs42l43_jack_present(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int sts = 0;
+
+ regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts);
+
+ sts = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+
+ return sts == CS42L43_JACK_PRESENT;
+}
+
+static void cs42l43_start_button_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0x3 << CS42L43_BUTTON_DETECT_MODE_SHIFT;
+
+ dev_dbg(priv->dev, "Start button detect\n");
+
+ priv->button_detect_running = true;
+
+ if (priv->bias_low)
+ val = 0x1 << CS42L43_BUTTON_DETECT_MODE_SHIFT;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_BUTTON_DETECT_MODE_MASK |
+ CS42L43_MIC_LVL_DET_DISABLE_MASK, val);
+
+ if (priv->bias_sense_ua) {
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK);
+ }
+}
+
+static void cs42l43_stop_button_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop button detect\n");
+
+ if (priv->bias_sense_ua) {
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_HSBIAS_SENSE_EN_MASK |
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_BUTTON_DETECT_MODE_MASK |
+ CS42L43_MIC_LVL_DET_DISABLE_MASK,
+ CS42L43_MIC_LVL_DET_DISABLE_MASK);
+
+ priv->button_detect_running = false;
+}
+
+#define CS42L43_BUTTON_COMB_MAX 512
+#define CS42L43_BUTTON_ROUT 2210
+
+void cs42l43_button_press_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ button_press_work.work);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int buttons = 0;
+ unsigned int val = 0;
+ int i, ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for button press: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (!priv->button_detect_running) {
+ dev_dbg(priv->dev, "Spurious button press IRQ\n");
+ goto error;
+ }
+
+ regmap_read(cs42l43->regmap, CS42L43_DETECT_STATUS_1, &val);
+
+ /* Bail if jack removed, the button is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv)) {
+ dev_dbg(priv->dev, "Button ignored due to removal\n");
+ goto error;
+ }
+
+ if (val & CS42L43_HSBIAS_CLAMP_STS_MASK) {
+ dev_dbg(priv->dev, "Button ignored due to bias sense\n");
+ goto error;
+ }
+
+ val = (val & CS42L43_HSDET_DC_STS_MASK) >> CS42L43_HSDET_DC_STS_SHIFT;
+ val = ((CS42L43_BUTTON_COMB_MAX << 20) / (val + 1)) - (1 << 20);
+ if (val)
+ val = (CS42L43_BUTTON_ROUT << 20) / val;
+ else
+ val = UINT_MAX;
+
+ for (i = 0; i < CS42L43_N_BUTTONS; i++) {
+ if (val < priv->buttons[i]) {
+ buttons = SND_JACK_BTN_0 >> i;
+ dev_dbg(priv->dev, "Detected button %d at %d Ohms\n", i, val);
+ break;
+ }
+ }
+
+ if (!buttons)
+ dev_dbg(priv->dev, "Unrecognised button: %d Ohms\n", val);
+
+ snd_soc_jack_report(priv->jack_hp, buttons, CS42L43_JACK_BUTTONS);
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+irqreturn_t cs42l43_button_press(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+
+ // Wait for 2 full cycles of comb filter to ensure good reading
+ queue_delayed_work(system_wq, &priv->button_press_work,
+ msecs_to_jiffies(10));
+
+ return IRQ_HANDLED;
+}
+
+void cs42l43_button_release_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ button_release_work);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for button release: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (priv->button_detect_running) {
+ dev_dbg(priv->dev, "Button release IRQ\n");
+
+ snd_soc_jack_report(priv->jack_hp, 0, CS42L43_JACK_BUTTONS);
+ } else {
+ dev_dbg(priv->dev, "Spurious button release IRQ\n");
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+irqreturn_t cs42l43_button_release(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+
+ queue_work(system_wq, &priv->button_release_work);
+
+ return IRQ_HANDLED;
+}
+
+void cs42l43_bias_sense_timeout(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ bias_sense_timeout.work);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for bias sense: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ if (cs42l43_jack_present(priv) && priv->button_detect_running) {
+ dev_dbg(priv->dev, "Bias sense timeout out, restore bias\n");
+
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap,
+ CS42L43_HS_BIAS_SENSE_AND_CLAMP_AUTOCONTROL,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK,
+ CS42L43_AUTO_HSBIAS_CLAMP_EN_MASK);
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+static void cs42l43_start_load_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Start load detect\n");
+
+ snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component));
+
+ priv->load_detect_running = true;
+
+ if (priv->hp_ena) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_shutdown);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, 0);
+
+ time_left = wait_for_completion_timeout(&priv->hp_shutdown,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "Load detect HP power down timed out\n");
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2, CS42L43_HP_HPF_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL,
+ CS42L43_ADPTPWR_MODE_MASK, 0x4 << CS42L43_ADPTPWR_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL,
+ CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK, 0x6);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK, 0);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, CS42L43_HS_CLAMP_DISABLE_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA,
+ CS42L43_HPLOAD_DET_EN_MASK,
+ CS42L43_HPLOAD_DET_EN_MASK);
+
+ snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component));
+}
+
+static void cs42l43_stop_load_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Stop load detect\n");
+
+ snd_soc_dapm_mutex_lock(snd_soc_component_get_dapm(priv->component));
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_LOADDETENA,
+ CS42L43_HPLOAD_DET_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HS_CLAMP_DISABLE_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG1,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK,
+ CS42L43_HP_MSTR_VOL_CTRL_EN_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PGAVOL,
+ CS42L43_HP_DIG_VOL_RAMP_MASK | CS42L43_HP_ANA_VOL_RAMP_MASK,
+ 0x4 << CS42L43_HP_DIG_VOL_RAMP_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL,
+ CS42L43_ADPTPWR_MODE_MASK, 0x7 << CS42L43_ADPTPWR_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_MIC_DETECT_CONTROL_1,
+ CS42L43_HSBIAS_MODE_MASK, 0x1 << CS42L43_HSBIAS_MODE_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_DACCNFG2,
+ CS42L43_HP_HPF_EN_MASK, CS42L43_HP_HPF_EN_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ CS42L43_ADC1_EN_MASK | CS42L43_ADC2_EN_MASK,
+ priv->adc_ena);
+
+ if (priv->hp_ena) {
+ unsigned long time_left;
+
+ reinit_completion(&priv->hp_startup);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ CS42L43_HP_EN_MASK, priv->hp_ena);
+
+ time_left = wait_for_completion_timeout(&priv->hp_startup,
+ msecs_to_jiffies(CS42L43_HP_TIMEOUT_MS));
+ if (!time_left)
+ dev_err(priv->dev, "Load detect HP restore timed out\n");
+ }
+
+ priv->load_detect_running = false;
+
+ snd_soc_dapm_mutex_unlock(snd_soc_component_get_dapm(priv->component));
+}
+
+static int cs42l43_run_load_detect(struct cs42l43_codec *priv, bool mic)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val = 0;
+ unsigned long time_left;
+
+ reinit_completion(&priv->load_detect);
+
+ cs42l43_start_load_detect(priv);
+ time_left = wait_for_completion_timeout(&priv->load_detect,
+ msecs_to_jiffies(CS42L43_LOAD_TIMEOUT_MS));
+ cs42l43_stop_load_detect(priv);
+
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ regmap_read(cs42l43->regmap, CS42L43_LOADDETRESULTS, &val);
+
+ dev_dbg(priv->dev, "Headphone load detect: 0x%x\n", val);
+
+ /* Bail if jack removed, the load is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv))
+ return -ENODEV;
+
+ if (mic) {
+ cs42l43_start_hs_bias(priv, false);
+ cs42l43_start_button_detect(priv);
+
+ return CS42L43_JACK_HEADSET;
+ }
+
+ switch (val & CS42L43_AMP3_RES_DET_MASK) {
+ case 0x0: // low impedance
+ case 0x1: // high impedance
+ return CS42L43_JACK_HEADPHONE;
+ case 0x2: // lineout
+ case 0x3: // Open circuit
+ return CS42L43_JACK_LINEOUT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_run_type_detect(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ int timeout_ms = ((2 * priv->detect_us) / 1000) + 200;
+ unsigned int type = 0xff;
+ unsigned long time_left;
+
+ reinit_completion(&priv->type_detect);
+
+ cs42l43_start_hs_bias(priv, true);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK, 0x3 << CS42L43_HSDET_MODE_SHIFT);
+
+ time_left = wait_for_completion_timeout(&priv->type_detect,
+ msecs_to_jiffies(timeout_ms));
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK, 0x2 << CS42L43_HSDET_MODE_SHIFT);
+ cs42l43_stop_hs_bias(priv);
+
+ if (!time_left)
+ return -ETIMEDOUT;
+
+ regmap_read(cs42l43->regmap, CS42L43_HS_STAT, &type);
+
+ dev_dbg(priv->dev, "Type detect: 0x%x\n", type);
+
+ /* Bail if jack removed, the type is irrelevant and likely invalid */
+ if (!cs42l43_jack_present(priv))
+ return -ENODEV;
+
+ switch (type & CS42L43_HSDET_TYPE_STS_MASK) {
+ case 0x0: // CTIA
+ case 0x1: // OMTP
+ return cs42l43_run_load_detect(priv, true);
+ case 0x2: // 3-pole
+ return cs42l43_run_load_detect(priv, false);
+ case 0x3: // Open-circuit
+ return CS42L43_JACK_EXTENSION;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void cs42l43_clear_jack(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ cs42l43_stop_button_detect(priv);
+ cs42l43_stop_hs_bias(priv);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL,
+ CS42L43_JACK_STEREO_CONFIG_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK | CS42L43_HSDET_MANUAL_MODE_MASK,
+ 0x2 << CS42L43_HSDET_MODE_SHIFT);
+
+ snd_soc_jack_report(priv->jack_hp, 0, 0xFFFF);
+}
+
+void cs42l43_tip_sense_work(struct work_struct *work)
+{
+ struct cs42l43_codec *priv = container_of(work, struct cs42l43_codec,
+ tip_sense_work.work);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int sts = 0;
+ unsigned int tip, ring;
+ int ret, report;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for tip work: %d\n", ret);
+ return;
+ }
+
+ mutex_lock(&priv->jack_lock);
+
+ regmap_read(cs42l43->regmap, CS42L43_TIP_RING_SENSE_INTERRUPT_STATUS, &sts);
+
+ dev_dbg(priv->dev, "Tip sense: 0x%x\n", sts);
+
+ tip = (sts >> CS42L43_TIPSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+ ring = (sts >> CS42L43_RINGSENSE_PLUG_DB_STS_SHIFT) & CS42L43_JACK_PRESENT;
+
+ if (tip == CS42L43_JACK_PRESENT) {
+ if (cs42l43->sdw && !priv->jack_present) {
+ priv->jack_present = true;
+ pm_runtime_get(priv->dev);
+ }
+
+ if (priv->use_ring_sense && ring == CS42L43_JACK_ABSENT) {
+ report = CS42L43_JACK_OPTICAL;
+ } else {
+ report = cs42l43_run_type_detect(priv);
+ if (report < 0) {
+ dev_err(priv->dev, "Jack detect failed: %d\n", report);
+ goto error;
+ }
+ }
+
+ snd_soc_jack_report(priv->jack_hp, report, report);
+ } else {
+ priv->jack_override = 0;
+
+ cs42l43_clear_jack(priv);
+
+ if (cs42l43->sdw && priv->jack_present) {
+ pm_runtime_put(priv->dev);
+ priv->jack_present = false;
+ }
+ }
+
+error:
+ mutex_unlock(&priv->jack_lock);
+
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+}
+
+irqreturn_t cs42l43_tip_sense(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+
+ cancel_delayed_work(&priv->bias_sense_timeout);
+ cancel_delayed_work(&priv->tip_sense_work);
+ cancel_delayed_work(&priv->button_press_work);
+ cancel_work(&priv->button_release_work);
+
+ queue_delayed_work(system_long_wq, &priv->tip_sense_work,
+ msecs_to_jiffies(priv->tip_debounce_ms));
+
+ return IRQ_HANDLED;
+}
+
+enum cs42l43_raw_jack {
+ CS42L43_JACK_RAW_CTIA = 0,
+ CS42L43_JACK_RAW_OMTP,
+ CS42L43_JACK_RAW_HEADPHONE,
+ CS42L43_JACK_RAW_LINE_OUT,
+ CS42L43_JACK_RAW_LINE_IN,
+ CS42L43_JACK_RAW_MICROPHONE,
+ CS42L43_JACK_RAW_OPTICAL,
+};
+
+#define CS42L43_JACK_3_POLE_SWITCHES ((0x2 << CS42L43_HSDET_MANUAL_MODE_SHIFT) | \
+ CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK | \
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK | \
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK | \
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK | \
+ CS42L43_HSGND_HS3_SEL_MASK | \
+ CS42L43_HSGND_HS4_SEL_MASK)
+
+static const struct cs42l43_jack_override_mode {
+ unsigned int hsdet_mode;
+ unsigned int mic_ctrl;
+ unsigned int clamp_ctrl;
+ int report;
+} cs42l43_jack_override_modes[] = {
+ [CS42L43_JACK_RAW_CTIA] = {
+ .hsdet_mode = CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
+ CS42L43_HSGND_HS3_SEL_MASK,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADSET,
+ },
+ [CS42L43_JACK_RAW_OMTP] = {
+ .hsdet_mode = (0x1 << CS42L43_HSDET_MANUAL_MODE_SHIFT) |
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
+ CS42L43_HSGND_HS4_SEL_MASK,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADSET,
+ },
+ [CS42L43_JACK_RAW_HEADPHONE] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_HEADPHONE,
+ },
+ [CS42L43_JACK_RAW_LINE_OUT] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_LINEOUT,
+ },
+ [CS42L43_JACK_RAW_LINE_IN] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .mic_ctrl = 0x2 << CS42L43_JACK_STEREO_CONFIG_SHIFT,
+ .report = CS42L43_JACK_LINEIN,
+ },
+ [CS42L43_JACK_RAW_MICROPHONE] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .mic_ctrl = (0x3 << CS42L43_JACK_STEREO_CONFIG_SHIFT) |
+ CS42L43_HS1_BIAS_EN_MASK | CS42L43_HS2_BIAS_EN_MASK,
+ .report = CS42L43_JACK_LINEIN,
+ },
+ [CS42L43_JACK_RAW_OPTICAL] = {
+ .hsdet_mode = CS42L43_JACK_3_POLE_SWITCHES,
+ .clamp_ctrl = CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ .report = CS42L43_JACK_OPTICAL,
+ },
+};
+
+static const char * const cs42l43_jack_text[] = {
+ "None", "CTIA", "OMTP", "Headphone", "Line-Out",
+ "Line-In", "Microphone", "Optical",
+};
+
+SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_jack_enum, cs42l43_jack_text);
+
+int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ mutex_lock(&priv->jack_lock);
+ ucontrol->value.integer.value[0] = priv->jack_override;
+ mutex_unlock(&priv->jack_lock);
+
+ return 0;
+}
+
+int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ unsigned int override = ucontrol->value.integer.value[0];
+
+ BUILD_BUG_ON(ARRAY_SIZE(cs42l43_jack_override_modes) !=
+ ARRAY_SIZE(cs42l43_jack_text) - 1);
+
+ if (override >= e->items)
+ return -EINVAL;
+
+ mutex_lock(&priv->jack_lock);
+
+ if (!cs42l43_jack_present(priv)) {
+ mutex_unlock(&priv->jack_lock);
+ return -EBUSY;
+ }
+
+ if (override == priv->jack_override) {
+ mutex_unlock(&priv->jack_lock);
+ return 0;
+ }
+
+ priv->jack_override = override;
+
+ cs42l43_clear_jack(priv);
+
+ if (!override) {
+ queue_delayed_work(system_long_wq, &priv->tip_sense_work, 0);
+ } else {
+ override--;
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_HS2,
+ CS42L43_HSDET_MODE_MASK |
+ CS42L43_HSDET_MANUAL_MODE_MASK |
+ CS42L43_AMP3_4_GNDREF_HS3_SEL_MASK |
+ CS42L43_AMP3_4_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS3_SEL_MASK |
+ CS42L43_HSBIAS_GNDREF_HS4_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS3_SEL_MASK |
+ CS42L43_HSBIAS_OUT_HS4_SEL_MASK |
+ CS42L43_HSGND_HS3_SEL_MASK |
+ CS42L43_HSGND_HS4_SEL_MASK,
+ cs42l43_jack_override_modes[override].hsdet_mode);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CTRL,
+ CS42L43_HS2_BIAS_EN_MASK | CS42L43_HS1_BIAS_EN_MASK |
+ CS42L43_JACK_STEREO_CONFIG_MASK,
+ cs42l43_jack_override_modes[override].mic_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_STEREO_MIC_CLAMP_CTRL,
+ CS42L43_SMIC_HPAMP_CLAMP_DIS_FRC_MASK,
+ cs42l43_jack_override_modes[override].clamp_ctrl);
+
+ switch (override) {
+ case CS42L43_JACK_RAW_CTIA:
+ case CS42L43_JACK_RAW_OMTP:
+ cs42l43_start_hs_bias(priv, false);
+ cs42l43_start_button_detect(priv);
+ break;
+ case CS42L43_JACK_RAW_LINE_IN:
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL1,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ADC_B_CTRL2,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK,
+ CS42L43_PGA_WIDESWING_MODE_EN_MASK);
+ break;
+ case CS42L43_JACK_RAW_MICROPHONE:
+ cs42l43_start_hs_bias(priv, false);
+ break;
+ default:
+ break;
+ }
+
+ snd_soc_jack_report(priv->jack_hp,
+ cs42l43_jack_override_modes[override].report,
+ cs42l43_jack_override_modes[override].report);
+ }
+
+ mutex_unlock(&priv->jack_lock);
+
+ return 1;
+}
diff --git a/sound/soc/codecs/cs42l43-sdw.c b/sound/soc/codecs/cs42l43-sdw.c
new file mode 100644
index 000000000000..55ac5fe8c3db
--- /dev/null
+++ b/sound/soc/codecs/cs42l43-sdw.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver SoundWire handling
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/errno.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/sdw.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+
+#include "cs42l43.h"
+
+int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent);
+ struct sdw_stream_config sconfig = {0};
+ struct sdw_port_config pconfig = {0};
+ int ret;
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ snd_sdw_params_to_config(substream, params, &sconfig, &pconfig);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ pconfig.num = dai->id;
+ else
+ pconfig.num = dai->id;
+
+ ret = sdw_stream_add_slave(sdw, &sconfig, &pconfig, 1, sdw_stream);
+ if (ret) {
+ dev_err(priv->dev, "Failed to add sdw stream: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_add_peripheral, SND_SOC_CS42L43);
+
+int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct sdw_stream_runtime *sdw_stream = snd_soc_dai_get_dma_data(dai, substream);
+ struct sdw_slave *sdw = dev_to_sdw_dev(priv->dev->parent);
+
+ if (!sdw_stream)
+ return -EINVAL;
+
+ return sdw_stream_remove_slave(sdw, sdw_stream);
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_remove_peripheral, SND_SOC_CS42L43);
+
+int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(cs42l43_sdw_set_stream, SND_SOC_CS42L43);
+
+MODULE_DESCRIPTION("CS42L43 CODEC SoundWire Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.c b/sound/soc/codecs/cs42l43.c
new file mode 100644
index 000000000000..24e718e51174
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.c
@@ -0,0 +1,2278 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// CS42L43 CODEC driver
+//
+// Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+// Cirrus Logic International Semiconductor Ltd.
+
+#include <linux/bitops.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/gcd.h>
+#include <linux/irq.h>
+#include <linux/jiffies.h>
+#include <linux/mfd/cs42l43.h>
+#include <linux/mfd/cs42l43-regs.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/string.h>
+#include <sound/control.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc-component.h>
+#include <sound/soc-dapm.h>
+#include <sound/soc-dai.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#include "cs42l43.h"
+
+#define CS42L43_DECL_MUX(name, reg) \
+static SOC_VALUE_ENUM_SINGLE_DECL(cs42l43_##name##_enum, reg, \
+ 0, CS42L43_MIXER_SRC_MASK, \
+ cs42l43_mixer_texts, cs42l43_mixer_values); \
+static const struct snd_kcontrol_new cs42l43_##name##_mux = \
+ SOC_DAPM_ENUM("Route", cs42l43_##name##_enum)
+
+#define CS42L43_DECL_MIXER(name, reg) \
+ CS42L43_DECL_MUX(name##_in1, reg); \
+ CS42L43_DECL_MUX(name##_in2, reg + 0x4); \
+ CS42L43_DECL_MUX(name##_in3, reg + 0x8); \
+ CS42L43_DECL_MUX(name##_in4, reg + 0xC)
+
+#define CS42L43_DAPM_MUX(name_str, name) \
+ SND_SOC_DAPM_MUX(name_str " Input", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_mux)
+
+#define CS42L43_DAPM_MIXER(name_str, name) \
+ SND_SOC_DAPM_MUX(name_str " Input 1", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in1_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 2", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in2_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 3", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in3_mux), \
+ SND_SOC_DAPM_MUX(name_str " Input 4", SND_SOC_NOPM, 0, 0, &cs42l43_##name##_in4_mux), \
+ SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+
+#define CS42L43_BASE_ROUTES(name_str) \
+ { name_str, "Tone Generator 1", "Tone 1" }, \
+ { name_str, "Tone Generator 2", "Tone 2" }, \
+ { name_str, "Decimator 1", "Decimator 1" }, \
+ { name_str, "Decimator 2", "Decimator 2" }, \
+ { name_str, "Decimator 3", "Decimator 3" }, \
+ { name_str, "Decimator 4", "Decimator 4" }, \
+ { name_str, "ASPRX1", "ASPRX1" }, \
+ { name_str, "ASPRX2", "ASPRX2" }, \
+ { name_str, "ASPRX3", "ASPRX3" }, \
+ { name_str, "ASPRX4", "ASPRX4" }, \
+ { name_str, "ASPRX5", "ASPRX5" }, \
+ { name_str, "ASPRX6", "ASPRX6" }, \
+ { name_str, "DP5RX1", "DP5RX1" }, \
+ { name_str, "DP5RX2", "DP5RX2" }, \
+ { name_str, "DP6RX1", "DP6RX1" }, \
+ { name_str, "DP6RX2", "DP6RX2" }, \
+ { name_str, "DP7RX1", "DP7RX1" }, \
+ { name_str, "DP7RX2", "DP7RX2" }, \
+ { name_str, "ASRC INT1", "ASRC_INT1" }, \
+ { name_str, "ASRC INT2", "ASRC_INT2" }, \
+ { name_str, "ASRC INT3", "ASRC_INT3" }, \
+ { name_str, "ASRC INT4", "ASRC_INT4" }, \
+ { name_str, "ASRC DEC1", "ASRC_DEC1" }, \
+ { name_str, "ASRC DEC2", "ASRC_DEC2" }, \
+ { name_str, "ASRC DEC3", "ASRC_DEC3" }, \
+ { name_str, "ASRC DEC4", "ASRC_DEC4" }, \
+ { name_str, "ISRC1 INT1", "ISRC1INT1" }, \
+ { name_str, "ISRC1 INT2", "ISRC1INT2" }, \
+ { name_str, "ISRC1 DEC1", "ISRC1DEC1" }, \
+ { name_str, "ISRC1 DEC2", "ISRC1DEC2" }, \
+ { name_str, "ISRC2 INT1", "ISRC2INT1" }, \
+ { name_str, "ISRC2 INT2", "ISRC2INT2" }, \
+ { name_str, "ISRC2 DEC1", "ISRC2DEC1" }, \
+ { name_str, "ISRC2 DEC2", "ISRC2DEC2" }, \
+ { name_str, "EQ1", "EQ" }, \
+ { name_str, "EQ2", "EQ" }
+
+#define CS42L43_MUX_ROUTES(name_str, widget) \
+ { widget, NULL, name_str " Input" }, \
+ { name_str " Input", NULL, "Mixer Core" }, \
+ CS42L43_BASE_ROUTES(name_str " Input")
+
+#define CS42L43_MIXER_ROUTES(name_str, widget) \
+ { name_str " Mixer", NULL, name_str " Input 1" }, \
+ { name_str " Mixer", NULL, name_str " Input 2" }, \
+ { name_str " Mixer", NULL, name_str " Input 3" }, \
+ { name_str " Mixer", NULL, name_str " Input 4" }, \
+ { widget, NULL, name_str " Mixer" }, \
+ { name_str " Mixer", NULL, "Mixer Core" }, \
+ CS42L43_BASE_ROUTES(name_str " Input 1"), \
+ CS42L43_BASE_ROUTES(name_str " Input 2"), \
+ CS42L43_BASE_ROUTES(name_str " Input 3"), \
+ CS42L43_BASE_ROUTES(name_str " Input 4")
+
+#define CS42L43_MIXER_VOLUMES(name_str, base) \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 1 Volume", base, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 2 Volume", base + 4, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 3 Volume", base + 8, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv), \
+ SOC_SINGLE_RANGE_TLV(name_str " Input 4 Volume", base + 12, \
+ CS42L43_MIXER_VOL_SHIFT, 0x20, 0x50, 0, \
+ cs42l43_mixer_tlv)
+
+#define CS42L43_IRQ_ERROR(name) \
+static irqreturn_t cs42l43_##name(int irq, void *data) \
+{ \
+ struct cs42l43_codec *priv = data; \
+ dev_err(priv->dev, "Error " #name " IRQ\n"); \
+ return IRQ_HANDLED; \
+}
+
+CS42L43_IRQ_ERROR(pll_lost_lock)
+CS42L43_IRQ_ERROR(spkr_clock_stop)
+CS42L43_IRQ_ERROR(spkl_clock_stop)
+CS42L43_IRQ_ERROR(spkr_brown_out)
+CS42L43_IRQ_ERROR(spkl_brown_out)
+CS42L43_IRQ_ERROR(spkr_therm_shutdown)
+CS42L43_IRQ_ERROR(spkl_therm_shutdown)
+CS42L43_IRQ_ERROR(spkr_therm_warm)
+CS42L43_IRQ_ERROR(spkl_therm_warm)
+CS42L43_IRQ_ERROR(spkr_sc_detect)
+CS42L43_IRQ_ERROR(spkl_sc_detect)
+CS42L43_IRQ_ERROR(hp_ilimit)
+
+#define CS42L43_IRQ_COMPLETE(name) \
+static irqreturn_t cs42l43_##name(int irq, void *data) \
+{ \
+ struct cs42l43_codec *priv = data; \
+ dev_dbg(priv->dev, #name " completed\n"); \
+ complete(&priv->name); \
+ return IRQ_HANDLED; \
+}
+
+CS42L43_IRQ_COMPLETE(pll_ready)
+CS42L43_IRQ_COMPLETE(hp_startup)
+CS42L43_IRQ_COMPLETE(hp_shutdown)
+CS42L43_IRQ_COMPLETE(type_detect)
+CS42L43_IRQ_COMPLETE(spkr_shutdown)
+CS42L43_IRQ_COMPLETE(spkl_shutdown)
+CS42L43_IRQ_COMPLETE(spkr_startup)
+CS42L43_IRQ_COMPLETE(spkl_startup)
+CS42L43_IRQ_COMPLETE(load_detect)
+
+static irqreturn_t cs42l43_mic_shutter(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ const char * const controls[] = {
+ "Decimator 1 Switch",
+ "Decimator 2 Switch",
+ "Decimator 3 Switch",
+ "Decimator 4 Switch",
+ };
+ int i, ret;
+
+ dev_dbg(priv->dev, "Microphone shutter changed\n");
+
+ if (!priv->component)
+ return IRQ_NONE;
+
+ for (i = 0; i < ARRAY_SIZE(controls); i++) {
+ ret = snd_soc_component_notify_control(priv->component,
+ controls[i]);
+ if (ret)
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t cs42l43_spk_shutter(int irq, void *data)
+{
+ struct cs42l43_codec *priv = data;
+ int ret;
+
+ dev_dbg(priv->dev, "Speaker shutter changed\n");
+
+ if (!priv->component)
+ return IRQ_NONE;
+
+ ret = snd_soc_component_notify_control(priv->component,
+ "Speaker Digital Switch");
+ if (ret)
+ return IRQ_NONE;
+
+ return IRQ_HANDLED;
+}
+
+static const unsigned int cs42l43_sample_rates[] = {
+ 8000, 16000, 24000, 32000, 44100, 48000, 96000, 192000,
+};
+
+#define CS42L43_CONSUMER_RATE_MASK 0xFF
+#define CS42L43_PROVIDER_RATE_MASK 0xEF // 44.1k only supported as consumer
+
+static const struct snd_pcm_hw_constraint_list cs42l43_constraint = {
+ .count = ARRAY_SIZE(cs42l43_sample_rates),
+ .list = cs42l43_sample_rates,
+};
+
+static int cs42l43_startup(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+
+ if (provider)
+ priv->constraint.mask = CS42L43_PROVIDER_RATE_MASK;
+ else
+ priv->constraint.mask = CS42L43_CONSUMER_RATE_MASK;
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &priv->constraint);
+}
+
+static int cs42l43_convert_sample_rate(unsigned int rate)
+{
+ switch (rate) {
+ case 8000:
+ return 0x11;
+ case 16000:
+ return 0x12;
+ case 24000:
+ return 0x02;
+ case 32000:
+ return 0x13;
+ case 44100:
+ return 0x0B;
+ case 48000:
+ return 0x03;
+ case 96000:
+ return 0x04;
+ case 192000:
+ return 0x05;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_set_sample_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ ret = cs42l43_convert_sample_rate(params_rate(params));
+ if (ret < 0) {
+ dev_err(priv->dev, "Failed to convert sample rate: %d\n", ret);
+ return ret;
+ }
+
+ //FIXME: For now lets just set sample rate 1, this needs expanded in the future
+ regmap_update_bits(cs42l43->regmap, CS42L43_SAMPLE_RATE1,
+ CS42L43_SAMPLE_RATE_MASK, ret);
+
+ return 0;
+}
+
+static int cs42l43_asp_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(dai->component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int dsp_mode = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CTRL,
+ CS42L43_ASP_FSYNC_MODE_MASK);
+ int provider = !!regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+ int n_chans = params_channels(params);
+ int data_width = params_width(params);
+ int n_slots = n_chans;
+ int slot_width = data_width;
+ int frame, bclk_target, i;
+ unsigned int reg;
+ int *slots;
+
+ if (priv->n_slots) {
+ n_slots = priv->n_slots;
+ slot_width = priv->slot_width;
+ }
+
+ if (!dsp_mode && (n_slots & 0x1)) {
+ dev_dbg(priv->dev, "Forcing balanced channels on ASP\n");
+ n_slots++;
+ }
+
+ frame = n_slots * slot_width;
+ bclk_target = params_rate(params) * frame;
+
+ if (provider) {
+ unsigned int gcd_nm = gcd(bclk_target, CS42L43_INTERNAL_SYSCLK);
+ int n = bclk_target / gcd_nm;
+ int m = CS42L43_INTERNAL_SYSCLK / gcd_nm;
+
+ if (n > (CS42L43_ASP_BCLK_N_MASK >> CS42L43_ASP_BCLK_N_SHIFT) ||
+ m > CS42L43_ASP_BCLK_M_MASK) {
+ dev_err(priv->dev, "Can't produce %dHz bclk\n", bclk_target);
+ return -EINVAL;
+ }
+
+ dev_dbg(priv->dev, "bclk %d/%d = %dHz, with %dx%d frame\n",
+ n, m, bclk_target, n_slots, slot_width);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG1,
+ CS42L43_ASP_BCLK_N_MASK | CS42L43_ASP_BCLK_M_MASK,
+ n << CS42L43_ASP_BCLK_N_SHIFT |
+ m << CS42L43_ASP_BCLK_M_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL1,
+ CS42L43_ASP_FSYNC_M_MASK, frame);
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL4,
+ CS42L43_ASP_NUM_BCLKS_PER_FSYNC_MASK,
+ frame << CS42L43_ASP_NUM_BCLKS_PER_FSYNC_SHIFT);
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ reg = CS42L43_ASP_TX_CH1_CTRL;
+ slots = priv->tx_slots;
+ } else {
+ reg = CS42L43_ASP_RX_CH1_CTRL;
+ slots = priv->rx_slots;
+ }
+
+ for (i = 0; i < n_chans; i++, reg += 4) {
+ int slot_phase = dsp_mode | (i & CS42L43_ASP_CH_SLOT_PHASE_MASK);
+ int slot_pos;
+
+ if (dsp_mode)
+ slot_pos = slots[i] * slot_width;
+ else
+ slot_pos = (slots[i] / 2) * slot_width;
+
+ dev_dbg(priv->dev, "Configure channel %d at slot %d (%d,%d)\n",
+ i, slots[i], slot_pos, slot_phase);
+
+ regmap_update_bits(cs42l43->regmap, reg,
+ CS42L43_ASP_CH_WIDTH_MASK |
+ CS42L43_ASP_CH_SLOT_MASK |
+ CS42L43_ASP_CH_SLOT_PHASE_MASK,
+ ((data_width - 1) << CS42L43_ASP_CH_WIDTH_SHIFT) |
+ (slot_pos << CS42L43_ASP_CH_SLOT_SHIFT) |
+ slot_phase);
+ }
+
+ return cs42l43_set_sample_rate(substream, params, dai);
+}
+
+static int cs42l43_asp_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_component *component = dai->component;
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int provider = regmap_test_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK);
+ struct snd_soc_dapm_route routes[] = {
+ { "BCLK", NULL, "FSYNC" },
+ };
+ unsigned int asp_ctrl = 0;
+ unsigned int data_ctrl = 0;
+ unsigned int fsync_ctrl = 0;
+ unsigned int clk_config = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_DSP_A:
+ data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT;
+ fallthrough;
+ case SND_SOC_DAIFMT_DSP_B:
+ asp_ctrl |= CS42L43_ASP_FSYNC_MODE_MASK;
+ data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK;
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ data_ctrl |= 2 << CS42L43_ASP_FSYNC_FRAME_START_DLY_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ data_ctrl |= CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported DAI format 0x%x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) {
+ case SND_SOC_DAIFMT_CBC_CFC:
+ if (provider)
+ snd_soc_dapm_del_routes(dapm, routes, ARRAY_SIZE(routes));
+ break;
+ case SND_SOC_DAIFMT_CBP_CFP:
+ if (!provider)
+ snd_soc_dapm_add_routes(dapm, routes, ARRAY_SIZE(routes));
+ clk_config |= CS42L43_ASP_MASTER_MODE_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported ASP mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ clk_config |= CS42L43_ASP_BCLK_INV_MASK; /* Yes BCLK_INV = NB */
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ clk_config |= CS42L43_ASP_BCLK_INV_MASK;
+ fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ fsync_ctrl |= CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK;
+ break;
+ default:
+ dev_err(priv->dev, "Unsupported invert mode 0x%x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CTRL,
+ CS42L43_ASP_FSYNC_MODE_MASK,
+ asp_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_DATA_CTRL,
+ CS42L43_ASP_FSYNC_FRAME_START_DLY_MASK |
+ CS42L43_ASP_FSYNC_FRAME_START_PHASE_MASK,
+ data_ctrl);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_CLK_CONFIG2,
+ CS42L43_ASP_MASTER_MODE_MASK |
+ CS42L43_ASP_BCLK_INV_MASK,
+ clk_config);
+ regmap_update_bits(cs42l43->regmap, CS42L43_ASP_FSYNC_CTRL3,
+ CS42L43_ASP_FSYNC_IN_INV_MASK |
+ CS42L43_ASP_FSYNC_OUT_INV_MASK,
+ fsync_ctrl);
+
+ return 0;
+}
+
+static void cs42l43_mask_to_slots(struct cs42l43_codec *priv, unsigned int mask, int *slots)
+{
+ int i;
+
+ for (i = 0; i < CS42L43_ASP_MAX_CHANNELS; ++i) {
+ int slot = ffs(mask) - 1;
+
+ if (slot < 0)
+ return;
+
+ slots[i] = slot;
+
+ mask &= ~(1 << slot);
+ }
+
+ if (mask)
+ dev_warn(priv->dev, "Too many channels in TDM mask\n");
+}
+
+static int cs42l43_asp_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_component *component = dai->component;
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ priv->n_slots = slots;
+ priv->slot_width = slot_width;
+
+ if (!slots) {
+ tx_mask = CS42L43_DEFAULT_SLOTS;
+ rx_mask = CS42L43_DEFAULT_SLOTS;
+ }
+
+ cs42l43_mask_to_slots(priv, tx_mask, priv->tx_slots);
+ cs42l43_mask_to_slots(priv, rx_mask, priv->rx_slots);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs42l43_asp_ops = {
+ .startup = cs42l43_startup,
+ .hw_params = cs42l43_asp_hw_params,
+ .set_fmt = cs42l43_asp_set_fmt,
+ .set_tdm_slot = cs42l43_asp_set_tdm_slot,
+};
+
+static int cs42l43_sdw_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ int ret;
+
+ ret = cs42l43_sdw_add_peripheral(substream, params, dai);
+ if (ret)
+ return ret;
+
+ return cs42l43_set_sample_rate(substream, params, dai);
+};
+
+static const struct snd_soc_dai_ops cs42l43_sdw_ops = {
+ .startup = cs42l43_startup,
+ .set_stream = cs42l43_sdw_set_stream,
+ .hw_params = cs42l43_sdw_hw_params,
+ .hw_free = cs42l43_sdw_remove_peripheral,
+};
+
+#define CS42L43_ASP_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+#define CS42L43_SDW_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver cs42l43_dais[] = {
+ {
+ .name = "cs42l43-asp",
+ .ops = &cs42l43_asp_ops,
+ .symmetric_rate = 1,
+ .capture = {
+ .stream_name = "ASP Capture",
+ .channels_min = 1,
+ .channels_max = CS42L43_ASP_MAX_CHANNELS,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_ASP_FORMATS,
+ },
+ .playback = {
+ .stream_name = "ASP Playback",
+ .channels_min = 1,
+ .channels_max = CS42L43_ASP_MAX_CHANNELS,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_ASP_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp1",
+ .id = 1,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP1 Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp2",
+ .id = 2,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp3",
+ .id = 3,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP3 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp4",
+ .id = 4,
+ .ops = &cs42l43_sdw_ops,
+ .capture = {
+ .stream_name = "DP4 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp5",
+ .id = 5,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP5 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp6",
+ .id = 6,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP6 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+ {
+ .name = "cs42l43-dp7",
+ .id = 7,
+ .ops = &cs42l43_sdw_ops,
+ .playback = {
+ .stream_name = "DP7 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS42L43_SDW_FORMATS,
+ },
+ },
+};
+
+static const DECLARE_TLV_DB_SCALE(cs42l43_mixer_tlv, -3200, 100, 0);
+
+static const char * const cs42l43_ramp_text[] = {
+ "0ms/6dB", "0.5ms/6dB", "1ms/6dB", "2ms/6dB", "4ms/6dB", "8ms/6dB",
+ "15ms/6dB", "30ms/6dB",
+};
+
+static const char * const cs42l43_adc1_input_text[] = { "IN1", "IN2" };
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_adc1_input, CS42L43_ADC_B_CTRL1,
+ CS42L43_ADC_AIN_SEL_SHIFT,
+ cs42l43_adc1_input_text);
+
+static const struct snd_kcontrol_new cs42l43_adc1_input_ctl =
+ SOC_DAPM_ENUM("ADC1 Input", cs42l43_adc1_input);
+
+static const char * const cs42l43_dec_mode_text[] = { "ADC", "PDM" };
+
+static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec1_mode, cs42l43_dec_mode_text);
+static SOC_ENUM_SINGLE_VIRT_DECL(cs42l43_dec2_mode, cs42l43_dec_mode_text);
+
+static const struct snd_kcontrol_new cs42l43_dec_mode_ctl[] = {
+ SOC_DAPM_ENUM("Decimator 1 Mode", cs42l43_dec1_mode),
+ SOC_DAPM_ENUM("Decimator 2 Mode", cs42l43_dec2_mode),
+};
+
+static const char * const cs42l43_pdm_clk_text[] = {
+ "3.072MHz", "1.536MHz", "768kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_pdm1_clk, CS42L43_PDM_CONTROL,
+ CS42L43_PDM1_CLK_DIV_SHIFT, cs42l43_pdm_clk_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_pdm2_clk, CS42L43_PDM_CONTROL,
+ CS42L43_PDM2_CLK_DIV_SHIFT, cs42l43_pdm_clk_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_adc_tlv, -600, 600, 0);
+static DECLARE_TLV_DB_SCALE(cs42l43_dec_tlv, -6400, 50, 0);
+
+static const char * const cs42l43_wnf_corner_text[] = {
+ "160Hz", "180Hz", "200Hz", "220Hz", "240Hz", "260Hz", "280Hz", "300Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_wnf_corner, CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_WNF_CF_SHIFT, cs42l43_wnf_corner_text);
+
+static const char * const cs42l43_hpf_corner_text[] = {
+ "3Hz", "12Hz", "48Hz", "96Hz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_hpf_corner, CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_HPF_CF_SHIFT, cs42l43_hpf_corner_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec1_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_up, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec2_ramp_down, CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec3_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VD_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_up, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VI_RAMP_SHIFT, cs42l43_ramp_text);
+static SOC_ENUM_SINGLE_DECL(cs42l43_dec4_ramp_down, CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VD_RAMP_SHIFT, cs42l43_ramp_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_speaker_tlv, -6400, 50, 0);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_up, CS42L43_AMP1_2_VOL_RAMP,
+ CS42L43_AMP1_2_VI_RAMP_SHIFT, cs42l43_ramp_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_speaker_ramp_down, CS42L43_AMP1_2_VOL_RAMP,
+ CS42L43_AMP1_2_VD_RAMP_SHIFT, cs42l43_ramp_text);
+
+static DECLARE_TLV_DB_SCALE(cs42l43_headphone_tlv, -11450, 50, 1);
+
+static const char * const cs42l43_headphone_ramp_text[] = {
+ "1", "2", "4", "6", "8", "11", "12", "16", "22", "24", "33", "36", "44",
+ "48", "66", "72",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_headphone_ramp, CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_RAMP_SHIFT,
+ cs42l43_headphone_ramp_text);
+
+static const char * const cs42l43_tone_freq_text[] = {
+ "1kHz", "2kHz", "4kHz", "6kHz", "8kHz",
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_tone1_freq, CS42L43_TONE_CH1_CTRL,
+ CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text);
+
+static SOC_ENUM_SINGLE_DECL(cs42l43_tone2_freq, CS42L43_TONE_CH2_CTRL,
+ CS42L43_TONE_FREQ_SHIFT, cs42l43_tone_freq_text);
+
+static const char * const cs42l43_mixer_texts[] = {
+ "None",
+ "Tone Generator 1", "Tone Generator 2",
+ "Decimator 1", "Decimator 2", "Decimator 3", "Decimator 4",
+ "ASPRX1", "ASPRX2", "ASPRX3", "ASPRX4", "ASPRX5", "ASPRX6",
+ "DP5RX1", "DP5RX2", "DP6RX1", "DP6RX2", "DP7RX1", "DP7RX2",
+ "ASRC INT1", "ASRC INT2", "ASRC INT3", "ASRC INT4",
+ "ASRC DEC1", "ASRC DEC2", "ASRC DEC3", "ASRC DEC4",
+ "ISRC1 INT1", "ISRC1 INT2",
+ "ISRC1 DEC1", "ISRC1 DEC2",
+ "ISRC2 INT1", "ISRC2 INT2",
+ "ISRC2 DEC1", "ISRC2 DEC2",
+ "EQ1", "EQ2",
+};
+
+static const unsigned int cs42l43_mixer_values[] = {
+ 0x00, // None
+ 0x04, 0x05, // Tone Generator 1, 2
+ 0x10, 0x11, 0x12, 0x13, // Decimator 1, 2, 3, 4
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, // ASPRX1,2,3,4,5,6
+ 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, // DP5, 6, 7RX1, 2
+ 0x40, 0x41, 0x42, 0x43, // ASRC INT1, 2, 3, 4
+ 0x44, 0x45, 0x46, 0x47, // ASRC DEC1, 2, 3, 4
+ 0x50, 0x51, // ISRC1 INT1, 2
+ 0x52, 0x53, // ISRC1 DEC1, 2
+ 0x54, 0x55, // ISRC2 INT1, 2
+ 0x56, 0x57, // ISRC2 DEC1, 2
+ 0x58, 0x59, // EQ1, 2
+};
+
+CS42L43_DECL_MUX(asptx1, CS42L43_ASPTX1_INPUT);
+CS42L43_DECL_MUX(asptx2, CS42L43_ASPTX2_INPUT);
+CS42L43_DECL_MUX(asptx3, CS42L43_ASPTX3_INPUT);
+CS42L43_DECL_MUX(asptx4, CS42L43_ASPTX4_INPUT);
+CS42L43_DECL_MUX(asptx5, CS42L43_ASPTX5_INPUT);
+CS42L43_DECL_MUX(asptx6, CS42L43_ASPTX6_INPUT);
+
+CS42L43_DECL_MUX(dp1tx1, CS42L43_SWIRE_DP1_CH1_INPUT);
+CS42L43_DECL_MUX(dp1tx2, CS42L43_SWIRE_DP1_CH2_INPUT);
+CS42L43_DECL_MUX(dp1tx3, CS42L43_SWIRE_DP1_CH3_INPUT);
+CS42L43_DECL_MUX(dp1tx4, CS42L43_SWIRE_DP1_CH4_INPUT);
+CS42L43_DECL_MUX(dp2tx1, CS42L43_SWIRE_DP2_CH1_INPUT);
+CS42L43_DECL_MUX(dp2tx2, CS42L43_SWIRE_DP2_CH2_INPUT);
+CS42L43_DECL_MUX(dp3tx1, CS42L43_SWIRE_DP3_CH1_INPUT);
+CS42L43_DECL_MUX(dp3tx2, CS42L43_SWIRE_DP3_CH2_INPUT);
+CS42L43_DECL_MUX(dp4tx1, CS42L43_SWIRE_DP4_CH1_INPUT);
+CS42L43_DECL_MUX(dp4tx2, CS42L43_SWIRE_DP4_CH2_INPUT);
+
+CS42L43_DECL_MUX(asrcint1, CS42L43_ASRC_INT1_INPUT1);
+CS42L43_DECL_MUX(asrcint2, CS42L43_ASRC_INT2_INPUT1);
+CS42L43_DECL_MUX(asrcint3, CS42L43_ASRC_INT3_INPUT1);
+CS42L43_DECL_MUX(asrcint4, CS42L43_ASRC_INT4_INPUT1);
+CS42L43_DECL_MUX(asrcdec1, CS42L43_ASRC_DEC1_INPUT1);
+CS42L43_DECL_MUX(asrcdec2, CS42L43_ASRC_DEC2_INPUT1);
+CS42L43_DECL_MUX(asrcdec3, CS42L43_ASRC_DEC3_INPUT1);
+CS42L43_DECL_MUX(asrcdec4, CS42L43_ASRC_DEC4_INPUT1);
+
+CS42L43_DECL_MUX(isrc1int1, CS42L43_ISRC1INT1_INPUT1);
+CS42L43_DECL_MUX(isrc1int2, CS42L43_ISRC1INT2_INPUT1);
+CS42L43_DECL_MUX(isrc1dec1, CS42L43_ISRC1DEC1_INPUT1);
+CS42L43_DECL_MUX(isrc1dec2, CS42L43_ISRC1DEC2_INPUT1);
+CS42L43_DECL_MUX(isrc2int1, CS42L43_ISRC2INT1_INPUT1);
+CS42L43_DECL_MUX(isrc2int2, CS42L43_ISRC2INT2_INPUT1);
+CS42L43_DECL_MUX(isrc2dec1, CS42L43_ISRC2DEC1_INPUT1);
+CS42L43_DECL_MUX(isrc2dec2, CS42L43_ISRC2DEC2_INPUT1);
+
+CS42L43_DECL_MUX(spdif1, CS42L43_SPDIF1_INPUT1);
+CS42L43_DECL_MUX(spdif2, CS42L43_SPDIF2_INPUT1);
+
+CS42L43_DECL_MIXER(eq1, CS42L43_EQ1MIX_INPUT1);
+CS42L43_DECL_MIXER(eq2, CS42L43_EQ2MIX_INPUT1);
+
+CS42L43_DECL_MIXER(amp1, CS42L43_AMP1MIX_INPUT1);
+CS42L43_DECL_MIXER(amp2, CS42L43_AMP2MIX_INPUT1);
+
+CS42L43_DECL_MIXER(amp3, CS42L43_AMP3MIX_INPUT1);
+CS42L43_DECL_MIXER(amp4, CS42L43_AMP4MIX_INPUT1);
+
+static int cs42l43_dapm_get_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_put_volsw(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_get_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_dapm_put_enum(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ int ret;
+
+ snd_soc_dapm_mutex_lock(dapm);
+ ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return ret;
+}
+
+static int cs42l43_eq_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ memcpy(ucontrol->value.integer.value, priv->eq_coeffs, sizeof(priv->eq_coeffs));
+
+ return 0;
+}
+
+static int cs42l43_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ snd_soc_dapm_mutex_lock(dapm);
+
+ memcpy(priv->eq_coeffs, ucontrol->value.integer.value, sizeof(priv->eq_coeffs));
+
+ snd_soc_dapm_mutex_unlock(dapm);
+
+ return 0;
+}
+
+static void cs42l43_spk_vu_sync(struct cs42l43_codec *priv)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ mutex_lock(&priv->spk_vu_lock);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1,
+ CS42L43_AMP1_2_VU_MASK, CS42L43_AMP1_2_VU_MASK);
+ regmap_update_bits(cs42l43->regmap, CS42L43_INTP_VOLUME_CTRL1,
+ CS42L43_AMP1_2_VU_MASK, 0);
+
+ mutex_unlock(&priv->spk_vu_lock);
+}
+
+static int cs42l43_shutter_get(struct cs42l43_codec *priv, unsigned int shift)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(priv->dev);
+ if (ret) {
+ dev_err(priv->dev, "Failed to resume for shutters: %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * SHUTTER_CONTROL is a mix of volatile and non-volatile bits, so must
+ * be cached for the non-volatiles, so drop it from the cache here so
+ * we force a read.
+ */
+ ret = regcache_drop_region(cs42l43->regmap, CS42L43_SHUTTER_CONTROL,
+ CS42L43_SHUTTER_CONTROL);
+ if (ret) {
+ dev_err(priv->dev, "Failed to drop shutter from cache: %d\n", ret);
+ goto error;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to check shutter status: %d\n", ret);
+ goto error;
+ }
+
+ ret = !(val & BIT(shift));
+
+ dev_dbg(priv->dev, "%s shutter is %s\n",
+ BIT(shift) == CS42L43_STATUS_MIC_SHUTTER_MUTE_MASK ? "Mic" : "Speaker",
+ ret ? "open" : "closed");
+
+error:
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return ret;
+}
+
+static int cs42l43_decim_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = cs42l43_shutter_get(priv, CS42L43_STATUS_MIC_SHUTTER_MUTE_SHIFT);
+ if (ret < 0)
+ return ret;
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+ else
+ ret = cs42l43_dapm_get_volsw(kcontrol, ucontrol);
+
+ return ret;
+}
+
+static int cs42l43_spk_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = cs42l43_shutter_get(priv, CS42L43_STATUS_SPK_SHUTTER_MUTE_SHIFT);
+ if (ret < 0)
+ return ret;
+ else if (!ret)
+ ucontrol->value.integer.value[0] = ret;
+ else
+ ret = snd_soc_get_volsw(kcontrol, ucontrol);
+
+ return ret;
+}
+
+static int cs42l43_spk_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol);
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ int ret;
+
+ ret = snd_soc_put_volsw(kcontrol, ucontrol);
+ if (ret > 0)
+ cs42l43_spk_vu_sync(priv);
+
+ return ret;
+}
+
+static const struct snd_kcontrol_new cs42l43_controls[] = {
+ SOC_ENUM_EXT("Jack Override", cs42l43_jack_enum,
+ cs42l43_jack_get, cs42l43_jack_put),
+
+ SOC_DOUBLE_R_SX_TLV("ADC Volume", CS42L43_ADC_B_CTRL1, CS42L43_ADC_B_CTRL2,
+ CS42L43_ADC_PGA_GAIN_SHIFT,
+ 0xF, 5, cs42l43_adc_tlv),
+
+ SOC_DOUBLE("PDM1 Invert Switch", CS42L43_DMIC_PDM_CTRL,
+ CS42L43_PDM1L_INV_SHIFT, CS42L43_PDM1R_INV_SHIFT, 1, 0),
+ SOC_DOUBLE("PDM2 Invert Switch", CS42L43_DMIC_PDM_CTRL,
+ CS42L43_PDM2L_INV_SHIFT, CS42L43_PDM2R_INV_SHIFT, 1, 0),
+ SOC_ENUM("PDM1 Clock", cs42l43_pdm1_clk),
+ SOC_ENUM("PDM2 Clock", cs42l43_pdm2_clk),
+
+ SOC_SINGLE("Decimator 1 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 2 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 3 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 4 WNF Switch", CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_WNF_EN_SHIFT, 1, 0),
+
+ SOC_ENUM("Decimator 1 WNF Corner Frequency", cs42l43_dec1_wnf_corner),
+ SOC_ENUM("Decimator 2 WNF Corner Frequency", cs42l43_dec2_wnf_corner),
+ SOC_ENUM("Decimator 3 WNF Corner Frequency", cs42l43_dec3_wnf_corner),
+ SOC_ENUM("Decimator 4 WNF Corner Frequency", cs42l43_dec4_wnf_corner),
+
+ SOC_SINGLE("Decimator 1 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL1,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 2 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL2,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 3 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL3,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Decimator 4 HPF Switch", CS42L43_DECIM_HPF_WNF_CTRL4,
+ CS42L43_DECIM_HPF_EN_SHIFT, 1, 0),
+
+ SOC_ENUM("Decimator 1 HPF Corner Frequency", cs42l43_dec1_hpf_corner),
+ SOC_ENUM("Decimator 2 HPF Corner Frequency", cs42l43_dec2_hpf_corner),
+ SOC_ENUM("Decimator 3 HPF Corner Frequency", cs42l43_dec3_hpf_corner),
+ SOC_ENUM("Decimator 4 HPF Corner Frequency", cs42l43_dec4_hpf_corner),
+
+ SOC_SINGLE_TLV("Decimator 1 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 1 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM1_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 2 Volume", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 2 Switch", CS42L43_DECIM_VOL_CTRL_CH1_CH2,
+ CS42L43_DECIM2_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 3 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 3 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM3_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+ SOC_SINGLE_TLV("Decimator 4 Volume", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_VOL_SHIFT, 0xBF, 0, cs42l43_dec_tlv),
+ SOC_SINGLE_EXT("Decimator 4 Switch", CS42L43_DECIM_VOL_CTRL_CH3_CH4,
+ CS42L43_DECIM4_MUTE_SHIFT, 1, 1,
+ cs42l43_decim_get, cs42l43_dapm_put_volsw),
+
+ SOC_ENUM_EXT("Decimator 1 Ramp Up", cs42l43_dec1_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 1 Ramp Down", cs42l43_dec1_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 2 Ramp Up", cs42l43_dec2_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 2 Ramp Down", cs42l43_dec2_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 3 Ramp Up", cs42l43_dec3_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 3 Ramp Down", cs42l43_dec3_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 4 Ramp Up", cs42l43_dec4_ramp_up,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+ SOC_ENUM_EXT("Decimator 4 Ramp Down", cs42l43_dec4_ramp_down,
+ cs42l43_dapm_get_enum, cs42l43_dapm_put_enum),
+
+ SOC_DOUBLE_R_EXT("Speaker Digital Switch",
+ CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2,
+ CS42L43_AMP_MUTE_SHIFT, 1, 1,
+ cs42l43_spk_get, cs42l43_spk_put),
+
+ SOC_DOUBLE_R_EXT_TLV("Speaker Digital Volume",
+ CS42L43_INTP_VOLUME_CTRL1, CS42L43_INTP_VOLUME_CTRL2,
+ CS42L43_AMP_VOL_SHIFT,
+ 0xBF, 0, snd_soc_get_volsw, cs42l43_spk_put,
+ cs42l43_speaker_tlv),
+
+ SOC_ENUM("Speaker Ramp Up", cs42l43_speaker_ramp_up),
+ SOC_ENUM("Speaker Ramp Down", cs42l43_speaker_ramp_down),
+
+ CS42L43_MIXER_VOLUMES("Speaker L", CS42L43_AMP1MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("Speaker R", CS42L43_AMP2MIX_INPUT1),
+
+ SOC_DOUBLE_SX_TLV("Headphone Digital Volume", CS42L43_HPPATHVOL,
+ CS42L43_AMP3_PATH_VOL_SHIFT, CS42L43_AMP4_PATH_VOL_SHIFT,
+ 0x11B, 229, cs42l43_headphone_tlv),
+
+ SOC_DOUBLE("Headphone Invert Switch", CS42L43_DACCNFG1,
+ CS42L43_AMP3_INV_SHIFT, CS42L43_AMP4_INV_SHIFT, 1, 0),
+
+ SOC_SINGLE("Headphone Zero Cross Switch", CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_ZC_SHIFT, 1, 0),
+ SOC_SINGLE("Headphone Ramp Switch", CS42L43_PGAVOL,
+ CS42L43_HP_PATH_VOL_SFT_SHIFT, 1, 0),
+ SOC_ENUM("Headphone Ramp Rate", cs42l43_headphone_ramp),
+
+ CS42L43_MIXER_VOLUMES("Headphone L", CS42L43_AMP3MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("Headphone R", CS42L43_AMP4MIX_INPUT1),
+
+ SOC_ENUM("Tone 1 Frequency", cs42l43_tone1_freq),
+ SOC_ENUM("Tone 2 Frequency", cs42l43_tone2_freq),
+
+ SOC_DOUBLE_EXT("EQ Switch",
+ CS42L43_MUTE_EQ_IN0, CS42L43_MUTE_EQ_CH1_SHIFT,
+ CS42L43_MUTE_EQ_CH2_SHIFT, 1, 1,
+ cs42l43_dapm_get_volsw, cs42l43_dapm_put_volsw),
+
+ SND_SOC_BYTES_E("EQ Coefficients", 0, CS42L43_N_EQ_COEFFS,
+ cs42l43_eq_get, cs42l43_eq_put),
+
+ CS42L43_MIXER_VOLUMES("EQ1", CS42L43_EQ1MIX_INPUT1),
+ CS42L43_MIXER_VOLUMES("EQ2", CS42L43_EQ2MIX_INPUT1),
+};
+
+static int cs42l43_eq_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int val;
+ int i, ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0,
+ CS42L43_WRITE_MODE_MASK, CS42L43_WRITE_MODE_MASK);
+
+ for (i = 0; i < CS42L43_N_EQ_COEFFS; i++)
+ regmap_write(cs42l43->regmap, CS42L43_COEFF_DATA_IN0,
+ priv->eq_coeffs[i]);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_COEFF_RD_WR0,
+ CS42L43_WRITE_MODE_MASK, 0);
+
+ return 0;
+ case SND_SOC_DAPM_POST_PMU:
+ ret = regmap_read_poll_timeout(cs42l43->regmap, CS42L43_INIT_DONE0,
+ val, (val & CS42L43_INITIALIZE_DONE_MASK),
+ 2000, 10000);
+ if (ret)
+ dev_err(priv->dev, "Failed to start EQs: %d\n", ret);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_MUTE_EQ_IN0,
+ CS42L43_MUTE_EQ_CH1_MASK | CS42L43_MUTE_EQ_CH2_MASK, 0);
+ return ret;
+ default:
+ return 0;
+ }
+}
+
+struct cs42l43_pll_config {
+ unsigned int freq;
+
+ unsigned int div;
+ unsigned int mode;
+ unsigned int cal;
+};
+
+static const struct cs42l43_pll_config cs42l43_pll_configs[] = {
+ { 2400000, 0x50000000, 0x1, 0xA4 },
+ { 3000000, 0x40000000, 0x1, 0x83 },
+ { 3072000, 0x40000000, 0x3, 0x80 },
+};
+
+static int cs42l43_set_pll(struct cs42l43_codec *priv, unsigned int src,
+ unsigned int freq)
+{
+ struct cs42l43 *cs42l43 = priv->core;
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ if (priv->refclk_src == src && priv->refclk_freq == freq)
+ return 0;
+
+ if (regmap_test_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK)) {
+ dev_err(priv->dev, "PLL active, can't change configuration\n");
+ return -EBUSY;
+ }
+
+ switch (src) {
+ case CS42L43_SYSCLK_MCLK:
+ case CS42L43_SYSCLK_SDW:
+ dev_dbg(priv->dev, "Source PLL from %s at %uHz\n",
+ src ? "SoundWire" : "MCLK", freq);
+
+ priv->refclk_src = src;
+ priv->refclk_freq = freq;
+
+ return 0;
+ default:
+ dev_err(priv->dev, "Invalid PLL source: 0x%x\n", src);
+ return -EINVAL;
+ }
+}
+
+static int cs42l43_enable_pll(struct cs42l43_codec *priv)
+{
+ static const struct reg_sequence enable_seq[] = {
+ { CS42L43_OSC_DIV_SEL, 0x0, },
+ { CS42L43_MCLK_SRC_SEL, CS42L43_OSC_PLL_MCLK_SEL_MASK, 5, },
+ };
+ struct cs42l43 *cs42l43 = priv->core;
+ const struct cs42l43_pll_config *config = NULL;
+ unsigned int div = 0;
+ unsigned int freq = priv->refclk_freq;
+ unsigned long time_left;
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ if (priv->refclk_src == CS42L43_SYSCLK_SDW) {
+ if (!freq)
+ freq = cs42l43->sdw_freq;
+ else if (!cs42l43->sdw_freq)
+ cs42l43->sdw_freq = freq;
+ }
+
+ dev_dbg(priv->dev, "Enabling PLL at %uHz\n", freq);
+
+ while (freq > cs42l43_pll_configs[ARRAY_SIZE(cs42l43_pll_configs) - 1].freq) {
+ div++;
+ freq /= 2;
+ }
+
+ if (div <= CS42L43_PLL_REFCLK_DIV_MASK) {
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_pll_configs); i++) {
+ if (freq == cs42l43_pll_configs[i].freq) {
+ config = &cs42l43_pll_configs[i];
+ break;
+ }
+ }
+ }
+
+ if (!config) {
+ dev_err(priv->dev, "No suitable PLL config: 0x%x, %uHz\n", div, freq);
+ return -EINVAL;
+ }
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_DIV_MASK | CS42L43_PLL_REFCLK_SRC_MASK,
+ div << CS42L43_PLL_REFCLK_DIV_SHIFT |
+ priv->refclk_src << CS42L43_PLL_REFCLK_SRC_SHIFT);
+ regmap_write(cs42l43->regmap, CS42L43_FDIV_FRAC, config->div);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_MODE_BYPASS_500_MASK |
+ CS42L43_PLL_MODE_BYPASS_1029_MASK,
+ config->mode << CS42L43_PLL_MODE_BYPASS_1029_SHIFT);
+ regmap_update_bits(cs42l43->regmap, CS42L43_CAL_RATIO,
+ CS42L43_PLL_CAL_RATIO_MASK, config->cal);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, CS42L43_PLL_REFCLK_EN_MASK);
+
+ reinit_completion(&priv->pll_ready);
+
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_EN_MASK, CS42L43_PLL_EN_MASK);
+
+ time_left = wait_for_completion_timeout(&priv->pll_ready,
+ msecs_to_jiffies(CS42L43_PLL_TIMEOUT_MS));
+ if (!time_left) {
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG,
+ CS42L43_PLL_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, 0);
+
+ dev_err(priv->dev, "Timeout out waiting for PLL\n");
+ return -ETIMEDOUT;
+ }
+
+ if (priv->refclk_src == CS42L43_SYSCLK_SDW)
+ cs42l43->sdw_pll_active = true;
+
+ dev_dbg(priv->dev, "PLL locked in %ums\n", 200 - jiffies_to_msecs(time_left));
+
+ /*
+ * Reads are not allowed over Soundwire without OSC_DIV2_EN or the PLL,
+ * but you can not change to PLL with OSC_DIV2_EN set. So ensure the whole
+ * change over happens under the regmap lock to prevent any reads.
+ */
+ regmap_multi_reg_write(cs42l43->regmap, enable_seq, ARRAY_SIZE(enable_seq));
+
+ return 0;
+}
+
+static int cs42l43_disable_pll(struct cs42l43_codec *priv)
+{
+ static const struct reg_sequence disable_seq[] = {
+ { CS42L43_MCLK_SRC_SEL, 0x0, 5, },
+ { CS42L43_OSC_DIV_SEL, CS42L43_OSC_DIV2_EN_MASK, },
+ };
+ struct cs42l43 *cs42l43 = priv->core;
+
+ dev_dbg(priv->dev, "Disabling PLL\n");
+
+ lockdep_assert_held(&cs42l43->pll_lock);
+
+ regmap_multi_reg_write(cs42l43->regmap, disable_seq, ARRAY_SIZE(disable_seq));
+ regmap_update_bits(cs42l43->regmap, CS42L43_CTRL_REG, CS42L43_PLL_EN_MASK, 0);
+ regmap_update_bits(cs42l43->regmap, CS42L43_PLL_CONTROL,
+ CS42L43_PLL_REFCLK_EN_MASK, 0);
+
+ cs42l43->sdw_pll_active = false;
+
+ return 0;
+}
+
+static int cs42l43_pll_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ mutex_lock(&cs42l43->pll_lock);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->refclk_src == CS42L43_SYSCLK_MCLK) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(priv->dev, "Failed to enable MCLK: %d\n", ret);
+ break;
+ }
+ }
+
+ ret = cs42l43_enable_pll(priv);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ ret = cs42l43_disable_pll(priv);
+
+ if (priv->refclk_src == CS42L43_SYSCLK_MCLK)
+ clk_disable_unprepare(priv->mclk);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+
+ mutex_unlock(&cs42l43->pll_lock);
+
+ return ret;
+}
+
+static int cs42l43_dapm_wait_completion(struct completion *pmu, struct completion *pmd,
+ int event, int timeout_ms)
+{
+ unsigned long time_left;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ reinit_completion(pmu);
+ return 0;
+ case SND_SOC_DAPM_PRE_PMD:
+ reinit_completion(pmd);
+ return 0;
+ case SND_SOC_DAPM_POST_PMU:
+ time_left = wait_for_completion_timeout(pmu, msecs_to_jiffies(timeout_ms));
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ time_left = wait_for_completion_timeout(pmd, msecs_to_jiffies(timeout_ms));
+ break;
+ default:
+ return 0;
+ }
+
+ if (!time_left)
+ return -ETIMEDOUT;
+ else
+ return 0;
+}
+
+static int cs42l43_spkr_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ return cs42l43_dapm_wait_completion(&priv->spkr_startup,
+ &priv->spkr_shutdown, event,
+ CS42L43_SPK_TIMEOUT_MS);
+}
+
+static int cs42l43_spkl_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+
+ return cs42l43_dapm_wait_completion(&priv->spkl_startup,
+ &priv->spkl_shutdown, event,
+ CS42L43_SPK_TIMEOUT_MS);
+}
+
+static int cs42l43_hp_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val = 0;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = mask;
+ fallthrough;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->hp_ena &= ~mask;
+ priv->hp_ena |= val;
+
+ ret = cs42l43_dapm_wait_completion(&priv->hp_startup,
+ &priv->hp_shutdown, event,
+ CS42L43_HP_TIMEOUT_MS);
+ if (ret)
+ return ret;
+
+ if (!priv->load_detect_running)
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN8,
+ mask, val);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->load_detect_running)
+ break;
+
+ ret = cs42l43_dapm_wait_completion(&priv->hp_startup,
+ &priv->hp_shutdown, event,
+ CS42L43_HP_TIMEOUT_MS);
+ if (ret)
+ return ret;
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l43_mic_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int reg, ramp, mute;
+ unsigned int *val;
+ int ret;
+
+ switch (w->shift) {
+ case CS42L43_ADC1_EN_SHIFT:
+ case CS42L43_PDM1_DIN_L_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2;
+ ramp = CS42L43_DECIM1_VD_RAMP_MASK;
+ mute = CS42L43_DECIM1_MUTE_MASK;
+ val = &priv->decim_cache[0];
+ break;
+ case CS42L43_ADC2_EN_SHIFT:
+ case CS42L43_PDM1_DIN_R_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH1_CH2;
+ ramp = CS42L43_DECIM2_VD_RAMP_MASK;
+ mute = CS42L43_DECIM2_MUTE_MASK;
+ val = &priv->decim_cache[1];
+ break;
+ case CS42L43_PDM2_DIN_L_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4;
+ ramp = CS42L43_DECIM3_VD_RAMP_MASK;
+ mute = CS42L43_DECIM3_MUTE_MASK;
+ val = &priv->decim_cache[2];
+ break;
+ case CS42L43_PDM2_DIN_R_EN_SHIFT:
+ reg = CS42L43_DECIM_VOL_CTRL_CH3_CH4;
+ ramp = CS42L43_DECIM4_VD_RAMP_MASK;
+ mute = CS42L43_DECIM4_MUTE_MASK;
+ val = &priv->decim_cache[3];
+ break;
+ default:
+ dev_err(priv->dev, "Invalid microphone shift: %d\n", w->shift);
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ ret = regmap_read(cs42l43->regmap, reg, val);
+ if (ret) {
+ dev_err(priv->dev,
+ "Failed to cache decimator settings: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_update_bits(cs42l43->regmap, reg, mute | ramp, mute);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(cs42l43->regmap, reg, mute | ramp, *val);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l43_adc_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 cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ unsigned int mask = 1 << w->shift;
+ unsigned int val = 0;
+ int ret;
+
+ ret = cs42l43_mic_ev(w, kcontrol, event);
+ if (ret)
+ return ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ val = mask;
+ fallthrough;
+ case SND_SOC_DAPM_PRE_PMD:
+ priv->adc_ena &= ~mask;
+ priv->adc_ena |= val;
+
+ if (!priv->load_detect_running)
+ regmap_update_bits(cs42l43->regmap, CS42L43_BLOCK_EN3,
+ mask, val);
+ fallthrough;
+ default:
+ return 0;
+ }
+}
+
+static const struct snd_soc_dapm_widget cs42l43_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("PLL", SND_SOC_NOPM, 0, 0, cs42l43_pll_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("ADC1_IN1_P"),
+ SND_SOC_DAPM_INPUT("ADC1_IN1_N"),
+ SND_SOC_DAPM_INPUT("ADC1_IN2_P"),
+ SND_SOC_DAPM_INPUT("ADC1_IN2_N"),
+ SND_SOC_DAPM_INPUT("ADC2_IN_P"),
+ SND_SOC_DAPM_INPUT("ADC2_IN_N"),
+
+ SND_SOC_DAPM_INPUT("PDM1_DIN"),
+ SND_SOC_DAPM_INPUT("PDM2_DIN"),
+
+ SND_SOC_DAPM_MUX("ADC1 Input", SND_SOC_NOPM, 0, 0, &cs42l43_adc1_input_ctl),
+
+ SND_SOC_DAPM_PGA_E("ADC1", SND_SOC_NOPM, CS42L43_ADC1_EN_SHIFT, 0, NULL, 0,
+ cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("ADC2", SND_SOC_NOPM, CS42L43_ADC2_EN_SHIFT, 0, NULL, 0,
+ cs42l43_adc_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA_E("PDM1L", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_L_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM1R", CS42L43_BLOCK_EN3, CS42L43_PDM1_DIN_R_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM2L", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_L_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_PGA_E("PDM2R", CS42L43_BLOCK_EN3, CS42L43_PDM2_DIN_R_EN_SHIFT,
+ 0, NULL, 0, cs42l43_mic_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_MUX("Decimator 1 Mode", SND_SOC_NOPM, 0, 0,
+ &cs42l43_dec_mode_ctl[0]),
+ SND_SOC_DAPM_MUX("Decimator 2 Mode", SND_SOC_NOPM, 0, 0,
+ &cs42l43_dec_mode_ctl[1]),
+
+ SND_SOC_DAPM_PGA("Decimator 1", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 2", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 3", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Decimator 4", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY_S("FSYNC", 0, CS42L43_ASP_CTRL, CS42L43_ASP_FSYNC_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("BCLK", 1, CS42L43_ASP_CTRL, CS42L43_ASP_BCLK_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH5_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5,
+ CS42L43_ASP_TX_EN, CS42L43_ASP_TX_CH6_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_IN("ASPRX1", NULL, 0,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH1_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX2", NULL, 1,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH2_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX3", NULL, 2,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH3_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX4", NULL, 3,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH4_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX5", NULL, 4,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH5_EN_SHIFT, 0),
+ SND_SOC_DAPM_AIF_IN("ASPRX6", NULL, 5,
+ CS42L43_ASP_RX_EN, CS42L43_ASP_RX_CH6_EN_SHIFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP1TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX3", NULL, 2, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP1TX4", NULL, 3, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP2TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP2TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP3TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP3TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_OUT("DP4TX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DP4TX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP5RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP5RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP6RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP6RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_AIF_IN("DP7RX1", NULL, 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("DP7RX2", NULL, 1, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_REGULATOR_SUPPLY("vdd-amp", 0, 0),
+
+ SND_SOC_DAPM_PGA_E("AMP1", CS42L43_BLOCK_EN10, CS42L43_AMP1_EN_SHIFT, 0, NULL, 0,
+ cs42l43_spkl_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_E("AMP2", CS42L43_BLOCK_EN10, CS42L43_AMP2_EN_SHIFT, 0, NULL, 0,
+ cs42l43_spkr_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_OUTPUT("AMP1_OUT_P"),
+ SND_SOC_DAPM_OUTPUT("AMP1_OUT_N"),
+ SND_SOC_DAPM_OUTPUT("AMP2_OUT_P"),
+ SND_SOC_DAPM_OUTPUT("AMP2_OUT_N"),
+
+ SND_SOC_DAPM_PGA("SPDIF", CS42L43_BLOCK_EN11, CS42L43_SPDIF_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_OUTPUT("SPDIF_TX"),
+
+ SND_SOC_DAPM_PGA_E("HP", SND_SOC_NOPM, CS42L43_HP_EN_SHIFT, 0, NULL, 0,
+ cs42l43_hp_ev, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_OUTPUT("AMP3_OUT"),
+ SND_SOC_DAPM_OUTPUT("AMP4_OUT"),
+
+ SND_SOC_DAPM_SIGGEN("Tone"),
+ SND_SOC_DAPM_SUPPLY("Tone Generator", CS42L43_BLOCK_EN9, CS42L43_TONE_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 1", CS42L43_TONE_CH1_CTRL,
+ CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0),
+ SND_SOC_DAPM_REG(snd_soc_dapm_pga, "Tone 2", CS42L43_TONE_CH2_CTRL,
+ CS42L43_TONE_SEL_SHIFT, CS42L43_TONE_SEL_MASK, 0xA, 0),
+
+ SND_SOC_DAPM_SUPPLY("ISRC1", CS42L43_BLOCK_EN5, CS42L43_ISRC1_BANK_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ISRC2", CS42L43_BLOCK_EN5, CS42L43_ISRC2_BANK_EN_SHIFT,
+ 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ISRC1INT2", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1INT1", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1DEC2", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC1DEC1", CS42L43_ISRC1_CTRL,
+ CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ISRC2INT2", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2INT1", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2DEC2", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ISRC2DEC1", CS42L43_ISRC2_CTRL,
+ CS42L43_ISRC_DEC1_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("ASRC_INT", CS42L43_BLOCK_EN4,
+ CS42L43_ASRC_INT_BANK_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ASRC_DEC", CS42L43_BLOCK_EN4,
+ CS42L43_ASRC_DEC_BANK_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_PGA("ASRC_INT1", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT2", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT3", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT3_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_INT4", CS42L43_ASRC_INT_ENABLES,
+ CS42L43_ASRC_INT4_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC1", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC1_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC2", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC2_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC3", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC3_EN_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ASRC_DEC4", CS42L43_ASRC_DEC_ENABLES,
+ CS42L43_ASRC_DEC4_EN_SHIFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_SUPPLY("EQ Clock", CS42L43_BLOCK_EN7, CS42L43_EQ_EN_SHIFT,
+ 0, NULL, 0),
+ SND_SOC_DAPM_PGA_E("EQ", CS42L43_START_EQZ0, CS42L43_START_FILTER_SHIFT,
+ 0, NULL, 0, cs42l43_eq_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SUPPLY("Mixer Core", CS42L43_BLOCK_EN6, CS42L43_MIXER_EN_SHIFT,
+ 0, NULL, 0),
+ CS42L43_DAPM_MUX("ASPTX1", asptx1),
+ CS42L43_DAPM_MUX("ASPTX2", asptx2),
+ CS42L43_DAPM_MUX("ASPTX3", asptx3),
+ CS42L43_DAPM_MUX("ASPTX4", asptx4),
+ CS42L43_DAPM_MUX("ASPTX5", asptx5),
+ CS42L43_DAPM_MUX("ASPTX6", asptx6),
+
+ CS42L43_DAPM_MUX("DP1TX1", dp1tx1),
+ CS42L43_DAPM_MUX("DP1TX2", dp1tx2),
+ CS42L43_DAPM_MUX("DP1TX3", dp1tx3),
+ CS42L43_DAPM_MUX("DP1TX4", dp1tx4),
+ CS42L43_DAPM_MUX("DP2TX1", dp2tx1),
+ CS42L43_DAPM_MUX("DP2TX2", dp2tx2),
+ CS42L43_DAPM_MUX("DP3TX1", dp3tx1),
+ CS42L43_DAPM_MUX("DP3TX2", dp3tx2),
+ CS42L43_DAPM_MUX("DP4TX1", dp4tx1),
+ CS42L43_DAPM_MUX("DP4TX2", dp4tx2),
+
+ CS42L43_DAPM_MUX("ASRC INT1", asrcint1),
+ CS42L43_DAPM_MUX("ASRC INT2", asrcint2),
+ CS42L43_DAPM_MUX("ASRC INT3", asrcint3),
+ CS42L43_DAPM_MUX("ASRC INT4", asrcint4),
+ CS42L43_DAPM_MUX("ASRC DEC1", asrcdec1),
+ CS42L43_DAPM_MUX("ASRC DEC2", asrcdec2),
+ CS42L43_DAPM_MUX("ASRC DEC3", asrcdec3),
+ CS42L43_DAPM_MUX("ASRC DEC4", asrcdec4),
+
+ CS42L43_DAPM_MUX("ISRC1INT1", isrc1int1),
+ CS42L43_DAPM_MUX("ISRC1INT2", isrc1int2),
+ CS42L43_DAPM_MUX("ISRC1DEC1", isrc1dec1),
+ CS42L43_DAPM_MUX("ISRC1DEC2", isrc1dec2),
+ CS42L43_DAPM_MUX("ISRC2INT1", isrc2int1),
+ CS42L43_DAPM_MUX("ISRC2INT2", isrc2int2),
+ CS42L43_DAPM_MUX("ISRC2DEC1", isrc2dec1),
+ CS42L43_DAPM_MUX("ISRC2DEC2", isrc2dec2),
+
+ CS42L43_DAPM_MUX("SPDIF1", spdif1),
+ CS42L43_DAPM_MUX("SPDIF2", spdif2),
+
+ CS42L43_DAPM_MIXER("EQ1", eq1),
+ CS42L43_DAPM_MIXER("EQ2", eq2),
+
+ CS42L43_DAPM_MIXER("Speaker L", amp1),
+ CS42L43_DAPM_MIXER("Speaker R", amp2),
+
+ CS42L43_DAPM_MIXER("Headphone L", amp3),
+ CS42L43_DAPM_MIXER("Headphone R", amp4),
+};
+
+static const struct snd_soc_dapm_route cs42l43_routes[] = {
+ { "ADC1_IN1_P", NULL, "PLL" },
+ { "ADC1_IN1_N", NULL, "PLL" },
+ { "ADC1_IN2_P", NULL, "PLL" },
+ { "ADC1_IN2_N", NULL, "PLL" },
+ { "ADC2_IN_P", NULL, "PLL" },
+ { "ADC2_IN_N", NULL, "PLL" },
+ { "PDM1_DIN", NULL, "PLL" },
+ { "PDM2_DIN", NULL, "PLL" },
+ { "AMP1_OUT_P", NULL, "PLL" },
+ { "AMP1_OUT_N", NULL, "PLL" },
+ { "AMP2_OUT_P", NULL, "PLL" },
+ { "AMP2_OUT_N", NULL, "PLL" },
+ { "SPDIF_TX", NULL, "PLL" },
+ { "HP", NULL, "PLL" },
+ { "AMP3_OUT", NULL, "PLL" },
+ { "AMP4_OUT", NULL, "PLL" },
+ { "Tone 1", NULL, "PLL" },
+ { "Tone 2", NULL, "PLL" },
+ { "ASP Playback", NULL, "PLL" },
+ { "ASP Capture", NULL, "PLL" },
+ { "DP1 Capture", NULL, "PLL" },
+ { "DP2 Capture", NULL, "PLL" },
+ { "DP3 Capture", NULL, "PLL" },
+ { "DP4 Capture", NULL, "PLL" },
+ { "DP5 Playback", NULL, "PLL" },
+ { "DP6 Playback", NULL, "PLL" },
+ { "DP7 Playback", NULL, "PLL" },
+
+ { "ADC1 Input", "IN1", "ADC1_IN1_P" },
+ { "ADC1 Input", "IN1", "ADC1_IN1_N" },
+ { "ADC1 Input", "IN2", "ADC1_IN2_P" },
+ { "ADC1 Input", "IN2", "ADC1_IN2_N" },
+
+ { "ADC1", NULL, "ADC1 Input" },
+ { "ADC2", NULL, "ADC2_IN_P" },
+ { "ADC2", NULL, "ADC2_IN_N" },
+
+ { "PDM1L", NULL, "PDM1_DIN" },
+ { "PDM1R", NULL, "PDM1_DIN" },
+ { "PDM2L", NULL, "PDM2_DIN" },
+ { "PDM2R", NULL, "PDM2_DIN" },
+
+ { "Decimator 1 Mode", "PDM", "PDM1L" },
+ { "Decimator 1 Mode", "ADC", "ADC1" },
+ { "Decimator 2 Mode", "PDM", "PDM1R" },
+ { "Decimator 2 Mode", "ADC", "ADC2" },
+
+ { "Decimator 1", NULL, "Decimator 1 Mode" },
+ { "Decimator 2", NULL, "Decimator 2 Mode" },
+ { "Decimator 3", NULL, "PDM2L" },
+ { "Decimator 4", NULL, "PDM2R" },
+
+ { "ASP Capture", NULL, "ASPTX1" },
+ { "ASP Capture", NULL, "ASPTX2" },
+ { "ASP Capture", NULL, "ASPTX3" },
+ { "ASP Capture", NULL, "ASPTX4" },
+ { "ASP Capture", NULL, "ASPTX5" },
+ { "ASP Capture", NULL, "ASPTX6" },
+ { "ASPTX1", NULL, "BCLK" },
+ { "ASPTX2", NULL, "BCLK" },
+ { "ASPTX3", NULL, "BCLK" },
+ { "ASPTX4", NULL, "BCLK" },
+ { "ASPTX5", NULL, "BCLK" },
+ { "ASPTX6", NULL, "BCLK" },
+
+ { "ASPRX1", NULL, "ASP Playback" },
+ { "ASPRX2", NULL, "ASP Playback" },
+ { "ASPRX3", NULL, "ASP Playback" },
+ { "ASPRX4", NULL, "ASP Playback" },
+ { "ASPRX5", NULL, "ASP Playback" },
+ { "ASPRX6", NULL, "ASP Playback" },
+ { "ASPRX1", NULL, "BCLK" },
+ { "ASPRX2", NULL, "BCLK" },
+ { "ASPRX3", NULL, "BCLK" },
+ { "ASPRX4", NULL, "BCLK" },
+ { "ASPRX5", NULL, "BCLK" },
+ { "ASPRX6", NULL, "BCLK" },
+
+ { "DP1 Capture", NULL, "DP1TX1" },
+ { "DP1 Capture", NULL, "DP1TX2" },
+ { "DP1 Capture", NULL, "DP1TX3" },
+ { "DP1 Capture", NULL, "DP1TX4" },
+
+ { "DP2 Capture", NULL, "DP2TX1" },
+ { "DP2 Capture", NULL, "DP2TX2" },
+
+ { "DP3 Capture", NULL, "DP3TX1" },
+ { "DP3 Capture", NULL, "DP3TX2" },
+
+ { "DP4 Capture", NULL, "DP4TX1" },
+ { "DP4 Capture", NULL, "DP4TX2" },
+
+ { "DP5RX1", NULL, "DP5 Playback" },
+ { "DP5RX2", NULL, "DP5 Playback" },
+
+ { "DP6RX1", NULL, "DP6 Playback" },
+ { "DP6RX2", NULL, "DP6 Playback" },
+
+ { "DP7RX1", NULL, "DP7 Playback" },
+ { "DP7RX2", NULL, "DP7 Playback" },
+
+ { "AMP1", NULL, "vdd-amp" },
+ { "AMP2", NULL, "vdd-amp" },
+
+ { "AMP1_OUT_P", NULL, "AMP1" },
+ { "AMP1_OUT_N", NULL, "AMP1" },
+ { "AMP2_OUT_P", NULL, "AMP2" },
+ { "AMP2_OUT_N", NULL, "AMP2" },
+
+ { "SPDIF_TX", NULL, "SPDIF" },
+
+ { "AMP3_OUT", NULL, "HP" },
+ { "AMP4_OUT", NULL, "HP" },
+
+ { "Tone 1", NULL, "Tone" },
+ { "Tone 1", NULL, "Tone Generator" },
+ { "Tone 2", NULL, "Tone" },
+ { "Tone 2", NULL, "Tone Generator" },
+
+ { "ISRC1INT2", NULL, "ISRC1" },
+ { "ISRC1INT1", NULL, "ISRC1" },
+ { "ISRC1DEC2", NULL, "ISRC1" },
+ { "ISRC1DEC1", NULL, "ISRC1" },
+
+ { "ISRC2INT2", NULL, "ISRC2" },
+ { "ISRC2INT1", NULL, "ISRC2" },
+ { "ISRC2DEC2", NULL, "ISRC2" },
+ { "ISRC2DEC1", NULL, "ISRC2" },
+
+ { "ASRC_INT1", NULL, "ASRC_INT" },
+ { "ASRC_INT2", NULL, "ASRC_INT" },
+ { "ASRC_INT3", NULL, "ASRC_INT" },
+ { "ASRC_INT4", NULL, "ASRC_INT" },
+ { "ASRC_DEC1", NULL, "ASRC_DEC" },
+ { "ASRC_DEC2", NULL, "ASRC_DEC" },
+ { "ASRC_DEC3", NULL, "ASRC_DEC" },
+ { "ASRC_DEC4", NULL, "ASRC_DEC" },
+
+ { "EQ", NULL, "EQ Clock" },
+
+ CS42L43_MUX_ROUTES("ASPTX1", "ASPTX1"),
+ CS42L43_MUX_ROUTES("ASPTX2", "ASPTX2"),
+ CS42L43_MUX_ROUTES("ASPTX3", "ASPTX3"),
+ CS42L43_MUX_ROUTES("ASPTX4", "ASPTX4"),
+ CS42L43_MUX_ROUTES("ASPTX5", "ASPTX5"),
+ CS42L43_MUX_ROUTES("ASPTX6", "ASPTX6"),
+
+ CS42L43_MUX_ROUTES("DP1TX1", "DP1TX1"),
+ CS42L43_MUX_ROUTES("DP1TX2", "DP1TX2"),
+ CS42L43_MUX_ROUTES("DP1TX3", "DP1TX3"),
+ CS42L43_MUX_ROUTES("DP1TX4", "DP1TX4"),
+ CS42L43_MUX_ROUTES("DP2TX1", "DP2TX1"),
+ CS42L43_MUX_ROUTES("DP2TX2", "DP2TX2"),
+ CS42L43_MUX_ROUTES("DP3TX1", "DP3TX1"),
+ CS42L43_MUX_ROUTES("DP3TX2", "DP3TX2"),
+ CS42L43_MUX_ROUTES("DP4TX1", "DP4TX1"),
+ CS42L43_MUX_ROUTES("DP4TX2", "DP4TX2"),
+
+ CS42L43_MUX_ROUTES("ASRC INT1", "ASRC_INT1"),
+ CS42L43_MUX_ROUTES("ASRC INT2", "ASRC_INT2"),
+ CS42L43_MUX_ROUTES("ASRC INT3", "ASRC_INT3"),
+ CS42L43_MUX_ROUTES("ASRC INT4", "ASRC_INT4"),
+ CS42L43_MUX_ROUTES("ASRC DEC1", "ASRC_DEC1"),
+ CS42L43_MUX_ROUTES("ASRC DEC2", "ASRC_DEC2"),
+ CS42L43_MUX_ROUTES("ASRC DEC3", "ASRC_DEC3"),
+ CS42L43_MUX_ROUTES("ASRC DEC4", "ASRC_DEC4"),
+
+ CS42L43_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+ CS42L43_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+ CS42L43_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+ CS42L43_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+ CS42L43_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+ CS42L43_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+ CS42L43_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+ CS42L43_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+ CS42L43_MUX_ROUTES("SPDIF1", "SPDIF"),
+ CS42L43_MUX_ROUTES("SPDIF2", "SPDIF"),
+
+ CS42L43_MIXER_ROUTES("EQ1", "EQ"),
+ CS42L43_MIXER_ROUTES("EQ2", "EQ"),
+
+ CS42L43_MIXER_ROUTES("Speaker L", "AMP1"),
+ CS42L43_MIXER_ROUTES("Speaker R", "AMP2"),
+
+ CS42L43_MIXER_ROUTES("Headphone L", "HP"),
+ CS42L43_MIXER_ROUTES("Headphone R", "HP"),
+};
+
+static int cs42l43_set_sysclk(struct snd_soc_component *component, int clk_id,
+ int src, unsigned int freq, int dir)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+ int ret;
+
+ mutex_lock(&cs42l43->pll_lock);
+ ret = cs42l43_set_pll(priv, src, freq);
+ mutex_unlock(&cs42l43->pll_lock);
+
+ return ret;
+}
+
+static int cs42l43_component_probe(struct snd_soc_component *component)
+{
+ struct cs42l43_codec *priv = snd_soc_component_get_drvdata(component);
+ struct cs42l43 *cs42l43 = priv->core;
+
+ snd_soc_component_init_regmap(component, cs42l43->regmap);
+
+ cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->tx_slots);
+ cs42l43_mask_to_slots(priv, CS42L43_DEFAULT_SLOTS, priv->rx_slots);
+
+ priv->component = component;
+ priv->constraint = cs42l43_constraint;
+
+ return 0;
+}
+
+static const struct snd_soc_component_driver cs42l43_component_drv = {
+ .name = "cs42l43-codec",
+
+ .probe = cs42l43_component_probe,
+ .set_sysclk = cs42l43_set_sysclk,
+ .set_jack = cs42l43_set_jack,
+
+ .endianness = 1,
+
+ .controls = cs42l43_controls,
+ .num_controls = ARRAY_SIZE(cs42l43_controls),
+ .dapm_widgets = cs42l43_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l43_widgets),
+ .dapm_routes = cs42l43_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42l43_routes),
+};
+
+struct cs42l43_irq {
+ unsigned int irq;
+ const char *name;
+ irq_handler_t handler;
+};
+
+static const struct cs42l43_irq cs42l43_irqs[] = {
+ { CS42L43_PLL_LOST_LOCK, "pll lost lock", cs42l43_pll_lost_lock },
+ { CS42L43_PLL_READY, "pll ready", cs42l43_pll_ready },
+ { CS42L43_HP_STARTUP_DONE, "hp startup", cs42l43_hp_startup },
+ { CS42L43_HP_SHUTDOWN_DONE, "hp shutdown", cs42l43_hp_shutdown },
+ { CS42L43_HSDET_DONE, "type detect", cs42l43_type_detect },
+ { CS42L43_TIPSENSE_UNPLUG_PDET, "tip sense unplug", cs42l43_tip_sense },
+ { CS42L43_TIPSENSE_PLUG_PDET, "tip sense plug", cs42l43_tip_sense },
+ { CS42L43_DC_DETECT1_TRUE, "button press", cs42l43_button_press },
+ { CS42L43_DC_DETECT1_FALSE, "button release", cs42l43_button_release },
+ { CS42L43_HSBIAS_CLAMPED, "hsbias detect clamp", cs42l43_bias_detect_clamp },
+ { CS42L43_AMP2_CLK_STOP_FAULT, "spkr clock stop", cs42l43_spkr_clock_stop },
+ { CS42L43_AMP1_CLK_STOP_FAULT, "spkl clock stop", cs42l43_spkl_clock_stop },
+ { CS42L43_AMP2_VDDSPK_FAULT, "spkr brown out", cs42l43_spkr_brown_out },
+ { CS42L43_AMP1_VDDSPK_FAULT, "spkl brown out", cs42l43_spkl_brown_out },
+ { CS42L43_AMP2_SHUTDOWN_DONE, "spkr shutdown", cs42l43_spkr_shutdown },
+ { CS42L43_AMP1_SHUTDOWN_DONE, "spkl shutdown", cs42l43_spkl_shutdown },
+ { CS42L43_AMP2_STARTUP_DONE, "spkr startup", cs42l43_spkr_startup },
+ { CS42L43_AMP1_STARTUP_DONE, "spkl startup", cs42l43_spkl_startup },
+ { CS42L43_AMP2_THERM_SHDN, "spkr thermal shutdown", cs42l43_spkr_therm_shutdown },
+ { CS42L43_AMP1_THERM_SHDN, "spkl thermal shutdown", cs42l43_spkl_therm_shutdown },
+ { CS42L43_AMP2_THERM_WARN, "spkr thermal warning", cs42l43_spkr_therm_warm },
+ { CS42L43_AMP1_THERM_WARN, "spkl thermal warning", cs42l43_spkl_therm_warm },
+ { CS42L43_AMP2_SCDET, "spkr short circuit", cs42l43_spkr_sc_detect },
+ { CS42L43_AMP1_SCDET, "spkl short circuit", cs42l43_spkl_sc_detect },
+ { CS42L43_HP_ILIMIT, "hp ilimit", cs42l43_hp_ilimit },
+ { CS42L43_HP_LOADDET_DONE, "load detect done", cs42l43_load_detect },
+};
+
+static int cs42l43_request_irq(struct cs42l43_codec *priv,
+ struct irq_domain *dom, const char * const name,
+ unsigned int irq, irq_handler_t handler)
+{
+ int ret;
+
+ ret = irq_create_mapping(dom, irq);
+ if (ret < 0)
+ return dev_err_probe(priv->dev, ret, "Failed to map IRQ %s\n", name);
+
+ dev_dbg(priv->dev, "Request IRQ %d for %s\n", ret, name);
+
+ ret = devm_request_threaded_irq(priv->dev, ret, NULL, handler, IRQF_ONESHOT,
+ name, priv);
+ if (ret)
+ return dev_err_probe(priv->dev, ret, "Failed to request IRQ %s\n", name);
+
+ return 0;
+}
+
+static int cs42l43_shutter_irq(struct cs42l43_codec *priv,
+ struct irq_domain *dom, unsigned int shutter,
+ const char * const open_name,
+ const char * const close_name,
+ irq_handler_t handler)
+{
+ unsigned int open_irq, close_irq;
+ int ret;
+
+ switch (shutter) {
+ case 0x1:
+ dev_warn(priv->dev, "Manual shutters, notifications not available\n");
+ return 0;
+ case 0x2:
+ open_irq = CS42L43_GPIO1_RISE;
+ close_irq = CS42L43_GPIO1_FALL;
+ break;
+ case 0x4:
+ open_irq = CS42L43_GPIO2_RISE;
+ close_irq = CS42L43_GPIO2_FALL;
+ break;
+ case 0x8:
+ open_irq = CS42L43_GPIO3_RISE;
+ close_irq = CS42L43_GPIO3_FALL;
+ break;
+ default:
+ return 0;
+ }
+
+ ret = cs42l43_request_irq(priv, dom, close_name, close_irq, handler);
+ if (ret)
+ return ret;
+
+ return cs42l43_request_irq(priv, dom, open_name, open_irq, handler);
+}
+
+static int cs42l43_codec_probe(struct platform_device *pdev)
+{
+ struct cs42l43 *cs42l43 = dev_get_drvdata(pdev->dev.parent);
+ struct cs42l43_codec *priv;
+ struct irq_domain *dom;
+ unsigned int val;
+ int i, ret;
+
+ dom = irq_find_matching_fwnode(dev_fwnode(cs42l43->dev), DOMAIN_BUS_ANY);
+ if (!dom)
+ return -EPROBE_DEFER;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ priv->dev = &pdev->dev;
+ priv->core = cs42l43;
+
+ platform_set_drvdata(pdev, priv);
+
+ mutex_init(&priv->jack_lock);
+ mutex_init(&priv->spk_vu_lock);
+
+ init_completion(&priv->hp_startup);
+ init_completion(&priv->hp_shutdown);
+ init_completion(&priv->spkr_shutdown);
+ init_completion(&priv->spkl_shutdown);
+ init_completion(&priv->spkr_startup);
+ init_completion(&priv->spkl_startup);
+ init_completion(&priv->pll_ready);
+ init_completion(&priv->type_detect);
+ init_completion(&priv->load_detect);
+
+ INIT_DELAYED_WORK(&priv->tip_sense_work, cs42l43_tip_sense_work);
+ INIT_DELAYED_WORK(&priv->bias_sense_timeout, cs42l43_bias_sense_timeout);
+ INIT_DELAYED_WORK(&priv->button_press_work, cs42l43_button_press_work);
+ INIT_WORK(&priv->button_release_work, cs42l43_button_release_work);
+
+ pm_runtime_set_autosuspend_delay(priv->dev, 100);
+ pm_runtime_use_autosuspend(priv->dev);
+ pm_runtime_set_active(priv->dev);
+ pm_runtime_get_noresume(priv->dev);
+ devm_pm_runtime_enable(priv->dev);
+
+ for (i = 0; i < ARRAY_SIZE(cs42l43_irqs); i++) {
+ ret = cs42l43_request_irq(priv, dom, cs42l43_irqs[i].name,
+ cs42l43_irqs[i].irq, cs42l43_irqs[i].handler);
+ if (ret)
+ goto err_pm;
+ }
+
+ ret = regmap_read(cs42l43->regmap, CS42L43_SHUTTER_CONTROL, &val);
+ if (ret) {
+ dev_err(priv->dev, "Failed to check shutter source: %d\n", ret);
+ goto err_pm;
+ }
+
+ ret = cs42l43_shutter_irq(priv, dom, val & CS42L43_MIC_SHUTTER_CFG_MASK,
+ "mic shutter open", "mic shutter close",
+ cs42l43_mic_shutter);
+ if (ret)
+ goto err_pm;
+
+ ret = cs42l43_shutter_irq(priv, dom, (val & CS42L43_SPK_SHUTTER_CFG_MASK) >>
+ CS42L43_SPK_SHUTTER_CFG_SHIFT,
+ "spk shutter open", "spk shutter close",
+ cs42l43_spk_shutter);
+ if (ret)
+ goto err_pm;
+
+ // Don't use devm as we need to get against the MFD device
+ priv->mclk = clk_get_optional(cs42l43->dev, "mclk");
+ if (IS_ERR(priv->mclk)) {
+ dev_err_probe(priv->dev, PTR_ERR(priv->mclk), "Failed to get mclk\n");
+ goto err_pm;
+ }
+
+ ret = devm_snd_soc_register_component(priv->dev, &cs42l43_component_drv,
+ cs42l43_dais, ARRAY_SIZE(cs42l43_dais));
+ if (ret) {
+ dev_err_probe(priv->dev, ret, "Failed to register component\n");
+ goto err_clk;
+ }
+
+ pm_runtime_mark_last_busy(priv->dev);
+ pm_runtime_put_autosuspend(priv->dev);
+
+ return 0;
+
+err_clk:
+ clk_put(priv->mclk);
+err_pm:
+ pm_runtime_put_sync(priv->dev);
+
+ return ret;
+}
+
+static int cs42l43_codec_remove(struct platform_device *pdev)
+{
+ struct cs42l43_codec *priv = platform_get_drvdata(pdev);
+
+ clk_put(priv->mclk);
+
+ return 0;
+}
+
+static int cs42l43_codec_runtime_resume(struct device *dev)
+{
+ struct cs42l43_codec *priv = dev_get_drvdata(dev);
+
+ dev_dbg(priv->dev, "Runtime resume\n");
+
+ // Toggle the speaker volume update incase the speaker volume was synced
+ cs42l43_spk_vu_sync(priv);
+
+ return 0;
+}
+
+DEFINE_RUNTIME_DEV_PM_OPS(cs42l43_codec_pm_ops, NULL,
+ cs42l43_codec_runtime_resume, NULL);
+
+static const struct platform_device_id cs42l43_codec_id_table[] = {
+ { "cs42l43-codec", },
+ {}
+};
+MODULE_DEVICE_TABLE(platform, cs42l43_codec_id_table);
+
+static struct platform_driver cs42l43_codec_driver = {
+ .driver = {
+ .name = "cs42l43-codec",
+ .pm = &cs42l43_codec_pm_ops,
+ },
+
+ .probe = cs42l43_codec_probe,
+ .remove = cs42l43_codec_remove,
+ .id_table = cs42l43_codec_id_table,
+};
+module_platform_driver(cs42l43_codec_driver);
+
+MODULE_IMPORT_NS(SND_SOC_CS42L43);
+
+MODULE_DESCRIPTION("CS42L43 CODEC Driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l43.h b/sound/soc/codecs/cs42l43.h
new file mode 100644
index 000000000000..bf4f728eea3e
--- /dev/null
+++ b/sound/soc/codecs/cs42l43.h
@@ -0,0 +1,131 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CS42L43 CODEC driver internal data
+ *
+ * Copyright (C) 2022-2023 Cirrus Logic, Inc. and
+ * Cirrus Logic International Semiconductor Ltd.
+ */
+
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/types.h>
+#include <sound/cs42l43.h>
+#include <sound/pcm.h>
+#include <sound/soc-jack.h>
+
+#ifndef CS42L43_ASOC_INT_H
+#define CS42L43_ASOC_INT_H
+
+#define CS42L43_INTERNAL_SYSCLK 24576000
+#define CS42L43_DEFAULT_SLOTS 0x3F
+
+#define CS42L43_PLL_TIMEOUT_MS 200
+#define CS42L43_SPK_TIMEOUT_MS 100
+#define CS42L43_HP_TIMEOUT_MS 2000
+#define CS42L43_LOAD_TIMEOUT_MS 1000
+
+#define CS42L43_ASP_MAX_CHANNELS 6
+#define CS42L43_N_EQ_COEFFS 15
+
+#define CS42L43_N_BUTTONS 6
+
+struct cs42l43_codec {
+ struct device *dev;
+ struct cs42l43 *core;
+ struct snd_soc_component *component;
+
+ struct clk *mclk;
+
+ int n_slots;
+ int slot_width;
+ int tx_slots[CS42L43_ASP_MAX_CHANNELS];
+ int rx_slots[CS42L43_ASP_MAX_CHANNELS];
+ struct snd_pcm_hw_constraint_list constraint;
+
+ u32 eq_coeffs[CS42L43_N_EQ_COEFFS];
+
+ unsigned int refclk_src;
+ unsigned int refclk_freq;
+ struct completion pll_ready;
+
+ unsigned int decim_cache[4];
+ unsigned int adc_ena;
+ unsigned int hp_ena;
+
+ struct completion hp_startup;
+ struct completion hp_shutdown;
+ struct completion spkr_shutdown;
+ struct completion spkl_shutdown;
+ struct completion spkr_startup;
+ struct completion spkl_startup;
+ // Lock to ensure speaker VU updates don't clash
+ struct mutex spk_vu_lock;
+
+ // Lock for all jack detect operations
+ struct mutex jack_lock;
+ struct snd_soc_jack *jack_hp;
+
+ bool use_ring_sense;
+ unsigned int tip_debounce_ms;
+ unsigned int bias_low;
+ unsigned int bias_sense_ua;
+ unsigned int bias_ramp_ms;
+ unsigned int detect_us;
+ unsigned int buttons[CS42L43_N_BUTTONS];
+
+ struct delayed_work tip_sense_work;
+ struct delayed_work bias_sense_timeout;
+ struct delayed_work button_press_work;
+ struct work_struct button_release_work;
+ struct completion type_detect;
+ struct completion load_detect;
+
+ bool load_detect_running;
+ bool button_detect_running;
+ bool jack_present;
+ int jack_override;
+};
+
+#if IS_REACHABLE(CONFIG_SND_SOC_CS42L43_SDW)
+
+int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai);
+int cs42l43_sdw_remove_peripheral(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai);
+int cs42l43_sdw_set_stream(struct snd_soc_dai *dai, void *sdw_stream, int direction);
+
+#else
+
+static inline int cs42l43_sdw_add_peripheral(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ return -EINVAL;
+}
+
+#define cs42l43_sdw_remove_peripheral NULL
+#define cs42l43_sdw_set_stream NULL
+
+#endif
+
+int cs42l43_set_jack(struct snd_soc_component *component,
+ struct snd_soc_jack *jack, void *d);
+void cs42l43_bias_sense_timeout(struct work_struct *work);
+void cs42l43_tip_sense_work(struct work_struct *work);
+void cs42l43_button_press_work(struct work_struct *work);
+void cs42l43_button_release_work(struct work_struct *work);
+irqreturn_t cs42l43_bias_detect_clamp(int irq, void *data);
+irqreturn_t cs42l43_button_press(int irq, void *data);
+irqreturn_t cs42l43_button_release(int irq, void *data);
+irqreturn_t cs42l43_tip_sense(int irq, void *data);
+int cs42l43_jack_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+int cs42l43_jack_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol);
+
+extern const struct soc_enum cs42l43_jack_enum;
+
+#endif /* CS42L43_ASOC_INT_H */
diff --git a/sound/soc/codecs/cs42l51-i2c.c b/sound/soc/codecs/cs42l51-i2c.c
index e7db7bcd0296..5ed2ef83dcdb 100644
--- a/sound/soc/codecs/cs42l51-i2c.c
+++ b/sound/soc/codecs/cs42l51-i2c.c
@@ -19,7 +19,7 @@ static struct i2c_device_id cs42l51_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, cs42l51_i2c_id);
-const struct of_device_id cs42l51_of_match[] = {
+static const struct of_device_id cs42l51_of_match[] = {
{ .compatible = "cirrus,cs42l51", },
{ }
};
diff --git a/sound/soc/codecs/cs47l15.c b/sound/soc/codecs/cs47l15.c
index a6538dab6639..1245e1a4f2a5 100644
--- a/sound/soc/codecs/cs47l15.c
+++ b/sound/soc/codecs/cs47l15.c
@@ -1143,6 +1143,10 @@ static int cs47l15_set_fll(struct snd_soc_component *component, int fll_id,
}
}
+static const struct snd_soc_dai_ops cs47l15_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l15_dai[] = {
{
.name = "cs47l15-aif1",
@@ -1219,7 +1223,7 @@ static struct snd_soc_dai_driver cs47l15_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &cs47l15_dai_ops,
},
{
.name = "cs47l15-dsp-trace",
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index a07b621d463e..cfa1d34f6ebd 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -957,6 +957,10 @@ static int cs47l24_set_fll(struct snd_soc_component *component, int fll_id,
#define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const struct snd_soc_dai_ops cs47l24_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l24_dai[] = {
{
.name = "cs47l24-aif1",
@@ -1033,7 +1037,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &cs47l24_dai_ops,
},
{
.name = "cs47l24-dsp-voicectrl",
@@ -1054,7 +1058,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
.rates = CS47L24_RATES,
.formats = CS47L24_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &cs47l24_dai_ops,
},
{
.name = "cs47l24-dsp-trace",
diff --git a/sound/soc/codecs/cs47l35.c b/sound/soc/codecs/cs47l35.c
index c05c80c16c84..a953f2ede1ee 100644
--- a/sound/soc/codecs/cs47l35.c
+++ b/sound/soc/codecs/cs47l35.c
@@ -1348,6 +1348,10 @@ static int cs47l35_set_fll(struct snd_soc_component *component, int fll_id,
}
}
+static const struct snd_soc_dai_ops cs47l35_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l35_dai[] = {
{
.name = "cs47l35-aif1",
@@ -1462,7 +1466,7 @@ static struct snd_soc_dai_driver cs47l35_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = &snd_soc_new_compress,
+ .ops = &cs47l35_dai_ops,
},
{
.name = "cs47l35-dsp-voicectrl",
@@ -1483,7 +1487,7 @@ static struct snd_soc_dai_driver cs47l35_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = &snd_soc_new_compress,
+ .ops = &cs47l35_dai_ops,
},
{
.name = "cs47l35-dsp-trace",
diff --git a/sound/soc/codecs/cs47l85.c b/sound/soc/codecs/cs47l85.c
index dd7997a53e70..827685481859 100644
--- a/sound/soc/codecs/cs47l85.c
+++ b/sound/soc/codecs/cs47l85.c
@@ -2249,6 +2249,10 @@ static int cs47l85_set_fll(struct snd_soc_component *component, int fll_id,
}
}
+static const struct snd_soc_dai_ops cs47l85_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l85_dai[] = {
{
.name = "cs47l85-aif1",
@@ -2404,7 +2408,7 @@ static struct snd_soc_dai_driver cs47l85_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = &snd_soc_new_compress,
+ .ops = &cs47l85_dai_ops,
},
{
.name = "cs47l85-dsp-voicectrl",
@@ -2425,7 +2429,7 @@ static struct snd_soc_dai_driver cs47l85_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = &snd_soc_new_compress,
+ .ops = &cs47l85_dai_ops,
},
{
.name = "cs47l85-dsp-trace",
diff --git a/sound/soc/codecs/cs47l90.c b/sound/soc/codecs/cs47l90.c
index cdd5e7e20b5d..2c9a5372cf51 100644
--- a/sound/soc/codecs/cs47l90.c
+++ b/sound/soc/codecs/cs47l90.c
@@ -2168,6 +2168,10 @@ static int cs47l90_set_fll(struct snd_soc_component *component, int fll_id,
}
}
+static const struct snd_soc_dai_ops cs47l90_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l90_dai[] = {
{
.name = "cs47l90-aif1",
@@ -2323,7 +2327,7 @@ static struct snd_soc_dai_driver cs47l90_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = &snd_soc_new_compress,
+ .ops = &cs47l90_dai_ops,
},
{
.name = "cs47l90-dsp-voicectrl",
@@ -2344,7 +2348,7 @@ static struct snd_soc_dai_driver cs47l90_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = &snd_soc_new_compress,
+ .ops = &cs47l90_dai_ops,
},
{
.name = "cs47l90-dsp-trace",
diff --git a/sound/soc/codecs/cs47l92.c b/sound/soc/codecs/cs47l92.c
index bc4d311d4778..352deeaff1ca 100644
--- a/sound/soc/codecs/cs47l92.c
+++ b/sound/soc/codecs/cs47l92.c
@@ -1690,6 +1690,10 @@ static int cs47l92_set_fll(struct snd_soc_component *component, int fll_id,
}
}
+static const struct snd_soc_dai_ops cs47l92_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver cs47l92_dai[] = {
{
.name = "cs47l92-aif1",
@@ -1823,7 +1827,7 @@ static struct snd_soc_dai_driver cs47l92_dai[] = {
.rates = MADERA_RATES,
.formats = MADERA_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &cs47l92_dai_ops,
},
{
.name = "cs47l92-dsp-trace",
diff --git a/sound/soc/codecs/cx2072x.c b/sound/soc/codecs/cx2072x.c
index 082231088a26..f8b128084015 100644
--- a/sound/soc/codecs/cx2072x.c
+++ b/sound/soc/codecs/cx2072x.c
@@ -1546,6 +1546,14 @@ static int cx2072x_dsp_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops cx2072x_dai_ops2 = {
+ .probe = cx2072x_dsp_dai_probe,
+ .set_sysclk = cx2072x_set_dai_sysclk,
+ .set_fmt = cx2072x_set_dai_fmt,
+ .hw_params = cx2072x_hw_params,
+ .set_bclk_ratio = cx2072x_set_dai_bclk_ratio,
+};
+
#define CX2072X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE)
static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = {
@@ -1572,7 +1580,6 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = {
{ /* plabayck only, return echo reference to Conexant DSP chip */
.name = "cx2072x-dsp",
.id = CX2072X_DAI_DSP,
- .probe = cx2072x_dsp_dai_probe,
.playback = {
.stream_name = "DSP Playback",
.channels_min = 2,
@@ -1580,7 +1587,7 @@ static struct snd_soc_dai_driver soc_codec_cx2072x_dai[] = {
.rates = CX2072X_RATES_DSP,
.formats = CX2072X_FORMATS,
},
- .ops = &cx2072x_dai_ops,
+ .ops = &cx2072x_dai_ops2,
},
{ /* plabayck only, return echo reference through I2S TX */
.name = "cx2072x-aec",
diff --git a/sound/soc/codecs/es8326.c b/sound/soc/codecs/es8326.c
index a7fbb758eeee..6c263086c44d 100644
--- a/sound/soc/codecs/es8326.c
+++ b/sound/soc/codecs/es8326.c
@@ -41,6 +41,8 @@ struct es8326_priv {
bool calibrated;
int version;
+ int hp;
+ int jack_remove_retry;
};
static const SNDRV_CTL_TLVD_DECLARE_DB_SCALE(dac_vol_tlv, -9550, 50, 0);
@@ -158,20 +160,25 @@ static const struct snd_soc_dapm_route es8326_dapm_routes[] = {
{"HPOR", NULL, "RHPMIX"},
};
-static const struct regmap_range es8326_volatile_ranges[] = {
- regmap_reg_range(ES8326_HP_DETECT, ES8326_HP_DETECT),
-};
-
-static const struct regmap_access_table es8326_volatile_table = {
- .yes_ranges = es8326_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(es8326_volatile_ranges),
-};
+static bool es8326_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ES8326_HPL_OFFSET_INI:
+ case ES8326_HPR_OFFSET_INI:
+ case ES8326_HPDET_STA:
+ case ES8326_CTIA_OMTP_STA:
+ case ES8326_CSM_MUTE_STA:
+ return true;
+ default:
+ return false;
+ }
+}
static const struct regmap_config es8326_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
.max_register = 0xff,
- .volatile_table = &es8326_volatile_table,
+ .volatile_reg = es8326_volatile_register,
.cache_type = REGCACHE_RBTREE,
};
@@ -383,6 +390,7 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
{
struct snd_soc_component *component = dai->component;
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
+ unsigned int offset_l, offset_r;
if (mute) {
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
@@ -393,10 +401,16 @@ static int es8326_mute(struct snd_soc_dai *dai, int mute, int direction)
if (!es8326->calibrated) {
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_FORCE_CAL);
msleep(30);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l);
+ regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l);
+ regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
es8326->calibrated = true;
}
regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa0);
- regmap_write(es8326->regmap, ES8326_HP_VOL, 0x00);
+ regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80);
regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_ON);
regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE,
ES8326_MUTE_MASK, ~(ES8326_MUTE));
@@ -415,15 +429,17 @@ static int es8326_set_bias_level(struct snd_soc_component *codec,
ret = clk_prepare_enable(es8326->mclk);
if (ret)
return ret;
- regmap_write(es8326->regmap, ES8326_RESET, ES8326_PWRUP_SEQ_EN);
- regmap_write(es8326->regmap, ES8326_INTOUT_IO, 0x45);
+
+ regmap_write(es8326->regmap, ES8326_RESET, 0x9f);
+ msleep(20);
+ regmap_update_bits(es8326->regmap, ES8326_DAC_DSM, 0x01, 0x00);
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
(ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
- regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
- regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
- regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x02);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E);
regmap_write(es8326->regmap, ES8326_PGA_PDN, 0x40);
- regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0xAA);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x20);
regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
break;
case SND_SOC_BIAS_PREPARE:
@@ -432,15 +448,10 @@ static int es8326_set_bias_level(struct snd_soc_component *codec,
break;
case SND_SOC_BIAS_OFF:
clk_disable_unprepare(es8326->mclk);
- regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x11);
- regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_OFF);
- regmap_write(es8326->regmap, ES8326_PGA_PDN, 0xF8);
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x00);
- regmap_write(es8326->regmap, ES8326_INT_SOURCE, 0x08);
+ regmap_update_bits(es8326->regmap, ES8326_CLK_CTL, 0x20, 0x00);
regmap_write(es8326->regmap, ES8326_SDINOUT1_IO, ES8326_IO_INPUT);
- regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
- regmap_write(es8326->regmap, ES8326_RESET,
- ES8326_CODEC_RESET | ES8326_PWRUP_SEQ_EN);
break;
}
@@ -519,13 +530,14 @@ static void es8326_jack_button_handler(struct work_struct *work)
return;
mutex_lock(&es8326->lock);
- iface = snd_soc_component_read(comp, ES8326_HP_DETECT);
+ iface = snd_soc_component_read(comp, ES8326_HPDET_STA);
switch (iface) {
case 0x93:
/* pause button detected */
cur_button = SND_JACK_BTN_0;
break;
case 0x6f:
+ case 0x4b:
/* button volume up */
cur_button = SND_JACK_BTN_1;
break;
@@ -534,6 +546,7 @@ static void es8326_jack_button_handler(struct work_struct *work)
cur_button = SND_JACK_BTN_2;
break;
case 0x1e:
+ case 0xe2:
/* button released or not pressed */
cur_button = 0;
break;
@@ -543,20 +556,20 @@ static void es8326_jack_button_handler(struct work_struct *work)
if ((prev_button == cur_button) && (cur_button != 0)) {
press_count++;
- if (press_count > 10) {
- /* report a press every 500ms */
+ if (press_count > 3) {
+ /* report a press every 120ms */
snd_soc_jack_report(es8326->jack, cur_button,
SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2);
press_count = 0;
}
button_to_report = cur_button;
queue_delayed_work(system_wq, &es8326->button_press_work,
- msecs_to_jiffies(50));
+ msecs_to_jiffies(35));
} else if (prev_button != cur_button) {
/* mismatch, detect again */
prev_button = cur_button;
queue_delayed_work(system_wq, &es8326->button_press_work,
- msecs_to_jiffies(50));
+ msecs_to_jiffies(35));
} else {
/* released or no pressed */
if (button_to_report != 0) {
@@ -578,34 +591,98 @@ static void es8326_jack_detect_handler(struct work_struct *work)
unsigned int iface;
mutex_lock(&es8326->lock);
- iface = snd_soc_component_read(comp, ES8326_HP_DETECT);
+ iface = snd_soc_component_read(comp, ES8326_HPDET_STA);
dev_dbg(comp->dev, "gpio flag %#04x", iface);
+
+ if (es8326->jack_remove_retry == 1) {
+ if (iface & ES8326_HPINSERT_FLAG)
+ es8326->jack_remove_retry = 2;
+ else
+ es8326->jack_remove_retry = 0;
+
+ dev_dbg(comp->dev, "remove event check, set HPJACK_POL normal, cnt = %d\n",
+ es8326->jack_remove_retry);
+ /*
+ * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
+ */
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE,
+ ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ?
+ ~es8326->jack_pol : es8326->jack_pol));
+ goto exit;
+ }
+
if ((iface & ES8326_HPINSERT_FLAG) == 0) {
/* Jack unplugged or spurious IRQ */
- dev_dbg(comp->dev, "No headset detected");
+ dev_dbg(comp->dev, "No headset detected\n");
+ es8326_disable_micbias(es8326->component);
if (es8326->jack->status & SND_JACK_HEADPHONE) {
+ dev_dbg(comp->dev, "Report hp remove event\n");
snd_soc_jack_report(es8326->jack, 0, SND_JACK_HEADSET);
- snd_soc_component_write(comp, ES8326_ADC1_SRC, es8326->mic2_src);
- es8326_disable_micbias(comp);
+ /* mute adc when mic path switch */
+ regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x44);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x66);
+ es8326->hp = 0;
+ }
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ /*
+ * Inverted HPJACK_POL bit to trigger one IRQ to double check HP Removal event
+ */
+ if (es8326->jack_remove_retry == 0) {
+ es8326->jack_remove_retry = 1;
+ dev_dbg(comp->dev, "remove event check, invert HPJACK_POL, cnt = %d\n",
+ es8326->jack_remove_retry);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE,
+ ES8326_HP_DET_JACK_POL, (es8326->jd_inverted ?
+ es8326->jack_pol : ~es8326->jack_pol));
+
+ } else {
+ es8326->jack_remove_retry = 0;
}
} else if ((iface & ES8326_HPINSERT_FLAG) == ES8326_HPINSERT_FLAG) {
+ es8326->jack_remove_retry = 0;
+ if (es8326->hp == 0) {
+ dev_dbg(comp->dev, "First insert, start OMTP/CTIA type check\n");
+ /*
+ * set auto-check mode, then restart jack_detect_work after 100ms.
+ * Don't report jack status.
+ */
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x01);
+ usleep_range(50000, 70000);
+ regmap_update_bits(es8326->regmap, ES8326_HPDET_TYPE, 0x03, 0x00);
+ queue_delayed_work(system_wq, &es8326->jack_detect_work,
+ msecs_to_jiffies(100));
+ es8326->hp = 1;
+ goto exit;
+ }
if (es8326->jack->status & SND_JACK_HEADSET) {
/* detect button */
+ dev_dbg(comp->dev, "button pressed\n");
queue_delayed_work(system_wq, &es8326->button_press_work, 10);
+ goto exit;
+ }
+ if ((iface & ES8326_HPBUTTON_FLAG) == 0x01) {
+ dev_dbg(comp->dev, "Headphone detected\n");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADPHONE, SND_JACK_HEADSET);
} else {
- if ((iface & ES8326_HPBUTTON_FLAG) == 0x00) {
- dev_dbg(comp->dev, "Headset detected");
- snd_soc_jack_report(es8326->jack,
- SND_JACK_HEADSET, SND_JACK_HEADSET);
- snd_soc_component_write(comp,
- ES8326_ADC1_SRC, es8326->mic1_src);
- } else {
- dev_dbg(comp->dev, "Headphone detected");
- snd_soc_jack_report(es8326->jack,
- SND_JACK_HEADPHONE, SND_JACK_HEADSET);
- }
+ dev_dbg(comp->dev, "Headset detected\n");
+ snd_soc_jack_report(es8326->jack,
+ SND_JACK_HEADSET, SND_JACK_HEADSET);
+
+ regmap_write(es8326->regmap, ES8326_ADC_SCALE, 0x33);
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x08);
+ regmap_update_bits(es8326->regmap, ES8326_PGAGAIN,
+ 0x80, 0x80);
+ regmap_write(es8326->regmap, ES8326_ADC1_SRC, 0x00);
+ regmap_write(es8326->regmap, ES8326_ADC2_SRC, 0x00);
+ regmap_update_bits(es8326->regmap, ES8326_PGA_PDN,
+ 0x08, 0x00);
+ usleep_range(10000, 15000);
}
}
+exit:
mutex_unlock(&es8326->lock);
}
@@ -624,51 +701,138 @@ static irqreturn_t es8326_irq(int irq, void *dev_id)
msecs_to_jiffies(10));
else
queue_delayed_work(system_wq, &es8326->jack_detect_work,
- msecs_to_jiffies(300));
+ msecs_to_jiffies(600));
out:
return IRQ_HANDLED;
}
-static int es8326_resume(struct snd_soc_component *component)
+static int es8326_calibrate(struct snd_soc_component *component)
{
struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
unsigned int reg;
+ unsigned int offset_l, offset_r;
+
+ regmap_read(es8326->regmap, ES8326_CHIP_VERSION, &reg);
+ es8326->version = reg;
+
+ if ((es8326->version == ES8326_VERSION_B) && (es8326->calibrated == false)) {
+ dev_dbg(component->dev, "ES8326_VERSION_B, calibrating\n");
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0xc0);
+ regmap_write(es8326->regmap, ES8326_CLK_DIV1, 0x01);
+ regmap_write(es8326->regmap, ES8326_CLK_DLL, 0x30);
+ regmap_write(es8326->regmap, ES8326_CLK_MUX, 0xed);
+ regmap_write(es8326->regmap, ES8326_CLK_TRI, 0xc1);
+ regmap_write(es8326->regmap, ES8326_DAC_MUTE, 0x03);
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7f);
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x33);
+ regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x88);
+ regmap_write(es8326->regmap, ES8326_HP_VOL, 0x80);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ regmap_write(es8326->regmap, ES8326_RESET, 0xc0);
+ usleep_range(15000, 20000);
+
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_CSM_MUTE_STA, &reg);
+ if ((reg & 0xf0) != 0x40)
+ msleep(50);
+
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0xd4);
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x4d);
+ msleep(200);
+ regmap_write(es8326->regmap, ES8326_HP_CAL, ES8326_HP_OFF);
+ regmap_read(es8326->regmap, ES8326_HPL_OFFSET_INI, &offset_l);
+ regmap_read(es8326->regmap, ES8326_HPR_OFFSET_INI, &offset_r);
+ regmap_write(es8326->regmap, ES8326_HP_OFFSET_CAL, 0x8c);
+ regmap_write(es8326->regmap, ES8326_HPL_OFFSET_INI, offset_l);
+ regmap_write(es8326->regmap, ES8326_HPR_OFFSET_INI, offset_r);
+ regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
+
+ es8326->calibrated = true;
+ }
+
+ return 0;
+}
+
+static int es8326_resume(struct snd_soc_component *component)
+{
+ struct es8326_priv *es8326 = snd_soc_component_get_drvdata(component);
regcache_cache_only(es8326->regmap, false);
regcache_sync(es8326->regmap);
+ /* reset internal clock state */
+ regmap_write(es8326->regmap, ES8326_RESET, 0x1f);
+ regmap_write(es8326->regmap, ES8326_VMIDSEL, 0x0E);
+ usleep_range(10000, 15000);
+ regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88);
+ /* set headphone default type and detect pin */
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x81);
+ regmap_write(es8326->regmap, ES8326_CLK_RESAMPLE, 0x05);
+
+ /* set internal oscillator as clock source of headpone cp */
+ regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x84);
regmap_write(es8326->regmap, ES8326_CLK_CTL, ES8326_CLK_ON);
- /* Two channel ADC */
- regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x02);
+ /* clock manager reset release */
+ regmap_write(es8326->regmap, ES8326_RESET, 0x17);
+ /* set headphone detection as half scan mode */
+ regmap_write(es8326->regmap, ES8326_HP_MISC, 0x08);
+ regmap_write(es8326->regmap, ES8326_PULLUP_CTL, 0x00);
+
+ /* enable headphone driver */
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa7);
+ usleep_range(2000, 5000);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xab);
+ usleep_range(2000, 5000);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER_REF, 0xbb);
+ usleep_range(2000, 5000);
+ regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xa1);
+
regmap_write(es8326->regmap, ES8326_CLK_INV, 0x00);
- regmap_write(es8326->regmap, ES8326_CLK_DIV_CPC, 0x1F);
- regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xC8);
- regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x88);
- regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x20);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS1, 0xc4);
+ regmap_write(es8326->regmap, ES8326_CLK_VMIDS2, 0x81);
+ regmap_write(es8326->regmap, ES8326_CLK_CAL_TIME, 0x00);
+ /* calibrate for B version */
+ es8326_calibrate(component);
+ /* turn off headphone out */
+ regmap_write(es8326->regmap, ES8326_HP_CAL, 0x00);
+ /* set ADC and DAC in low power mode */
+ regmap_write(es8326->regmap, ES8326_ANA_LP, 0xf0);
+
+ /* force micbias on */
+ regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0x4f);
regmap_write(es8326->regmap, ES8326_SYS_BIAS, 0x08);
- regmap_write(es8326->regmap, ES8326_DAC2HPMIX, 0x22);
- regmap_write(es8326->regmap, ES8326_ADC1_SRC, es8326->mic1_src);
- regmap_write(es8326->regmap, ES8326_ADC2_SRC, es8326->mic2_src);
- regmap_write(es8326->regmap, ES8326_HPJACK_TIMER, 0x88);
- regmap_write(es8326->regmap, ES8326_HP_DET,
- ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol);
- regmap_write(es8326->regmap, ES8326_INT_SOURCE, es8326->interrupt_src);
- regmap_write(es8326->regmap, ES8326_INTOUT_IO, es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
+ /* select vdda as micbias source */
+ regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x23);
+ /* set dac dsmclip = 1 */
+ regmap_write(es8326->regmap, ES8326_DAC_DSM, 0x08);
+ regmap_write(es8326->regmap, ES8326_DAC_VPPSCALE, 0x15);
+
+ regmap_write(es8326->regmap, ES8326_INT_SOURCE,
+ (ES8326_INT_SRC_PIN9 | ES8326_INT_SRC_BUTTON));
+ regmap_write(es8326->regmap, ES8326_INTOUT_IO,
+ es8326->interrupt_clk);
+ regmap_write(es8326->regmap, ES8326_SDINOUT1_IO,
+ (ES8326_IO_DMIC_CLK << ES8326_SDINOUT1_SHIFT));
+ regmap_write(es8326->regmap, ES8326_SDINOUT23_IO, ES8326_IO_INPUT);
+
+ regmap_write(es8326->regmap, ES8326_ANA_PDN, 0x3b);
regmap_write(es8326->regmap, ES8326_RESET, ES8326_CSM_ON);
- snd_soc_component_update_bits(component, ES8326_PGAGAIN,
- ES8326_MIC_SEL_MASK, ES8326_MIC1_SEL);
+ regmap_update_bits(es8326->regmap, ES8326_PGAGAIN, ES8326_MIC_SEL_MASK,
+ ES8326_MIC1_SEL);
- regmap_read(es8326->regmap, ES8326_CHIP_VERSION, &reg);
- if ((reg & ES8326_VERSION_B) == 1) {
- regmap_write(es8326->regmap, ES8326_ANA_MICBIAS, 0xDD);
- regmap_write(es8326->regmap, ES8326_ANA_VSEL, 0x7F);
- regmap_write(es8326->regmap, ES8326_VMIDLOW, 0x0F);
- /* enable button detect */
- regmap_write(es8326->regmap, ES8326_HP_DRIVER, 0xA0);
- }
+ regmap_update_bits(es8326->regmap, ES8326_DAC_MUTE, ES8326_MUTE_MASK,
+ ES8326_MUTE);
- es8326_irq(es8326->irq, es8326);
+ regmap_write(es8326->regmap, ES8326_HPDET_TYPE, 0x80 |
+ ((es8326->version == ES8326_VERSION_B) ?
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol) :
+ (ES8326_HP_DET_SRC_PIN9 | es8326->jack_pol | 0x04)));
+
+ es8326->jack_remove_retry = 0;
+ es8326->hp = 0;
return 0;
}
@@ -683,6 +847,10 @@ static int es8326_suspend(struct snd_soc_component *component)
regcache_cache_only(es8326->regmap, true);
regcache_mark_dirty(es8326->regmap);
+ /* reset register value to default */
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x01);
+ usleep_range(1000, 3000);
+ regmap_write(es8326->regmap, ES8326_CSM_I2C_STA, 0x00);
return 0;
}
@@ -712,7 +880,7 @@ static int es8326_probe(struct snd_soc_component *component)
ret = device_property_read_u8(component->dev, "everest,jack-pol", &es8326->jack_pol);
if (ret != 0) {
dev_dbg(component->dev, "jack-pol return %d", ret);
- es8326->jack_pol = ES8326_HP_DET_BUTTON_POL | ES8326_HP_TYPE_OMTP;
+ es8326->jack_pol = ES8326_HP_TYPE_AUTO;
}
dev_dbg(component->dev, "jack-pol %x", es8326->jack_pol);
@@ -743,7 +911,7 @@ static void es8326_enable_jack_detect(struct snd_soc_component *component,
mutex_lock(&es8326->lock);
if (es8326->jd_inverted)
- snd_soc_component_update_bits(component, ES8326_HP_DET,
+ snd_soc_component_update_bits(component, ES8326_HPDET_TYPE,
ES8326_HP_DET_JACK_POL, ~es8326->jack_pol);
es8326->jack = jack;
diff --git a/sound/soc/codecs/es8326.h b/sound/soc/codecs/es8326.h
index 3f8f7da58062..90a08351d6ac 100644
--- a/sound/soc/codecs/es8326.h
+++ b/sound/soc/codecs/es8326.h
@@ -73,15 +73,19 @@
#define ES8326_DRC_RECOVERY 0x53
#define ES8326_DRC_WINSIZE 0x54
#define ES8326_HPJACK_TIMER 0x56
-#define ES8326_HP_DET 0x57
+#define ES8326_HPDET_TYPE 0x57
#define ES8326_INT_SOURCE 0x58
#define ES8326_INTOUT_IO 0x59
#define ES8326_SDINOUT1_IO 0x5A
#define ES8326_SDINOUT23_IO 0x5B
#define ES8326_JACK_PULSE 0x5C
+#define ES8326_HP_MISC 0xF7
+#define ES8326_CTIA_OMTP_STA 0xF8
#define ES8326_PULLUP_CTL 0xF9
-#define ES8326_HP_DETECT 0xFB
+#define ES8326_CSM_I2C_STA 0xFA
+#define ES8326_HPDET_STA 0xFB
+#define ES8326_CSM_MUTE_STA 0xFC
#define ES8326_CHIP_ID1 0xFD
#define ES8326_CHIP_ID2 0xFE
#define ES8326_CHIP_VERSION 0xFF
@@ -146,7 +150,7 @@
#define ES8326_ADC3_SHIFT 0
#define ES8326_ADC4_SHIFT 3
-/* ES8326_HP_DET */
+/* ES8326_HPDET_TYPE */
#define ES8326_HP_DET_SRC_PIN27 (1 << 5)
#define ES8326_HP_DET_SRC_PIN9 (1 << 4)
#define ES8326_HP_DET_JACK_POL (1 << 3)
@@ -156,6 +160,13 @@
#define ES8326_HP_TYPE_AUTO (1 << 0)
#define ES8326_HP_TYPE_AUTO_INV (0 << 0)
+/* ES8326_INT_SOURCE */
+#define ES8326_INT_SRC_DAC_MOZ (1 << 0)
+#define ES8326_INT_SRC_ADC_MOZ (1 << 1)
+#define ES8326_INT_SRC_BUTTON (1 << 2)
+#define ES8326_INT_SRC_PIN9 (1 << 3)
+#define ES8326_INT_SRC_PIN27 (1 << 4)
+
/* ES8326_SDINOUT1_IO */
#define ES8326_IO_INPUT (0 << 0)
#define ES8326_IO_SDIN_SLOT0 (1 << 0)
@@ -174,12 +185,12 @@
#define ES8326_SDINOUT2_SHIFT 4
#define ES8326_SDINOUT3_SHIFT 0
-/* ES8326_HP_DETECT */
+/* ES8326_HPDET_STA */
#define ES8326_HPINSERT_FLAG (1 << 1)
#define ES8326_HPBUTTON_FLAG (1 << 0)
/* ES8326_CHIP_VERSION 0xFF */
-#define ES8326_VERSION_B (1 << 0)
+#define ES8326_VERSION (1 << 0)
+#define ES8326_VERSION_B (3 << 0)
#endif
-
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index d21f69f05342..13689e718d36 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -723,24 +723,6 @@ static u64 hdmi_codec_formats =
SND_SOC_POSSIBLE_DAIFMT_LEFT_J |
SND_SOC_POSSIBLE_DAIFMT_AC97;
-static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
- .startup = hdmi_codec_startup,
- .shutdown = hdmi_codec_shutdown,
- .hw_params = hdmi_codec_hw_params,
- .prepare = hdmi_codec_prepare,
- .set_fmt = hdmi_codec_i2s_set_fmt,
- .mute_stream = hdmi_codec_mute,
- .auto_selectable_formats = &hdmi_codec_formats,
- .num_auto_selectable_formats = 1,
-};
-
-static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
- .startup = hdmi_codec_startup,
- .shutdown = hdmi_codec_shutdown,
- .hw_params = hdmi_codec_hw_params,
- .mute_stream = hdmi_codec_mute,
-};
-
#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
@@ -921,10 +903,31 @@ static int hdmi_dai_spdif_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops hdmi_codec_i2s_dai_ops = {
+ .probe = hdmi_dai_probe,
+ .startup = hdmi_codec_startup,
+ .shutdown = hdmi_codec_shutdown,
+ .hw_params = hdmi_codec_hw_params,
+ .prepare = hdmi_codec_prepare,
+ .set_fmt = hdmi_codec_i2s_set_fmt,
+ .mute_stream = hdmi_codec_mute,
+ .pcm_new = hdmi_codec_pcm_new,
+ .auto_selectable_formats = &hdmi_codec_formats,
+ .num_auto_selectable_formats = 1,
+};
+
+static const struct snd_soc_dai_ops hdmi_codec_spdif_dai_ops = {
+ .probe = hdmi_dai_spdif_probe,
+ .startup = hdmi_codec_startup,
+ .shutdown = hdmi_codec_shutdown,
+ .hw_params = hdmi_codec_hw_params,
+ .mute_stream = hdmi_codec_mute,
+ .pcm_new = hdmi_codec_pcm_new,
+};
+
static const struct snd_soc_dai_driver hdmi_i2s_dai = {
.name = "i2s-hifi",
.id = DAI_ID_I2S,
- .probe = hdmi_dai_probe,
.playback = {
.stream_name = "I2S Playback",
.channels_min = 2,
@@ -942,13 +945,11 @@ static const struct snd_soc_dai_driver hdmi_i2s_dai = {
.sig_bits = 24,
},
.ops = &hdmi_codec_i2s_dai_ops,
- .pcm_new = hdmi_codec_pcm_new,
};
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.name = "spdif-hifi",
.id = DAI_ID_SPDIF,
- .probe = hdmi_dai_spdif_probe,
.playback = {
.stream_name = "SPDIF Playback",
.channels_min = 2,
@@ -964,7 +965,6 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.formats = SPDIF_FORMATS,
},
.ops = &hdmi_codec_spdif_dai_ops,
- .pcm_new = hdmi_codec_pcm_new,
};
static int hdmi_of_xlate_dai_id(struct snd_soc_component *component,
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 7c25acf6ff0d..d1cea93bdb59 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -301,7 +301,7 @@ static const struct regmap_config jz4740_codec_regmap_config = {
.reg_defaults = jz4740_codec_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
};
static int jz4740_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
deleted file mode 100644
index b84f6f1f6800..000000000000
--- a/sound/soc/codecs/l3.c
+++ /dev/null
@@ -1,132 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * L3 code
- *
- * Copyright (C) 2008, Christian Pellegrin <chripell@evolware.org>
- *
- * based on:
- *
- * L3 bus algorithm module.
- *
- * Copyright (C) 2001 Russell King, All Rights Reserved.
- */
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/gpio.h>
-
-#include <sound/l3.h>
-
-/*
- * Send one byte of data to the chip. Data is latched into the chip on
- * the rising edge of the clock.
- */
-static void sendbyte(struct l3_pins *adap, unsigned int byte)
-{
- int i;
-
- for (i = 0; i < 8; i++) {
- adap->setclk(adap, 0);
- udelay(adap->data_hold);
- adap->setdat(adap, byte & 1);
- udelay(adap->data_setup);
- adap->setclk(adap, 1);
- udelay(adap->clock_high);
- byte >>= 1;
- }
-}
-
-/*
- * Send a set of bytes to the chip. We need to pulse the MODE line
- * between each byte, but never at the start nor at the end of the
- * transfer.
- */
-static void sendbytes(struct l3_pins *adap, const u8 *buf,
- int len)
-{
- int i;
-
- for (i = 0; i < len; i++) {
- if (i) {
- udelay(adap->mode_hold);
- adap->setmode(adap, 0);
- udelay(adap->mode);
- }
- adap->setmode(adap, 1);
- udelay(adap->mode_setup);
- sendbyte(adap, buf[i]);
- }
-}
-
-int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
-{
- adap->setclk(adap, 1);
- adap->setdat(adap, 1);
- adap->setmode(adap, 1);
- udelay(adap->mode);
-
- adap->setmode(adap, 0);
- udelay(adap->mode_setup);
- sendbyte(adap, addr);
- udelay(adap->mode_hold);
-
- sendbytes(adap, data, len);
-
- adap->setclk(adap, 1);
- adap->setdat(adap, 1);
- adap->setmode(adap, 0);
-
- return len;
-}
-EXPORT_SYMBOL_GPL(l3_write);
-
-
-static void l3_set_clk(struct l3_pins *adap, int val)
-{
- gpio_set_value(adap->gpio_clk, val);
-}
-
-static void l3_set_data(struct l3_pins *adap, int val)
-{
- gpio_set_value(adap->gpio_data, val);
-}
-
-static void l3_set_mode(struct l3_pins *adap, int val)
-{
- gpio_set_value(adap->gpio_mode, val);
-}
-
-int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap)
-{
- int ret;
-
- if (!adap->use_gpios)
- return -EINVAL;
-
- ret = devm_gpio_request_one(dev, adap->gpio_data,
- GPIOF_OUT_INIT_LOW, "l3_data");
- if (ret < 0)
- return ret;
- adap->setdat = l3_set_data;
-
- ret = devm_gpio_request_one(dev, adap->gpio_clk,
- GPIOF_OUT_INIT_LOW, "l3_clk");
- if (ret < 0)
- return ret;
- adap->setclk = l3_set_clk;
-
- ret = devm_gpio_request_one(dev, adap->gpio_mode,
- GPIOF_OUT_INIT_LOW, "l3_mode");
- if (ret < 0)
- return ret;
- adap->setmode = l3_set_mode;
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(l3_set_gpio_ops);
-
-MODULE_DESCRIPTION("L3 bit-banging driver");
-MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98363.c b/sound/soc/codecs/max98363.c
index 2dfaf4fcfbd3..a2cca3436c68 100644
--- a/sound/soc/codecs/max98363.c
+++ b/sound/soc/codecs/max98363.c
@@ -160,28 +160,17 @@ static int max98363_io_init(struct sdw_slave *slave)
struct max98363_priv *max98363 = dev_get_drvdata(dev);
int ret, reg;
- if (max98363->first_hw_init) {
- regcache_cache_only(max98363->regmap, false);
+ regcache_cache_only(max98363->regmap, false);
+ if (max98363->first_hw_init)
regcache_cache_bypass(max98363->regmap, true);
- }
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only 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);
-
+ if (!max98363->first_hw_init)
/* 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);
@@ -410,6 +399,8 @@ static int max98363_init(struct sdw_slave *slave, struct regmap *regmap)
max98363->regmap = regmap;
max98363->slave = slave;
+ regcache_cache_only(max98363->regmap, true);
+
max98363->hw_init = false;
max98363->first_hw_init = false;
@@ -417,10 +408,26 @@ static int max98363_init(struct sdw_slave *slave, struct regmap *regmap)
ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98363,
max98363_dai,
ARRAY_SIZE(max98363_dai));
- if (ret < 0)
+ if (ret < 0) {
dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
- return ret;
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+ return 0;
}
static int max98363_sdw_probe(struct sdw_slave *slave,
diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c
index df92242af960..b5cb951af570 100644
--- a/sound/soc/codecs/max98373-sdw.c
+++ b/sound/soc/codecs/max98373-sdw.c
@@ -361,28 +361,17 @@ static int max98373_io_init(struct sdw_slave *slave)
struct device *dev = &slave->dev;
struct max98373_priv *max98373 = dev_get_drvdata(dev);
- if (max98373->first_hw_init) {
- regcache_cache_only(max98373->regmap, false);
+ regcache_cache_only(max98373->regmap, false);
+ if (max98373->first_hw_init)
regcache_cache_bypass(max98373->regmap, true);
- }
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
*/
- if (!max98373->first_hw_init) {
- /* set autosuspend parameters */
- pm_runtime_set_autosuspend_delay(dev, 3000);
- pm_runtime_use_autosuspend(dev);
-
+ if (!max98373->first_hw_init)
/* 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);
/* Software Reset */
@@ -753,6 +742,8 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
max98373->regmap = regmap;
max98373->slave = slave;
+ regcache_cache_only(max98373->regmap, true);
+
max98373->cache_num = ARRAY_SIZE(max98373_sdw_cache_reg);
max98373->cache = devm_kcalloc(dev, max98373->cache_num,
sizeof(*max98373->cache),
@@ -773,10 +764,27 @@ static int max98373_init(struct sdw_slave *slave, struct regmap *regmap)
ret = devm_snd_soc_register_component(dev, &soc_codec_dev_max98373_sdw,
max98373_sdw_dai,
ARRAY_SIZE(max98373_sdw_dai));
- if (ret < 0)
+ if (ret < 0) {
dev_err(dev, "Failed to register codec: %d\n", ret);
+ return ret;
+ }
- return ret;
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ return 0;
}
static int max98373_update_status(struct sdw_slave *slave,
@@ -834,10 +842,7 @@ static int max98373_sdw_probe(struct sdw_slave *slave,
static int max98373_sdw_remove(struct sdw_slave *slave)
{
- struct max98373_priv *max98373 = dev_get_drvdata(&slave->dev);
-
- if (max98373->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
index bdc508e23e59..922ce0dc4e60 100644
--- a/sound/soc/codecs/max98926.c
+++ b/sound/soc/codecs/max98926.c
@@ -528,7 +528,8 @@ static int max98926_i2c_probe(struct i2c_client *i2c)
"Failed to allocate regmap: %d\n", ret);
goto err_out;
}
- if (of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
+ if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode") ||
+ of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
max98926->interleave_mode = true;
if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
index 0aaf2e6ae78d..776f23d38ac5 100644
--- a/sound/soc/codecs/max98927.c
+++ b/sound/soc/codecs/max98927.c
@@ -162,10 +162,8 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- regmap_update_bits(max98927->regmap,
- MAX98927_R0021_PCM_MASTER_MODE,
- MAX98927_PCM_MASTER_MODE_MASK,
- mode);
+ regmap_update_bits(max98927->regmap, MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MASK, mode);
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_NF:
@@ -178,10 +176,8 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
return -EINVAL;
}
- regmap_update_bits(max98927->regmap,
- MAX98927_R0020_PCM_MODE_CFG,
- MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE,
- invert);
+ regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE, invert);
/* interface format */
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
@@ -207,36 +203,31 @@ static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
if (!use_pdm) {
/* pcm channel configuration */
- regmap_update_bits(max98927->regmap,
- MAX98927_R0018_PCM_RX_EN_A,
- MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
- MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN);
+ regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN);
regmap_update_bits(max98927->regmap,
- MAX98927_R0020_PCM_MODE_CFG,
- MAX98927_PCM_MODE_CFG_FORMAT_MASK,
- format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT);
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT);
- regmap_update_bits(max98927->regmap,
- MAX98927_R003B_SPK_SRC_SEL,
- MAX98927_SPK_SRC_MASK, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 0);
- regmap_update_bits(max98927->regmap,
- MAX98927_R0035_PDM_RX_CTRL,
- MAX98927_PDM_RX_EN_MASK, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 0);
} else {
/* pdm channel configuration */
- regmap_update_bits(max98927->regmap,
- MAX98927_R0035_PDM_RX_CTRL,
- MAX98927_PDM_RX_EN_MASK, 1);
+ regmap_update_bits(max98927->regmap, MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 1);
- regmap_update_bits(max98927->regmap,
- MAX98927_R003B_SPK_SRC_SEL,
- MAX98927_SPK_SRC_MASK, 3);
+ regmap_update_bits(max98927->regmap, MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 3);
- regmap_update_bits(max98927->regmap,
- MAX98927_R0018_PCM_RX_EN_A,
- MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+ 0);
}
return 0;
}
@@ -283,9 +274,9 @@ static int max98927_set_clock(struct max98927_priv *max98927,
return -EINVAL;
}
regmap_update_bits(max98927->regmap,
- MAX98927_R0021_PCM_MASTER_MODE,
- MAX98927_PCM_MASTER_MODE_MCLK_MASK,
- i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
+ MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MCLK_MASK,
+ i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
}
if (!max98927->tdm_mode) {
@@ -298,9 +289,8 @@ static int max98927_set_clock(struct max98927_priv *max98927,
}
regmap_update_bits(max98927->regmap,
- MAX98927_R0022_PCM_CLK_SETUP,
- MAX98927_PCM_CLK_SETUP_BSEL_MASK,
- value);
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK, value);
}
return 0;
}
@@ -333,9 +323,8 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
max98927->ch_size = snd_pcm_format_width(params_format(params));
- regmap_update_bits(max98927->regmap,
- MAX98927_R0020_PCM_MODE_CFG,
- MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+ regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
dev_dbg(component->dev, "format supported %d",
params_format(params));
@@ -375,27 +364,24 @@ static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
goto err;
}
/* set DAI_SR to correct LRCLK frequency */
- regmap_update_bits(max98927->regmap,
- MAX98927_R0023_PCM_SR_SETUP1,
- MAX98927_PCM_SR_SET1_SR_MASK,
- sampling_rate);
- regmap_update_bits(max98927->regmap,
- MAX98927_R0024_PCM_SR_SETUP2,
- MAX98927_PCM_SR_SET2_SR_MASK,
- sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT);
+ regmap_update_bits(max98927->regmap, MAX98927_R0023_PCM_SR_SETUP1,
+ MAX98927_PCM_SR_SET1_SR_MASK, sampling_rate);
+ regmap_update_bits(max98927->regmap, MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT);
/* set sampling rate of IV */
if (max98927->interleave_mode &&
sampling_rate > MAX98927_PCM_SR_SET1_SR_16000)
regmap_update_bits(max98927->regmap,
- MAX98927_R0024_PCM_SR_SETUP2,
- MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
- sampling_rate - 3);
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate - 3);
else
regmap_update_bits(max98927->regmap,
- MAX98927_R0024_PCM_SR_SETUP2,
- MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
- sampling_rate);
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
return max98927_set_clock(max98927, params);
err:
return -EINVAL;
@@ -420,10 +406,8 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- regmap_update_bits(max98927->regmap,
- MAX98927_R0022_PCM_CLK_SETUP,
- MAX98927_PCM_CLK_SETUP_BSEL_MASK,
- bsel);
+ regmap_update_bits(max98927->regmap, MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK, bsel);
/* Channel size configuration */
switch (slot_width) {
@@ -442,33 +426,26 @@ static int max98927_dai_tdm_slot(struct snd_soc_dai *dai,
return -EINVAL;
}
- regmap_update_bits(max98927->regmap,
- MAX98927_R0020_PCM_MODE_CFG,
- MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+ regmap_update_bits(max98927->regmap, MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
/* Rx slot configuration */
- regmap_write(max98927->regmap,
- MAX98927_R0018_PCM_RX_EN_A,
- rx_mask & 0xFF);
- regmap_write(max98927->regmap,
- MAX98927_R0019_PCM_RX_EN_B,
- (rx_mask & 0xFF00) >> 8);
+ regmap_write(max98927->regmap, MAX98927_R0018_PCM_RX_EN_A,
+ rx_mask & 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R0019_PCM_RX_EN_B,
+ (rx_mask & 0xFF00) >> 8);
/* Tx slot configuration */
- regmap_write(max98927->regmap,
- MAX98927_R001A_PCM_TX_EN_A,
- tx_mask & 0xFF);
- regmap_write(max98927->regmap,
- MAX98927_R001B_PCM_TX_EN_B,
- (tx_mask & 0xFF00) >> 8);
+ regmap_write(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A,
+ tx_mask & 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B,
+ (tx_mask & 0xFF00) >> 8);
/* Tx slot Hi-Z configuration */
- regmap_write(max98927->regmap,
- MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
- ~tx_mask & 0xFF);
- regmap_write(max98927->regmap,
- MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
- (~tx_mask & 0xFF00) >> 8);
+ regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ ~tx_mask & 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ (~tx_mask & 0xFF00) >> 8);
return 0;
}
@@ -506,20 +483,16 @@ static int max98927_dac_event(struct snd_soc_dapm_widget *w,
max98927->tdm_mode = false;
break;
case SND_SOC_DAPM_POST_PMU:
- regmap_update_bits(max98927->regmap,
- MAX98927_R003A_AMP_EN,
- MAX98927_AMP_EN_MASK, 1);
- regmap_update_bits(max98927->regmap,
- MAX98927_R00FF_GLOBAL_SHDN,
- MAX98927_GLOBAL_EN_MASK, 1);
+ regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 1);
+ regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 1);
break;
case SND_SOC_DAPM_POST_PMD:
- regmap_update_bits(max98927->regmap,
- MAX98927_R00FF_GLOBAL_SHDN,
- MAX98927_GLOBAL_EN_MASK, 0);
- regmap_update_bits(max98927->regmap,
- MAX98927_R003A_AMP_EN,
- MAX98927_AMP_EN_MASK, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 0);
break;
default:
return 0;
@@ -532,8 +505,8 @@ static const char * const max98927_switch_text[] = {
static const struct soc_enum dai_sel_enum =
SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
- MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
- 3, max98927_switch_text);
+ MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT, 3,
+ max98927_switch_text);
static const struct snd_kcontrol_new max98927_dai_controls =
SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
@@ -543,17 +516,17 @@ static const struct snd_kcontrol_new max98927_vi_control =
static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = {
SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN,
- 0, 0, max98927_dac_event,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ 0, 0, max98927_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
- &max98927_dai_controls),
+ &max98927_dai_controls),
SND_SOC_DAPM_OUTPUT("BE_OUT"),
SND_SOC_DAPM_AIF_OUT("Voltage Sense", "HiFi Capture", 0,
- MAX98927_R003E_MEAS_EN, 0, 0),
+ MAX98927_R003E_MEAS_EN, 0, 0),
SND_SOC_DAPM_AIF_OUT("Current Sense", "HiFi Capture", 0,
- MAX98927_R003E_MEAS_EN, 1, 0),
+ MAX98927_R003E_MEAS_EN, 1, 0),
SND_SOC_DAPM_SWITCH("VI Sense", SND_SOC_NOPM, 0, 0,
- &max98927_vi_control),
+ &max98927_vi_control),
SND_SOC_DAPM_SIGGEN("VMON"),
SND_SOC_DAPM_SIGGEN("IMON"),
};
@@ -623,20 +596,19 @@ static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
max98927_current_limit_text);
static const struct snd_kcontrol_new max98927_snd_controls[] = {
- SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN,
- 0, 6, 0,
- max98927_spk_tlv),
+ SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN, 0, 6, 0,
+ max98927_spk_tlv),
SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL,
- 0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0,
- max98927_digital_tlv),
+ 0, (1 << MAX98927_AMP_VOL_WIDTH) - 1, 0,
+ max98927_digital_tlv),
SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN,
- MAX98927_BROWNOUT_DSP_SHIFT, 1, 0),
+ MAX98927_BROWNOUT_DSP_SHIFT, 1, 0),
SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG,
- MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
- SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL,
- MAX98927_DRE_EN_SHIFT, 1, 0),
+ MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+ SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL, MAX98927_DRE_EN_SHIFT,
+ 1, 0),
SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL,
- MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
+ MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
SOC_ENUM("Boost Output Voltage", max98927_boost_voltage),
SOC_ENUM("Current Limit", max98927_current_limit),
};
@@ -682,117 +654,82 @@ static int max98927_probe(struct snd_soc_component *component)
max98927->component = component;
/* Software Reset */
- regmap_write(max98927->regmap,
- MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET);
+ regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET,
+ MAX98927_SOFT_RESET);
/* IV default slot configuration */
- regmap_write(max98927->regmap,
- MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
- 0xFF);
- regmap_write(max98927->regmap,
- MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
- 0xFF);
- regmap_write(max98927->regmap,
- MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
- 0x80);
- regmap_write(max98927->regmap,
- MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,
- 0x1);
+ regmap_write(max98927->regmap, MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0xFF);
+ regmap_write(max98927->regmap, MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+ 0x80);
+ regmap_write(max98927->regmap, MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,
+ 0x1);
/* Set inital volume (+13dB) */
- regmap_write(max98927->regmap,
- MAX98927_R0036_AMP_VOL_CTRL,
- 0x38);
- regmap_write(max98927->regmap,
- MAX98927_R003C_SPK_GAIN,
- 0x05);
+ regmap_write(max98927->regmap, MAX98927_R0036_AMP_VOL_CTRL, 0x38);
+ regmap_write(max98927->regmap, MAX98927_R003C_SPK_GAIN, 0x05);
/* Enable DC blocker */
- regmap_write(max98927->regmap,
- MAX98927_R0037_AMP_DSP_CFG,
- 0x03);
+ regmap_write(max98927->regmap, MAX98927_R0037_AMP_DSP_CFG, 0x03);
/* Enable IMON VMON DC blocker */
- regmap_write(max98927->regmap,
- MAX98927_R003F_MEAS_DSP_CFG,
- 0xF7);
+ regmap_write(max98927->regmap, MAX98927_R003F_MEAS_DSP_CFG, 0xF7);
/* Boost Output Voltage & Current limit */
- regmap_write(max98927->regmap,
- MAX98927_R0040_BOOST_CTRL0,
- 0x1C);
- regmap_write(max98927->regmap,
- MAX98927_R0042_BOOST_CTRL1,
- 0x3E);
+ regmap_write(max98927->regmap, MAX98927_R0040_BOOST_CTRL0, 0x1C);
+ regmap_write(max98927->regmap, MAX98927_R0042_BOOST_CTRL1, 0x3E);
/* Measurement ADC config */
- regmap_write(max98927->regmap,
- MAX98927_R0043_MEAS_ADC_CFG,
- 0x04);
- regmap_write(max98927->regmap,
- MAX98927_R0044_MEAS_ADC_BASE_MSB,
- 0x00);
- regmap_write(max98927->regmap,
- MAX98927_R0045_MEAS_ADC_BASE_LSB,
- 0x24);
+ regmap_write(max98927->regmap, MAX98927_R0043_MEAS_ADC_CFG, 0x04);
+ regmap_write(max98927->regmap, MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x00);
+ regmap_write(max98927->regmap, MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x24);
/* Brownout Level */
- regmap_write(max98927->regmap,
- MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1,
- 0x06);
+ regmap_write(max98927->regmap, MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1,
+ 0x06);
/* Envelope Tracking configuration */
- regmap_write(max98927->regmap,
- MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM,
- 0x08);
- regmap_write(max98927->regmap,
- MAX98927_R0086_ENV_TRACK_CTRL,
- 0x01);
- regmap_write(max98927->regmap,
- MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,
- 0x10);
+ regmap_write(max98927->regmap, MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM,
+ 0x08);
+ regmap_write(max98927->regmap, MAX98927_R0086_ENV_TRACK_CTRL, 0x01);
+ regmap_write(max98927->regmap, MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,
+ 0x10);
/* voltage, current slot configuration */
- regmap_write(max98927->regmap,
- MAX98927_R001E_PCM_TX_CH_SRC_A,
- (max98927->i_l_slot<<MAX98927_PCM_TX_CH_SRC_A_I_SHIFT|
- max98927->v_l_slot)&0xFF);
+ regmap_write(max98927->regmap, MAX98927_R001E_PCM_TX_CH_SRC_A,
+ (max98927->i_l_slot << MAX98927_PCM_TX_CH_SRC_A_I_SHIFT | max98927->v_l_slot) & 0xFF);
if (max98927->v_l_slot < 8) {
regmap_update_bits(max98927->regmap,
- MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
- 1 << max98927->v_l_slot, 0);
- regmap_update_bits(max98927->regmap,
- MAX98927_R001A_PCM_TX_EN_A,
- 1 << max98927->v_l_slot,
- 1 << max98927->v_l_slot);
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->v_l_slot, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->v_l_slot,
+ 1 << max98927->v_l_slot);
} else {
regmap_update_bits(max98927->regmap,
- MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
- 1 << (max98927->v_l_slot - 8), 0);
- regmap_update_bits(max98927->regmap,
- MAX98927_R001B_PCM_TX_EN_B,
- 1 << (max98927->v_l_slot - 8),
- 1 << (max98927->v_l_slot - 8));
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->v_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->v_l_slot - 8),
+ 1 << (max98927->v_l_slot - 8));
}
if (max98927->i_l_slot < 8) {
regmap_update_bits(max98927->regmap,
- MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
- 1 << max98927->i_l_slot, 0);
- regmap_update_bits(max98927->regmap,
- MAX98927_R001A_PCM_TX_EN_A,
- 1 << max98927->i_l_slot,
- 1 << max98927->i_l_slot);
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->i_l_slot, 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->i_l_slot,
+ 1 << max98927->i_l_slot);
} else {
regmap_update_bits(max98927->regmap,
- MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
- 1 << (max98927->i_l_slot - 8), 0);
- regmap_update_bits(max98927->regmap,
- MAX98927_R001B_PCM_TX_EN_B,
- 1 << (max98927->i_l_slot - 8),
- 1 << (max98927->i_l_slot - 8));
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->i_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap, MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->i_l_slot - 8),
+ 1 << (max98927->i_l_slot - 8));
}
/* Set interleave mode */
if (max98927->interleave_mode)
regmap_update_bits(max98927->regmap,
- MAX98927_R001F_PCM_TX_CH_SRC_B,
- MAX98927_PCM_TX_CH_INTERLEAVE_MASK,
- MAX98927_PCM_TX_CH_INTERLEAVE_MASK);
+ MAX98927_R001F_PCM_TX_CH_SRC_B,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK);
return 0;
}
@@ -809,8 +746,8 @@ static int max98927_resume(struct device *dev)
{
struct max98927_priv *max98927 = dev_get_drvdata(dev);
- regmap_write(max98927->regmap,
- MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET);
+ regmap_write(max98927->regmap, MAX98927_R0100_SOFT_RESET,
+ MAX98927_SOFT_RESET);
regcache_cache_only(max98927->regmap, false);
regcache_sync(max98927->regmap);
return 0;
@@ -869,9 +806,7 @@ static int max98927_i2c_probe(struct i2c_client *i2c)
int reg = 0;
struct max98927_priv *max98927 = NULL;
- max98927 = devm_kzalloc(&i2c->dev,
- sizeof(*max98927), GFP_KERNEL);
-
+ max98927 = devm_kzalloc(&i2c->dev, sizeof(*max98927), GFP_KERNEL);
if (!max98927) {
ret = -ENOMEM;
return ret;
@@ -879,14 +814,14 @@ static int max98927_i2c_probe(struct i2c_client *i2c)
i2c_set_clientdata(i2c, max98927);
/* update interleave mode info */
- if (!of_property_read_u32(i2c->dev.of_node,
- "interleave_mode", &value)) {
- if (value > 0)
- max98927->interleave_mode = true;
- else
- max98927->interleave_mode = false;
- } else
- max98927->interleave_mode = false;
+ if (of_property_read_bool(i2c->dev.of_node, "maxim,interleave-mode")) {
+ max98927->interleave_mode = true;
+ } else {
+ if (!of_property_read_u32(i2c->dev.of_node, "interleave_mode",
+ &value))
+ if (value > 0)
+ max98927->interleave_mode = true;
+ }
/* regmap initialization */
max98927->regmap
@@ -897,9 +832,9 @@ static int max98927_i2c_probe(struct i2c_client *i2c)
"Failed to allocate regmap: %d\n", ret);
return ret;
}
-
- max98927->reset_gpio
- = devm_gpiod_get_optional(&i2c->dev, "reset", GPIOD_OUT_HIGH);
+
+ max98927->reset_gpio = devm_gpiod_get_optional(&i2c->dev, "reset",
+ GPIOD_OUT_HIGH);
if (IS_ERR(max98927->reset_gpio)) {
ret = PTR_ERR(max98927->reset_gpio);
return dev_err_probe(&i2c->dev, ret, "failed to request GPIO reset pin");
@@ -912,8 +847,7 @@ static int max98927_i2c_probe(struct i2c_client *i2c)
}
/* Check Revision ID */
- ret = regmap_read(max98927->regmap,
- MAX98927_R01FF_REV_ID, &reg);
+ ret = regmap_read(max98927->regmap, MAX98927_R01FF_REV_ID, &reg);
if (ret < 0) {
dev_err(&i2c->dev,
"Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID);
@@ -938,9 +872,8 @@ static void max98927_i2c_remove(struct i2c_client *i2c)
{
struct max98927_priv *max98927 = i2c_get_clientdata(i2c);
- if (max98927->reset_gpio) {
+ if (max98927->reset_gpio)
gpiod_set_value_cansleep(max98927->reset_gpio, 1);
- }
}
static const struct i2c_device_id max98927_i2c_id[] = {
diff --git a/sound/soc/codecs/nau8821.c b/sound/soc/codecs/nau8821.c
index ca6beb2d2649..f307374834b5 100644
--- a/sound/soc/codecs/nau8821.c
+++ b/sound/soc/codecs/nau8821.c
@@ -624,6 +624,36 @@ static int system_clock_control(struct snd_soc_dapm_widget *w,
return 0;
}
+static int nau8821_left_fepga_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct nau8821 *nau8821 = snd_soc_component_get_drvdata(component);
+
+ if (!nau8821->left_input_single_end)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA,
+ NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK,
+ NAU8821_ACDC_VREF_MICN | NAU8821_FEPGA_MODEL_AAF);
+ regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST,
+ NAU8821_HP_BOOST_DISCHRG_EN, NAU8821_HP_BOOST_DISCHRG_EN);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8821->regmap, NAU8821_R77_FEPGA,
+ NAU8821_ACDC_CTRL_MASK | NAU8821_FEPGA_MODEL_MASK, 0);
+ regmap_update_bits(nau8821->regmap, NAU8821_R76_BOOST,
+ NAU8821_HP_BOOST_DISCHRG_EN, 0);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
system_clock_control, SND_SOC_DAPM_POST_PMD),
@@ -635,8 +665,10 @@ static const struct snd_soc_dapm_widget nau8821_dapm_widgets[] = {
NAU8821_POWERUP_ADCL_SFT, 0),
SND_SOC_DAPM_ADC("ADCR Power", NULL, NAU8821_R72_ANALOG_ADC_2,
NAU8821_POWERUP_ADCR_SFT, 0),
+ /* single-ended design only on the left */
SND_SOC_DAPM_PGA_S("Frontend PGA L", 1, NAU8821_R7F_POWER_UP_CONTROL,
- NAU8821_PUP_PGA_L_SFT, 0, NULL, 0),
+ NAU8821_PUP_PGA_L_SFT, 0, nau8821_left_fepga_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA_S("Frontend PGA R", 1, NAU8821_R7F_POWER_UP_CONTROL,
NAU8821_PUP_PGA_R_SFT, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("ADCL Digital path", 0, NAU8821_R01_ENA_CTRL,
@@ -1677,6 +1709,8 @@ static int nau8821_read_device_properties(struct device *dev,
"nuvoton,jkdet-pull-up");
nau8821->key_enable = device_property_read_bool(dev,
"nuvoton,key-enable");
+ nau8821->left_input_single_end = device_property_read_bool(dev,
+ "nuvoton,left-input-single-end");
ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
&nau8821->jkdet_polarity);
if (ret)
@@ -1760,6 +1794,12 @@ static void nau8821_init_regs(struct nau8821 *nau8821)
NAU8821_ADC_SYNC_DOWN_MASK, NAU8821_ADC_SYNC_DOWN_64);
regmap_update_bits(regmap, NAU8821_R2C_DAC_CTRL1,
NAU8821_DAC_OVERSAMPLE_MASK, NAU8821_DAC_OVERSAMPLE_64);
+ if (nau8821->left_input_single_end) {
+ regmap_update_bits(regmap, NAU8821_R6B_PGA_MUTE,
+ NAU8821_MUTE_MICNL_EN, NAU8821_MUTE_MICNL_EN);
+ regmap_update_bits(regmap, NAU8821_R74_MIC_BIAS,
+ NAU8821_MICBIAS_LOWNOISE_EN, NAU8821_MICBIAS_LOWNOISE_EN);
+ }
}
static int nau8821_setup_irq(struct nau8821 *nau8821)
diff --git a/sound/soc/codecs/nau8821.h b/sound/soc/codecs/nau8821.h
index d962293c218e..00a888ed07ce 100644
--- a/sound/soc/codecs/nau8821.h
+++ b/sound/soc/codecs/nau8821.h
@@ -433,6 +433,14 @@
#define NAU8821_DAC_CAPACITOR_MSB (0x1 << 1)
#define NAU8821_DAC_CAPACITOR_LSB 0x1
+/* MUTE_MIC_L_N (0x6b) */
+#define NAU8821_MUTE_MICNL_SFT 5
+#define NAU8821_MUTE_MICNL_EN (0x1 << NAU8821_MUTE_MICNL_SFT)
+#define NAU8821_MUTE_MICNR_SFT 4
+#define NAU8821_MUTE_MICNR_EN (0x1 << NAU8821_MUTE_MICNR_SFT)
+#define NAU8821_MUTE_MICRP_SFT 2
+#define NAU8821_MUTE_MICRP_EN (0x1 << NAU8821_MUTE_MICRP_SFT)
+
/* ANALOG_ADC_1 (0x71) */
#define NAU8821_MICDET_EN_SFT 0
#define NAU8821_MICDET_MASK 0x1
@@ -463,23 +471,39 @@
/* MIC_BIAS (0x74) */
#define NAU8821_MICBIAS_JKR2 (0x1 << 12)
+#define NAU8821_MICBIAS_LOWNOISE_SFT 10
+#define NAU8821_MICBIAS_LOWNOISE_EN (0x1 << NAU8821_MICBIAS_LOWNOISE_SFT)
#define NAU8821_MICBIAS_POWERUP_SFT 8
+#define NAU8821_MICBIAS_POWERUP_EN (0x1 << NAU8821_MICBIAS_POWERUP_SFT)
#define NAU8821_MICBIAS_VOLTAGE_SFT 0
#define NAU8821_MICBIAS_VOLTAGE_MASK 0x7
/* BOOST (0x76) */
#define NAU8821_PRECHARGE_DIS (0x1 << 13)
#define NAU8821_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8821_HP_BOOST_DISCHRG_SFT 11
+#define NAU8821_HP_BOOST_DISCHRG_EN (0x1 << NAU8821_HP_BOOST_DISCHRG_SFT)
#define NAU8821_HP_BOOST_DIS_SFT 9
#define NAU8821_HP_BOOST_DIS (0x1 << NAU8821_HP_BOOST_DIS_SFT)
#define NAU8821_HP_BOOST_G_DIS (0x1 << 8)
#define NAU8821_SHORT_SHUTDOWN_EN (0x1 << 6)
/* FEPGA (0x77) */
+#define NAU8821_ACDC_CTRL_SFT 14
+#define NAU8821_ACDC_CTRL_MASK (0x3 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_ACDC_VREF_MICP (0x1 << NAU8821_ACDC_CTRL_SFT)
+#define NAU8821_ACDC_VREF_MICN (0x2 << NAU8821_ACDC_CTRL_SFT)
#define NAU8821_FEPGA_MODEL_SFT 4
#define NAU8821_FEPGA_MODEL_MASK (0xf << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_AAF (0x1 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_DIS (0x2 << NAU8821_FEPGA_MODEL_SFT)
+#define NAU8821_FEPGA_MODEL_IMP12K (0x8 << NAU8821_FEPGA_MODEL_SFT)
#define NAU8821_FEPGA_MODER_SFT 0
#define NAU8821_FEPGA_MODER_MASK 0xf
+#define NAU8821_FEPGA_MODER_AAF 0x1
+#define NAU8821_FEPGA_MODER_DIS 0x2
+#define NAU8821_FEPGA_MODER_IMP12K 0x8
+
/* PGA_GAIN (0x7e) */
#define NAU8821_PGA_GAIN_L_SFT 8
@@ -543,6 +567,7 @@ struct nau8821 {
bool jkdet_enable;
bool jkdet_pull_enable;
bool jkdet_pull_up;
+ bool left_input_single_end;
int jkdet_polarity;
int jack_insert_debounce;
int jack_eject_debounce;
diff --git a/sound/soc/codecs/rt1011.c b/sound/soc/codecs/rt1011.c
index 42bac8150f62..d5285baad53a 100644
--- a/sound/soc/codecs/rt1011.c
+++ b/sound/soc/codecs/rt1011.c
@@ -13,11 +13,9 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
#include <linux/regmap.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <sound/core.h>
diff --git a/sound/soc/codecs/rt1015.c b/sound/soc/codecs/rt1015.c
index 38d9f69b08d6..99ec0f9a8362 100644
--- a/sound/soc/codecs/rt1015.c
+++ b/sound/soc/codecs/rt1015.c
@@ -12,7 +12,6 @@
#include <linux/delay.h>
#include <linux/firmware.h>
#include <linux/fs.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/module.h>
diff --git a/sound/soc/codecs/rt1015p.c b/sound/soc/codecs/rt1015p.c
index 06800dad8798..44e7fe3c32da 100644
--- a/sound/soc/codecs/rt1015p.c
+++ b/sound/soc/codecs/rt1015p.c
@@ -8,7 +8,6 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/err.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/sound/soc/codecs/rt1016.c b/sound/soc/codecs/rt1016.c
index b1e69fa290b2..919a1f25e584 100644
--- a/sound/soc/codecs/rt1016.c
+++ b/sound/soc/codecs/rt1016.c
@@ -16,7 +16,6 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
-#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c
new file mode 100644
index 000000000000..7295f44c77eb
--- /dev/null
+++ b/sound/soc/codecs/rt1017-sdca-sdw.c
@@ -0,0 +1,824 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// rt1017-sdca-sdw.c -- rt1017 SDCA ALSA SoC amplifier audio driver
+//
+// Copyright(c) 2023 Realtek Semiconductor Corp.
+//
+//
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rt1017-sdca-sdw.h"
+
+static bool rt1017_sdca_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0x3206:
+ case 0xc000:
+ case 0xc001:
+ case 0xc022:
+ case 0xc030:
+ case 0xc104:
+ case 0xc10b:
+ case 0xc10c:
+ case 0xc110:
+ case 0xc112:
+ case 0xc300:
+ case 0xc301:
+ case 0xc318:
+ case 0xc325 ... 0xc328:
+ case 0xc331:
+ case 0xc340:
+ case 0xc350 ... 0xc351:
+ case 0xc500:
+ case 0xc502:
+ case 0xc504:
+ case 0xc507:
+ case 0xc509:
+ case 0xc510:
+ case 0xc512:
+ case 0xc518:
+ case 0xc51b:
+ case 0xc51d:
+ case 0xc520:
+ case 0xc540 ... 0xc542:
+ case 0xc550 ... 0xc552:
+ case 0xc600:
+ case 0xc602:
+ case 0xc612:
+ case 0xc622:
+ case 0xc632:
+ case 0xc642:
+ case 0xc651:
+ case 0xca00:
+ case 0xca09 ... 0xca0c:
+ case 0xca0e ... 0xca0f:
+ case 0xca10 ... 0xca11:
+ case 0xca16 ... 0xca17:
+ case 0xcb00:
+ case 0xcc00:
+ case 0xcc02:
+ case 0xd017:
+ case 0xd01a ... 0xd01c:
+ case 0xd101:
+ case 0xd20c:
+ case 0xd300:
+ case 0xd370:
+ case 0xd500:
+ case 0xd545 ... 0xd548:
+ case 0xd5a5 ... 0xd5a8:
+ case 0xd5aa ... 0xd5ad:
+ case 0xda04 ... 0xda07:
+ case 0xda09 ... 0xda0a:
+ case 0xda0c ... 0xda0f:
+ case 0xda11 ... 0xda14:
+ case 0xda16 ... 0xda19:
+ case 0xdab6 ... 0xdabb:
+ case 0xdb09 ... 0xdb0a:
+ case 0xdb14:
+
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU,
+ RT1017_SDCA_CTL_FU_MUTE, 0x01):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22,
+ RT1017_SDCA_CTL_BYPASS, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29,
+ RT1017_SDCA_CTL_PROT_STAT, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0):
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt1017_sdca_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case 0x2f55:
+ case 0xc000:
+ case 0xc022:
+ case 0xc351:
+ case 0xc518:
+ case SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_SAPU29,
+ RT1017_SDCA_CTL_PROT_STAT, 0):
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct reg_sequence rt1017_blind_write[] = {
+ { 0xc001, 0x43 },
+ { 0x2f55, 0x02 },
+ { 0x3206, 0x80 },
+ { 0x005f, 0x7f },
+ { 0xd101, 0xa0 },
+ { 0xc112, 0xc0 },
+ { 0xc104, 0xaa },
+ { 0xc110, 0x59 },
+ { 0xc112, 0xc0 },
+ { 0xc340, 0x80 },
+ { 0xd017, 0x2c },
+ { 0xd01a, 0xc8 },
+ { 0xd01b, 0xcf },
+ { 0xd01c, 0x0c },
+ { 0xd20c, 0x14 },
+ { 0xdb09, 0x0f },
+ { 0xdb0a, 0x7f },
+ { 0xdb14, 0x03 },
+ { 0xcb00, 0x31 },
+ { 0xc318, 0x44 },
+ { 0xc325, 0xce },
+ { 0xc326, 0x13 },
+ { 0xc327, 0x5f },
+ { 0xc328, 0xf3 },
+ { 0xc350, 0xe1 },
+ { 0xc351, 0x88 },
+ { 0xc030, 0x14 },
+ { 0xc331, 0xf2 },
+ { 0xc551, 0x0f },
+ { 0xc552, 0xff },
+ { 0xc651, 0xc0 },
+ { 0xc550, 0xd0 },
+ { 0xc612, 0x00 },
+ { 0xc622, 0x00 },
+ { 0xc632, 0x00 },
+ { 0xc642, 0x00 },
+ { 0xc602, 0xf0 },
+ { 0xc600, 0xd0 },
+ { 0xcc02, 0x78 },
+ { 0xcc00, 0x90 },
+ { 0xc300, 0x3f },
+ { 0xc301, 0x1d },
+ { 0xc10b, 0x2e },
+ { 0xc10c, 0x36 },
+
+ { 0xd5a5, 0x00 },
+ { 0xd5a6, 0x6a },
+ { 0xd5a7, 0xaa },
+ { 0xd5a8, 0xaa },
+ { 0xd5aa, 0x00 },
+ { 0xd5ab, 0x16 },
+ { 0xd5ac, 0xdb },
+ { 0xd5ad, 0x6d },
+ { 0xd545, 0x09 },
+ { 0xd546, 0x30 },
+ { 0xd547, 0xf0 },
+ { 0xd548, 0xf0 },
+ { 0xd500, 0x20 },
+ { 0xc504, 0x3f },
+ { 0xc540, 0x00 },
+ { 0xc541, 0x0a },
+ { 0xc542, 0x1a },
+ { 0xc512, 0x00 },
+ { 0xc520, 0x40 },
+ { 0xc51b, 0x7f },
+ { 0xc51d, 0x0f },
+ { 0xc500, 0x40 },
+ { 0xc502, 0xde },
+ { 0xc507, 0x05 },
+ { 0xc509, 0x05 },
+ { 0xc510, 0x40 },
+ { 0xc518, 0xc0 },
+ { 0xc500, 0xc0 },
+
+ { 0xda0c, 0x00 },
+ { 0xda0d, 0x0b },
+ { 0xda0e, 0x55 },
+ { 0xda0f, 0x55 },
+ { 0xda04, 0x00 },
+ { 0xda05, 0x51 },
+ { 0xda06, 0xeb },
+ { 0xda07, 0x85 },
+ { 0xca16, 0x0f },
+ { 0xca17, 0x00 },
+ { 0xda09, 0x5d },
+ { 0xda0a, 0xc0 },
+ { 0xda11, 0x26 },
+ { 0xda12, 0x66 },
+ { 0xda13, 0x66 },
+ { 0xda14, 0x66 },
+ { 0xda16, 0x79 },
+ { 0xda17, 0x99 },
+ { 0xda18, 0x99 },
+ { 0xda19, 0x99 },
+ { 0xca09, 0x00 },
+ { 0xca0a, 0x07 },
+ { 0xca0b, 0x89 },
+ { 0xca0c, 0x61 },
+ { 0xca0e, 0x00 },
+ { 0xca0f, 0x03 },
+ { 0xca10, 0xc4 },
+ { 0xca11, 0xb0 },
+ { 0xdab6, 0x00 },
+ { 0xdab7, 0x01 },
+ { 0xdab8, 0x00 },
+ { 0xdab9, 0x00 },
+ { 0xdaba, 0x00 },
+ { 0xdabb, 0x00 },
+ { 0xd017, 0x0e },
+ { 0xca00, 0xcd },
+ { 0xc022, 0x84 },
+};
+
+#define RT1017_MAX_REG_NUM 0x4108ffff
+
+static const struct regmap_config rt1017_sdca_regmap = {
+ .reg_bits = 32,
+ .val_bits = 8,
+ .readable_reg = rt1017_sdca_readable_register,
+ .volatile_reg = rt1017_sdca_volatile_register,
+ .max_register = RT1017_MAX_REG_NUM,
+ .reg_defaults = rt1017_sdca_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(rt1017_sdca_reg_defaults),
+ .cache_type = REGCACHE_MAPLE,
+ .use_single_read = true,
+ .use_single_write = true,
+};
+
+static int rt1017_sdca_read_prop(struct sdw_slave *slave)
+{
+ struct sdw_slave_prop *prop = &slave->prop;
+ int nval;
+ int i, j;
+ u32 bit;
+ unsigned long addr;
+ struct sdw_dpn_prop *dpn;
+
+ prop->scp_int1_mask = SDW_SCP_INT1_BUS_CLASH | SDW_SCP_INT1_PARITY;
+ prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY;
+
+ prop->paging_support = true;
+
+ /* first we need to allocate memory for set bits in port lists
+ * port = 1 for AMP playback
+ * port = 2 for IV capture
+ */
+ prop->source_ports = BIT(2); /* BITMAP: 00000100 */
+ prop->sink_ports = BIT(1); /* BITMAP: 00000010 */
+
+ nval = hweight32(prop->source_ports);
+ prop->src_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->src_dpn_prop), GFP_KERNEL);
+ if (!prop->src_dpn_prop)
+ return -ENOMEM;
+
+ i = 0;
+ dpn = prop->src_dpn_prop;
+ addr = prop->source_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[i].num = bit;
+ dpn[i].type = SDW_DPN_FULL;
+ dpn[i].simple_ch_prep_sm = true;
+ dpn[i].ch_prep_timeout = 10;
+ i++;
+ }
+
+ /* do this again for sink now */
+ nval = hweight32(prop->sink_ports);
+ prop->sink_dpn_prop = devm_kcalloc(&slave->dev, nval,
+ sizeof(*prop->sink_dpn_prop), GFP_KERNEL);
+ if (!prop->sink_dpn_prop)
+ return -ENOMEM;
+
+ j = 0;
+ dpn = prop->sink_dpn_prop;
+ addr = prop->sink_ports;
+ for_each_set_bit(bit, &addr, 32) {
+ dpn[j].num = bit;
+ dpn[j].type = SDW_DPN_FULL;
+ dpn[j].simple_ch_prep_sm = true;
+ dpn[j].ch_prep_timeout = 10;
+ j++;
+ }
+
+ /* set the timeout values */
+ prop->clk_stop_timeout = 64;
+
+ return 0;
+}
+
+static int rt1017_sdca_io_init(struct device *dev, struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+
+ if (rt1017->hw_init)
+ return 0;
+
+ if (rt1017->first_hw_init) {
+ regcache_cache_only(rt1017->regmap, false);
+ regcache_cache_bypass(rt1017->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);
+
+ /* sw reset */
+ regmap_write(rt1017->regmap, 0xc000, 0x02);
+
+ /* initial settings - blind write */
+ regmap_multi_reg_write(rt1017->regmap, rt1017_blind_write,
+ ARRAY_SIZE(rt1017_blind_write));
+
+ if (rt1017->first_hw_init) {
+ regcache_cache_bypass(rt1017->regmap, false);
+ regcache_mark_dirty(rt1017->regmap);
+ } else
+ rt1017->first_hw_init = true;
+
+ /* Mark Slave initialization complete */
+ rt1017->hw_init = true;
+
+ pm_runtime_mark_last_busy(&slave->dev);
+ pm_runtime_put_autosuspend(&slave->dev);
+
+ dev_dbg(&slave->dev, "hw_init complete\n");
+ return 0;
+}
+
+static int rt1017_sdca_update_status(struct sdw_slave *slave,
+ enum sdw_slave_status status)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev);
+
+ if (status == SDW_SLAVE_UNATTACHED)
+ rt1017->hw_init = false;
+
+ /*
+ * Perform initialization only if slave status is present and
+ * hw_init flag is false
+ */
+ if (rt1017->hw_init || status != SDW_SLAVE_ATTACHED)
+ return 0;
+
+ /* perform I/O transfers required for Slave initialization */
+ return rt1017_sdca_io_init(&slave->dev, slave);
+}
+
+static const char * const rt1017_rx_data_ch_select[] = {
+ "Bypass",
+ "CN1",
+ "CN2",
+ "CN3",
+ "CN4",
+ "(1+2)/2",
+ "(1+3)/2",
+ "(1+4)/2",
+ "(2+3)/2",
+ "(2+4)/2",
+ "(3+4)/2",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1017_rx_data_ch_enum,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0),
+ 0, rt1017_rx_data_ch_select);
+
+static const struct snd_kcontrol_new rt1017_sdca_controls[] = {
+ /* UDMPU Cluster Selection */
+ SOC_ENUM("RX Channel Select", rt1017_rx_data_ch_enum),
+};
+
+static const struct snd_kcontrol_new rt1017_sto_dac =
+ SOC_DAPM_SINGLE("Switch",
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU, RT1017_SDCA_CTL_FU_MUTE, 0x1),
+ 0, 1, 1);
+
+static int rt1017_sdca_pde23_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *component = snd_soc_dapm_to_component(w->dapm);
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ unsigned char ps0 = 0x0, ps3 = 0x3;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_write(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps0);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_write(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0),
+ ps3);
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+static int rt1017_sdca_classd_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 rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(rt1017->regmap, RT1017_PWM_TRIM_1,
+ RT1017_PWM_FREQ_CTL_SRC_SEL_MASK, RT1017_PWM_FREQ_CTL_SRC_SEL_REG);
+ regmap_write(rt1017->regmap, RT1017_CLASSD_INT_1, 0x10);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt1017_sdca_feedback_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 rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x08);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(rt1017->regmap, 0xd017, 0x1f, 0x09);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget rt1017_sdca_dapm_widgets[] = {
+ /* Audio Interface */
+ SND_SOC_DAPM_AIF_IN("DP1RX", "DP1 Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT_E("DP2TX", "DP2 Capture", 0, SND_SOC_NOPM, 0, 0,
+ rt1017_sdca_feedback_event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SWITCH("DAC", SND_SOC_NOPM, 0, 0, &rt1017_sto_dac),
+
+ /* Output Lines */
+ SND_SOC_DAPM_PGA_E("CLASS D", SND_SOC_NOPM, 0, 0, NULL, 0,
+ rt1017_sdca_classd_event, SND_SOC_DAPM_POST_PMU),
+ SND_SOC_DAPM_OUTPUT("SPO"),
+
+ SND_SOC_DAPM_SUPPLY("PDE23", SND_SOC_NOPM, 0, 0,
+ rt1017_sdca_pde23_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_PGA("I Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("V Sense", SND_SOC_NOPM, 0, 0, NULL, 0),
+ SND_SOC_DAPM_SIGGEN("I Gen"),
+ SND_SOC_DAPM_SIGGEN("V Gen"),
+};
+
+static const struct snd_soc_dapm_route rt1017_sdca_dapm_routes[] = {
+
+ { "DAC", "Switch", "DP1RX" },
+ { "CLASS D", NULL, "DAC" },
+ { "CLASS D", NULL, "PDE23" },
+ { "SPO", NULL, "CLASS D" },
+
+ { "I Sense", NULL, "I Gen" },
+ { "V Sense", NULL, "V Gen" },
+ { "I Sense", NULL, "PDE23" },
+ { "V Sense", NULL, "PDE23" },
+ { "DP2TX", NULL, "I Sense" },
+ { "DP2TX", NULL, "V Sense" },
+};
+
+static struct sdw_slave_ops rt1017_sdca_slave_ops = {
+ .read_prop = rt1017_sdca_read_prop,
+ .update_status = rt1017_sdca_update_status,
+};
+
+static int rt1017_sdca_component_probe(struct snd_soc_component *component)
+{
+ int ret;
+
+ ret = pm_runtime_resume(component->dev);
+ if (ret < 0 && ret != -EACCES)
+ return ret;
+
+ return 0;
+}
+
+static void rt1017_sdca_component_remove(struct snd_soc_component *component)
+{
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+
+ regcache_cache_only(rt1017->regmap, true);
+}
+
+static const struct snd_soc_component_driver soc_sdca_component_rt1017 = {
+ .probe = rt1017_sdca_component_probe,
+ .remove = rt1017_sdca_component_remove,
+ .controls = rt1017_sdca_controls,
+ .num_controls = ARRAY_SIZE(rt1017_sdca_controls),
+ .dapm_widgets = rt1017_sdca_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt1017_sdca_dapm_widgets),
+ .dapm_routes = rt1017_sdca_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt1017_sdca_dapm_routes),
+ .endianness = 1,
+};
+
+static int rt1017_sdca_set_sdw_stream(struct snd_soc_dai *dai, void *sdw_stream,
+ int direction)
+{
+ snd_soc_dai_dma_data_set(dai, direction, sdw_stream);
+
+ return 0;
+}
+
+static void rt1017_sdca_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_soc_dai_set_dma_data(dai, substream, NULL);
+}
+
+static int rt1017_sdca_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_config stream_config;
+ struct sdw_port_config port_config;
+ enum sdw_data_direction direction;
+ struct sdw_stream_runtime *sdw_stream;
+ int retval, port, num_channels, ch_mask;
+ 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 (!rt1017->sdw_slave)
+ return -EINVAL;
+
+ /* SoundWire specific configuration */
+ /* port 1 for playback */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ direction = SDW_DATA_DIR_RX;
+ port = 1;
+ } else {
+ direction = SDW_DATA_DIR_TX;
+ port = 2;
+ }
+
+ num_channels = params_channels(params);
+ ch_mask = (1 << num_channels) - 1;
+
+ stream_config.frame_rate = params_rate(params);
+ stream_config.ch_count = num_channels;
+ stream_config.bps = snd_pcm_format_width(params_format(params));
+ stream_config.direction = direction;
+
+ port_config.ch_mask = ch_mask;
+ port_config.num = port;
+
+ dev_dbg(dai->dev, "frame_rate %d, ch_count %d, bps %d, direction %d, ch_mask %d, port: %d\n",
+ params_rate(params), num_channels, snd_pcm_format_width(params_format(params)),
+ direction, ch_mask, port);
+
+ retval = sdw_stream_add_slave(rt1017->sdw_slave, &stream_config,
+ &port_config, 1, sdw_stream);
+ if (retval) {
+ dev_err(dai->dev, "Unable to configure port\n");
+ return retval;
+ }
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 44100:
+ sampling_rate = RT1017_SDCA_RATE_44100HZ;
+ break;
+ case 48000:
+ sampling_rate = RT1017_SDCA_RATE_48000HZ;
+ break;
+ case 96000:
+ sampling_rate = RT1017_SDCA_RATE_96000HZ;
+ break;
+ case 192000:
+ sampling_rate = RT1017_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(rt1017->regmap,
+ SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0),
+ sampling_rate);
+
+ return 0;
+}
+
+static int rt1017_sdca_pcm_hw_free(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_component *component = dai->component;
+ struct rt1017_sdca_priv *rt1017 = snd_soc_component_get_drvdata(component);
+ struct sdw_stream_runtime *sdw_stream =
+ snd_soc_dai_get_dma_data(dai, substream);
+
+ if (!rt1017->sdw_slave)
+ return -EINVAL;
+
+ sdw_stream_remove_slave(rt1017->sdw_slave, sdw_stream);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops rt1017_sdca_ops = {
+ .hw_params = rt1017_sdca_pcm_hw_params,
+ .hw_free = rt1017_sdca_pcm_hw_free,
+ .set_stream = rt1017_sdca_set_sdw_stream,
+ .shutdown = rt1017_sdca_shutdown,
+};
+
+#define RT1017_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+#define RT1017_FORMATS (SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver rt1017_sdca_dai[] = {
+ {
+ .name = "rt1017-aif",
+ .playback = {
+ .stream_name = "DP1 Playback",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = RT1017_STEREO_RATES,
+ .formats = RT1017_FORMATS,
+ },
+ .capture = {
+ .stream_name = "DP2 Capture",
+ .channels_min = 1,
+ .channels_max = 1,
+ .rates = RT1017_STEREO_RATES,
+ .formats = RT1017_FORMATS,
+ },
+ .ops = &rt1017_sdca_ops,
+ },
+};
+
+static int rt1017_sdca_init(struct device *dev, struct regmap *regmap,
+ struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017;
+ int ret;
+
+ rt1017 = devm_kzalloc(dev, sizeof(*rt1017), GFP_KERNEL);
+ if (!rt1017)
+ return -ENOMEM;
+
+ dev_set_drvdata(dev, rt1017);
+ rt1017->sdw_slave = slave;
+ rt1017->regmap = regmap;
+
+ /*
+ * Mark hw_init to false
+ * HW init will be performed when device reports present
+ */
+ rt1017->hw_init = false;
+ rt1017->first_hw_init = false;
+
+ ret = devm_snd_soc_register_component(dev,
+ &soc_sdca_component_rt1017,
+ rt1017_sdca_dai,
+ ARRAY_SIZE(rt1017_sdca_dai));
+
+ return ret;
+}
+
+static int rt1017_sdca_sdw_probe(struct sdw_slave *slave,
+ const struct sdw_device_id *id)
+{
+ struct regmap *regmap;
+
+ /* Regmap Initialization */
+ regmap = devm_regmap_init_sdw(slave, &rt1017_sdca_regmap);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rt1017_sdca_init(&slave->dev, regmap, slave);
+}
+
+static int rt1017_sdca_sdw_remove(struct sdw_slave *slave)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev);
+
+ if (rt1017->first_hw_init)
+ pm_runtime_disable(&slave->dev);
+
+ return 0;
+}
+
+static const struct sdw_device_id rt1017_sdca_id[] = {
+ SDW_SLAVE_ENTRY_EXT(0x025d, 0x1017, 0x3, 0x1, 0),
+ {},
+};
+MODULE_DEVICE_TABLE(sdw, rt1017_sdca_id);
+
+static int __maybe_unused rt1017_sdca_dev_suspend(struct device *dev)
+{
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+
+ if (!rt1017->hw_init)
+ return 0;
+
+ regcache_cache_only(rt1017->regmap, true);
+
+ return 0;
+}
+
+#define RT1017_PROBE_TIMEOUT 5000
+
+static int __maybe_unused rt1017_sdca_dev_resume(struct device *dev)
+{
+ struct sdw_slave *slave = dev_to_sdw_dev(dev);
+ struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(dev);
+ unsigned long time;
+
+ if (!rt1017->first_hw_init)
+ return 0;
+
+ if (!slave->unattach_request)
+ goto regmap_sync;
+
+ time = wait_for_completion_timeout(&slave->initialization_complete,
+ msecs_to_jiffies(RT1017_PROBE_TIMEOUT));
+ if (!time) {
+ dev_err(&slave->dev, "Initialization not complete, timed out\n");
+ sdw_show_ping_status(slave->bus, true);
+
+ return -ETIMEDOUT;
+ }
+
+regmap_sync:
+ slave->unattach_request = 0;
+ regcache_cache_only(rt1017->regmap, false);
+ regcache_sync(rt1017->regmap);
+
+ return 0;
+}
+
+static const struct dev_pm_ops rt1017_sdca_pm = {
+ SET_SYSTEM_SLEEP_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume)
+ SET_RUNTIME_PM_OPS(rt1017_sdca_dev_suspend, rt1017_sdca_dev_resume, NULL)
+};
+
+static struct sdw_driver rt1017_sdca_sdw_driver = {
+ .driver = {
+ .name = "rt1017-sdca",
+ .owner = THIS_MODULE,
+ .pm = &rt1017_sdca_pm,
+ },
+ .probe = rt1017_sdca_sdw_probe,
+ .remove = rt1017_sdca_sdw_remove,
+ .ops = &rt1017_sdca_slave_ops,
+ .id_table = rt1017_sdca_id,
+};
+module_sdw_driver(rt1017_sdca_sdw_driver);
+
+MODULE_DESCRIPTION("ASoC RT1017 driver SDCA SDW");
+MODULE_AUTHOR("Derek Fang <derek.fang@realtek.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/rt1017-sdca-sdw.h b/sound/soc/codecs/rt1017-sdca-sdw.h
new file mode 100644
index 000000000000..4932b5dbe3c0
--- /dev/null
+++ b/sound/soc/codecs/rt1017-sdca-sdw.h
@@ -0,0 +1,183 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * rt1017-sdca-sdw.h -- RT1017 SDCA ALSA SoC audio driver header
+ *
+ * Copyright(c) 2023 Realtek Semiconductor Corp.
+ */
+
+#ifndef __RT1017_SDW_H__
+#define __RT1017_SDW_H__
+
+#include <linux/regmap.h>
+#include <linux/soundwire/sdw.h>
+#include <linux/soundwire/sdw_type.h>
+#include <linux/soundwire/sdw_registers.h>
+#include <sound/soc.h>
+
+/* RT1017 SDCA Control - function number */
+#define FUNC_NUM_SMART_AMP 0x04
+
+/* RT1017 SDCA entity */
+#define RT1017_SDCA_ENT_PDE23 0x31
+#define RT1017_SDCA_ENT_PDE22 0x33
+#define RT1017_SDCA_ENT_CS21 0x21
+#define RT1017_SDCA_ENT_SAPU29 0x29
+#define RT1017_SDCA_ENT_XU22 0x22
+#define RT1017_SDCA_ENT_FU 0x03
+#define RT1017_SDCA_ENT_UDMPU21 0x02
+
+/* RT1017 SDCA control */
+#define RT1017_SDCA_CTL_FS_INDEX 0x10
+#define RT1017_SDCA_CTL_REQ_POWER_STATE 0x01
+#define RT1017_SDCA_CTL_PROT_STAT 0x11
+#define RT1017_SDCA_CTL_BYPASS 0x01
+#define RT1017_SDCA_CTL_FU_MUTE 0x01
+#define RT1017_SDCA_CTL_FU_VOLUME 0x02
+#define RT1017_SDCA_CTL_UDMPU_CLUSTER 0x10
+
+
+#define RT1017_CLASSD_INT_1 0xd300
+#define RT1017_PWM_TRIM_1 0xd370
+
+
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_MASK (0x3 << 2)
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_EFUSE (0x2 << 2)
+#define RT1017_PWM_FREQ_CTL_SRC_SEL_REG (0x0 << 2)
+
+enum {
+ RT1017_SDCA_RATE_44100HZ = 0x8,
+ RT1017_SDCA_RATE_48000HZ = 0x9,
+ RT1017_SDCA_RATE_96000HZ = 0xb,
+ RT1017_SDCA_RATE_192000HZ = 0xd,
+};
+
+struct rt1017_sdca_priv {
+ struct snd_soc_component *component;
+ struct regmap *regmap;
+ struct sdw_slave *sdw_slave;
+ struct sdw_bus_params params;
+ bool hw_init;
+ bool first_hw_init;
+};
+
+static const struct reg_default rt1017_sdca_reg_defaults[] = {
+ { 0x3206, 0x00 },
+ { 0xc001, 0x43 },
+ { 0xc030, 0x54 },
+ { 0xc104, 0x8a },
+ { 0xc10b, 0x2f },
+ { 0xc10c, 0x2f },
+ { 0xc110, 0x49 },
+ { 0xc112, 0x10 },
+ { 0xc300, 0xff },
+ { 0xc301, 0xdd },
+ { 0xc318, 0x40 },
+ { 0xc325, 0x00 },
+ { 0xc326, 0x00 },
+ { 0xc327, 0x00 },
+ { 0xc328, 0x02 },
+ { 0xc331, 0xb2 },
+ { 0xc340, 0x02 },
+ { 0xc350, 0x21 },
+ { 0xc500, 0x00 },
+ { 0xc502, 0x00 },
+ { 0xc504, 0x3f },
+ { 0xc507, 0x1f },
+ { 0xc509, 0x1f },
+ { 0xc510, 0x40 },
+ { 0xc512, 0x00 },
+ { 0xc518, 0x02 },
+ { 0xc51b, 0x7f },
+ { 0xc51d, 0x0f },
+ { 0xc520, 0x00 },
+ { 0xc540, 0x80 },
+ { 0xc541, 0x00 },
+ { 0xc542, 0x0a },
+ { 0xc550, 0x80 },
+ { 0xc551, 0x0f },
+ { 0xc552, 0xff },
+ { 0xc600, 0x10 },
+ { 0xc602, 0x83 },
+ { 0xc612, 0x40 },
+ { 0xc622, 0x40 },
+ { 0xc632, 0x40 },
+ { 0xc642, 0x40 },
+ { 0xc651, 0x00 },
+ { 0xca00, 0xc1 },
+ { 0xca09, 0x00 },
+ { 0xca0a, 0x51 },
+ { 0xca0b, 0xeb },
+ { 0xca0c, 0x85 },
+ { 0xca0e, 0x00 },
+ { 0xca0f, 0x10 },
+ { 0xca10, 0x62 },
+ { 0xca11, 0x4d },
+ { 0xca16, 0x0f },
+ { 0xca17, 0x00 },
+ { 0xcb00, 0x10 },
+ { 0xcc00, 0x10 },
+ { 0xcc02, 0x0b },
+ { 0xd017, 0x09 },
+ { 0xd01a, 0x00 },
+ { 0xd01b, 0x00 },
+ { 0xd01c, 0x00 },
+ { 0xd101, 0xa0 },
+ { 0xd20c, 0x14 },
+ { 0xd300, 0x0f },
+ { 0xd370, 0x18 },
+ { 0xd500, 0x00 },
+ { 0xd545, 0x0b },
+ { 0xd546, 0xf9 },
+ { 0xd547, 0xb2 },
+ { 0xd548, 0xa9 },
+ { 0xd5a5, 0x00 },
+ { 0xd5a6, 0x00 },
+ { 0xd5a7, 0x00 },
+ { 0xd5a8, 0x00 },
+ { 0xd5aa, 0x00 },
+ { 0xd5ab, 0x00 },
+ { 0xd5ac, 0x00 },
+ { 0xd5ad, 0x00 },
+ { 0xda04, 0x03 },
+ { 0xda05, 0x33 },
+ { 0xda06, 0x33 },
+ { 0xda07, 0x33 },
+ { 0xda09, 0x5d },
+ { 0xda0a, 0xc0 },
+ { 0xda0c, 0x00 },
+ { 0xda0d, 0x01 },
+ { 0xda0e, 0x5d },
+ { 0xda0f, 0x86 },
+ { 0xda11, 0x20 },
+ { 0xda12, 0x00 },
+ { 0xda13, 0x00 },
+ { 0xda14, 0x00 },
+ { 0xda16, 0x7f },
+ { 0xda17, 0xff },
+ { 0xda18, 0xff },
+ { 0xda19, 0xff },
+ { 0xdab6, 0x00 },
+ { 0xdab7, 0x01 },
+ { 0xdab8, 0x00 },
+ { 0xdab9, 0x01 },
+ { 0xdaba, 0x00 },
+ { 0xdabb, 0x01 },
+ { 0xdb09, 0x0f },
+ { 0xdb0a, 0xff },
+ { 0xdb14, 0x00 },
+
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_UDMPU21,
+ RT1017_SDCA_CTL_UDMPU_CLUSTER, 0), 0x00 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_FU,
+ RT1017_SDCA_CTL_FU_MUTE, 0x01), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_XU22,
+ RT1017_SDCA_CTL_BYPASS, 0), 0x01 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_CS21,
+ RT1017_SDCA_CTL_FS_INDEX, 0), 0x09 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE23,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+ { SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1017_SDCA_ENT_PDE22,
+ RT1017_SDCA_CTL_REQ_POWER_STATE, 0), 0x03 },
+};
+
+#endif /* __RT1017_SDW_H__ */
diff --git a/sound/soc/codecs/rt1019.c b/sound/soc/codecs/rt1019.c
index fd55049920c1..ceb8baa6a20d 100644
--- a/sound/soc/codecs/rt1019.c
+++ b/sound/soc/codecs/rt1019.c
@@ -18,7 +18,6 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
-#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/codecs/rt1305.c b/sound/soc/codecs/rt1305.c
index 59895131e6e0..80888cbcf49c 100644
--- a/sound/soc/codecs/rt1305.c
+++ b/sound/soc/codecs/rt1305.c
@@ -12,10 +12,8 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
#include <sound/core.h>
diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c
index e566c8ddd3e9..63d4abf964d4 100644
--- a/sound/soc/codecs/rt1308-sdw.c
+++ b/sound/soc/codecs/rt1308-sdw.c
@@ -219,28 +219,17 @@ static int rt1308_io_init(struct device *dev, struct sdw_slave *slave)
if (rt1308->hw_init)
return 0;
- if (rt1308->first_hw_init) {
- regcache_cache_only(rt1308->regmap, false);
+ regcache_cache_only(rt1308->regmap, false);
+ if (rt1308->first_hw_init)
regcache_cache_bypass(rt1308->regmap, true);
- }
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
*/
- if (!rt1308->first_hw_init) {
- /* set autosuspend parameters */
- pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
- pm_runtime_use_autosuspend(&slave->dev);
-
+ if (!rt1308->first_hw_init)
/* 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);
regmap_read(rt1308->regmap, 0xcf01, &hibernation_flag);
@@ -637,6 +626,9 @@ static int rt1308_sdw_component_probe(struct snd_soc_component *component)
rt1308->component = component;
rt1308_sdw_parse_dt(rt1308, &rt1308->sdw_slave->dev);
+ if (!rt1308->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -699,6 +691,8 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap,
rt1308->sdw_slave = slave;
rt1308->regmap = regmap;
+ regcache_cache_only(rt1308->regmap, true);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -710,10 +704,27 @@ static int rt1308_sdw_init(struct device *dev, struct regmap *regmap,
&soc_component_sdw_rt1308,
rt1308_sdw_dai,
ARRAY_SIZE(rt1308_sdw_dai));
+ if (ret < 0)
+ return ret;
- dev_dbg(&slave->dev, "%s\n", __func__);
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
- return ret;
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
}
static int rt1308_sdw_probe(struct sdw_slave *slave,
@@ -726,17 +737,12 @@ static int rt1308_sdw_probe(struct sdw_slave *slave,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- rt1308_sdw_init(&slave->dev, regmap, slave);
-
- return 0;
+ return rt1308_sdw_init(&slave->dev, regmap, slave);
}
static int rt1308_sdw_remove(struct sdw_slave *slave)
{
- struct rt1308_sdw_priv *rt1308 = dev_get_drvdata(&slave->dev);
-
- if (rt1308->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/rt1308.c b/sound/soc/codecs/rt1308.c
index 9d07756cda40..86afb429d423 100644
--- a/sound/soc/codecs/rt1308.c
+++ b/sound/soc/codecs/rt1308.c
@@ -11,10 +11,8 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
-#include <linux/of_gpio.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c
index 721821d9e9af..47511f70119a 100644
--- a/sound/soc/codecs/rt1316-sdw.c
+++ b/sound/soc/codecs/rt1316-sdw.c
@@ -272,25 +272,16 @@ static int rt1316_io_init(struct device *dev, struct sdw_slave *slave)
if (rt1316->hw_init)
return 0;
+ regcache_cache_only(rt1316->regmap, false);
if (rt1316->first_hw_init) {
- regcache_cache_only(rt1316->regmap, false);
regcache_cache_bypass(rt1316->regmap, true);
} else {
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only 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);
@@ -424,6 +415,15 @@ static SOC_ENUM_SINGLE_DECL(rt1316_rx_data_ch_enum,
SDW_SDCA_CTL(FUNC_NUM_SMART_AMP, RT1316_SDCA_ENT_UDMPU21, RT1316_SDCA_CTL_UDMPU_CLUSTER, 0), 0,
rt1316_rx_data_ch_select);
+static const char * const rt1316_dac_output_vol_select[] = {
+ "immediately",
+ "zero crossing",
+ "zero crossing with soft ramp",
+};
+
+static SOC_ENUM_SINGLE_DECL(rt1316_dac_vol_ctl_enum,
+ 0xc010, 6, rt1316_dac_output_vol_select);
+
static const struct snd_kcontrol_new rt1316_snd_controls[] = {
/* I2S Data Channel Selection */
@@ -442,6 +442,9 @@ static const struct snd_kcontrol_new rt1316_snd_controls[] = {
/* IV mixer Control */
SOC_DOUBLE("Isense Mixer Switch", 0xc605, 2, 0, 1, 1),
SOC_DOUBLE("Vsense Mixer Switch", 0xc605, 3, 1, 1, 1),
+
+ /* DAC Output Volume Control */
+ SOC_ENUM("DAC Output Vol Control", rt1316_dac_vol_ctl_enum),
};
static const struct snd_kcontrol_new rt1316_sto_dac =
@@ -595,6 +598,9 @@ static int rt1316_sdw_component_probe(struct snd_soc_component *component)
rt1316->component = component;
rt1316_sdw_parse_dt(rt1316, &rt1316->sdw_slave->dev);
+ if (!rt1316->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -662,6 +668,8 @@ static int rt1316_sdw_init(struct device *dev, struct regmap *regmap,
rt1316->sdw_slave = slave;
rt1316->regmap = regmap;
+ regcache_cache_only(rt1316->regmap, true);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -673,10 +681,27 @@ static int rt1316_sdw_init(struct device *dev, struct regmap *regmap,
&soc_component_sdw_rt1316,
rt1316_sdw_dai,
ARRAY_SIZE(rt1316_sdw_dai));
+ if (ret < 0)
+ return ret;
- dev_dbg(&slave->dev, "%s\n", __func__);
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
- return ret;
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
}
static int rt1316_sdw_probe(struct sdw_slave *slave,
@@ -694,10 +719,7 @@ static int rt1316_sdw_probe(struct sdw_slave *slave,
static int rt1316_sdw_remove(struct sdw_slave *slave)
{
- struct rt1316_sdw_priv *rt1316 = dev_get_drvdata(&slave->dev);
-
- if (rt1316->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c
index 16d750102c8c..ff364bde4a08 100644
--- a/sound/soc/codecs/rt1318-sdw.c
+++ b/sound/soc/codecs/rt1318-sdw.c
@@ -408,25 +408,15 @@ static int rt1318_io_init(struct device *dev, struct sdw_slave *slave)
if (rt1318->hw_init)
return 0;
+ regcache_cache_only(rt1318->regmap, false);
if (rt1318->first_hw_init) {
- regcache_cache_only(rt1318->regmap, false);
regcache_cache_bypass(rt1318->regmap, true);
} else {
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only 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);
@@ -686,6 +676,9 @@ static int rt1318_sdw_component_probe(struct snd_soc_component *component)
rt1318->component = component;
+ if (!rt1318->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
dev_dbg(&rt1318->sdw_slave->dev, "%s pm_runtime_resume, ret=%d", __func__, ret);
if (ret < 0 && ret != -EACCES)
@@ -752,6 +745,8 @@ static int rt1318_sdw_init(struct device *dev, struct regmap *regmap,
rt1318->sdw_slave = slave;
rt1318->regmap = regmap;
+ regcache_cache_only(rt1318->regmap, true);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -763,8 +758,25 @@ static int rt1318_sdw_init(struct device *dev, struct regmap *regmap,
&soc_component_sdw_rt1318,
rt1318_sdw_dai,
ARRAY_SIZE(rt1318_sdw_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
- dev_dbg(&slave->dev, "%s\n", __func__);
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
return ret;
}
@@ -784,10 +796,7 @@ static int rt1318_sdw_probe(struct sdw_slave *slave,
static int rt1318_sdw_remove(struct sdw_slave *slave)
{
- struct rt1318_sdw_priv *rt1318 = dev_get_drvdata(&slave->dev);
-
- if (rt1318->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
index 362663abcb89..3ee6d85268ba 100644
--- a/sound/soc/codecs/rt5514-spi.c
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -15,7 +15,6 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/slab.h>
-#include <linux/gpio.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/regulator/consumer.h>
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index b3aac2373357..43fc7814fdde 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -17,7 +17,6 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/firmware.h>
-#include <linux/gpio.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index eceed8209787..15e1a62b9e57 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -12,11 +12,10 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
@@ -2571,7 +2570,7 @@ static void rt5640_enable_jack_detect(struct snd_soc_component *component,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
"rt5640", rt5640);
if (ret) {
- dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
+ dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
rt5640_disable_jack_detect(component);
return;
}
@@ -2626,7 +2625,7 @@ static void rt5640_enable_hda_jack_detect(
NULL, rt5640_irq, IRQF_TRIGGER_RISING | IRQF_ONESHOT,
"rt5640", rt5640);
if (ret) {
- dev_warn(component->dev, "Failed to reguest IRQ %d: %d\n", rt5640->irq, ret);
+ dev_warn(component->dev, "Failed to request IRQ %d: %d\n", rt5640->irq, ret);
rt5640->irq = -ENXIO;
return;
}
@@ -2812,8 +2811,8 @@ static int rt5640_suspend(struct snd_soc_component *component)
rt5640_reset(component);
regcache_cache_only(rt5640->regmap, true);
regcache_mark_dirty(rt5640->regmap);
- if (gpio_is_valid(rt5640->ldo1_en))
- gpio_set_value_cansleep(rt5640->ldo1_en, 0);
+ if (rt5640->ldo1_en)
+ gpiod_set_value_cansleep(rt5640->ldo1_en, 0);
return 0;
}
@@ -2822,8 +2821,8 @@ static int rt5640_resume(struct snd_soc_component *component)
{
struct rt5640_priv *rt5640 = snd_soc_component_get_drvdata(component);
- if (gpio_is_valid(rt5640->ldo1_en)) {
- gpio_set_value_cansleep(rt5640->ldo1_en, 1);
+ if (rt5640->ldo1_en) {
+ gpiod_set_value_cansleep(rt5640->ldo1_en, 1);
msleep(400);
}
@@ -2986,22 +2985,6 @@ static const struct acpi_device_id rt5640_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, rt5640_acpi_match);
#endif
-static int rt5640_parse_dt(struct rt5640_priv *rt5640, struct device_node *np)
-{
- rt5640->ldo1_en = of_get_named_gpio(np, "realtek,ldo1-en-gpios", 0);
- /*
- * LDO1_EN is optional (it may be statically tied on the board).
- * -ENOENT means that the property doesn't exist, i.e. there is no
- * GPIO, so is not an error. Any other error code means the property
- * exists, but could not be parsed.
- */
- if (!gpio_is_valid(rt5640->ldo1_en) &&
- (rt5640->ldo1_en != -ENOENT))
- return rt5640->ldo1_en;
-
- return 0;
-}
-
static int rt5640_i2c_probe(struct i2c_client *i2c)
{
struct rt5640_priv *rt5640;
@@ -3015,12 +2998,16 @@ static int rt5640_i2c_probe(struct i2c_client *i2c)
return -ENOMEM;
i2c_set_clientdata(i2c, rt5640);
- if (i2c->dev.of_node) {
- ret = rt5640_parse_dt(rt5640, i2c->dev.of_node);
- if (ret)
- return ret;
- } else
- rt5640->ldo1_en = -EINVAL;
+ rt5640->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5640->ldo1_en))
+ return PTR_ERR(rt5640->ldo1_en);
+
+ if (rt5640->ldo1_en) {
+ gpiod_set_consumer_name(rt5640->ldo1_en, "RT5640 LDO1_EN");
+ msleep(400);
+ }
rt5640->regmap = devm_regmap_init_i2c(i2c, &rt5640_regmap);
if (IS_ERR(rt5640->regmap)) {
@@ -3030,18 +3017,6 @@ static int rt5640_i2c_probe(struct i2c_client *i2c)
return ret;
}
- if (gpio_is_valid(rt5640->ldo1_en)) {
- ret = devm_gpio_request_one(&i2c->dev, rt5640->ldo1_en,
- GPIOF_OUT_INIT_HIGH,
- "RT5640 LDO1_EN");
- if (ret < 0) {
- dev_err(&i2c->dev, "Failed to request LDO1_EN %d: %d\n",
- rt5640->ldo1_en, ret);
- return ret;
- }
- msleep(400);
- }
-
regmap_read(rt5640->regmap, RT5640_VENDOR_ID2, &val);
if (val != RT5640_DEVICE_ID) {
dev_err(&i2c->dev,
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 9847a1ae01f4..94b9a502f7f9 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -2138,7 +2138,7 @@ struct rt5640_priv {
struct regmap *regmap;
struct clk *mclk;
- int ldo1_en; /* GPIO for LDO1_EN */
+ struct gpio_desc *ldo1_en; /* GPIO for LDO1_EN */
int irq;
int jd_gpio_irq;
int sysclk;
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 5be5ec0260e9..038d93e20883 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -14,7 +14,6 @@
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/acpi.h>
#include <linux/dmi.h>
@@ -81,6 +80,7 @@ static const struct reg_sequence init_list[] = {
static const struct reg_sequence rt5650_init_list[] = {
{0xf6, 0x0100},
+ {RT5645_PWR_ANLG1, 0x02},
};
static const struct reg_default rt5645_reg[] = {
@@ -1697,6 +1697,9 @@ static void hp_amp_power(struct snd_soc_component *component, int on)
regmap_write(rt5645->regmap, RT5645_PR_BASE +
RT5645_MAMP_INT_REG2, 0xfc00);
snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R);
msleep(90);
} else {
/* depop parameters */
@@ -1744,7 +1747,8 @@ static void hp_amp_power(struct snd_soc_component *component, int on)
snd_soc_component_write(component, RT5645_DEPOP_M2, 0x1140);
msleep(100);
snd_soc_component_write(component, RT5645_DEPOP_M1, 0x0001);
-
+ snd_soc_component_update_bits(component, RT5645_PWR_ANLG1,
+ RT5645_PWR_HP_L | RT5645_PWR_HP_R, 0);
} else {
snd_soc_component_update_bits(component, RT5645_DEPOP_M1,
RT5645_HP_SG_MASK |
@@ -3151,7 +3155,7 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse
unsigned int val;
if (jack_insert) {
- regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06);
+ regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0206);
/* for jack type detect */
snd_soc_dapm_force_enable_pin(dapm, "LDO2");
@@ -3196,6 +3200,8 @@ static int rt5645_jack_detect(struct snd_soc_component *component, int jack_inse
if (rt5645->pdata.level_trigger_irq)
regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
+
+ regmap_write(rt5645->regmap, RT5645_CHARGE_PUMP, 0x0e06);
} else { /* jack out */
rt5645->jack_type = 0;
@@ -4004,13 +4010,13 @@ static int rt5645_i2c_probe(struct i2c_client *i2c)
regmap_write(rt5645->regmap, RT5645_AD_DA_MIXER, 0x8080);
- ret = regmap_register_patch(rt5645->regmap, init_list,
+ ret = regmap_multi_reg_write(rt5645->regmap, init_list,
ARRAY_SIZE(init_list));
if (ret != 0)
dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
if (rt5645->codec_type == CODEC_TYPE_RT5650) {
- ret = regmap_register_patch(rt5645->regmap, rt5650_init_list,
+ ret = regmap_multi_reg_write(rt5645->regmap, rt5650_init_list,
ARRAY_SIZE(rt5650_init_list));
if (ret != 0)
dev_warn(&i2c->dev, "Apply rt5650 patch failed: %d\n",
@@ -4221,8 +4227,7 @@ static int __maybe_unused rt5645_sys_resume(struct device *dev)
if (rt5645->hp_jack) {
rt5645->jack_type = 0;
- queue_delayed_work(system_power_efficient_wq,
- &rt5645->jack_detect_work, msecs_to_jiffies(0));
+ rt5645_jack_detect_work(&rt5645->jack_detect_work.work);
}
return 0;
}
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index df6f0d769bbd..a061028a16d8 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -16,7 +16,6 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <sound/core.h>
#include <sound/pcm.h>
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
index eade087b2d8b..0cecfd602415 100644
--- a/sound/soc/codecs/rt5660.c
+++ b/sound/soc/codecs/rt5660.c
@@ -11,11 +11,9 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
-#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 525713c33d71..a39de4a7df00 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -15,8 +15,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -4659,9 +4658,6 @@ static int rt5665_parse_dt(struct rt5665_priv *rt5665, struct device *dev)
of_property_read_u32(dev->of_node, "realtek,jd-src",
&rt5665->pdata.jd_src);
- rt5665->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
- "realtek,ldo1-en-gpios", 0);
-
return 0;
}
@@ -4795,10 +4791,13 @@ static int rt5665_i2c_probe(struct i2c_client *i2c)
return ret;
}
- if (gpio_is_valid(rt5665->pdata.ldo1_en)) {
- if (devm_gpio_request_one(&i2c->dev, rt5665->pdata.ldo1_en,
- GPIOF_OUT_INIT_HIGH, "rt5665"))
- dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n");
+
+ rt5665->gpiod_ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5665->gpiod_ldo1_en)) {
+ dev_err(&i2c->dev, "Failed gpio request ldo1_en\n");
+ return PTR_ERR(rt5665->gpiod_ldo1_en);
}
/* Sleep for 300 ms miniumum */
diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c
index f04c810fd710..4623b3e62487 100644
--- a/sound/soc/codecs/rt5668.c
+++ b/sound/soc/codecs/rt5668.c
@@ -15,8 +15,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
#include <sound/core.h>
@@ -43,6 +42,7 @@ static const char *rt5668_supply_names[RT5668_NUM_SUPPLIES] = {
struct rt5668_priv {
struct snd_soc_component *component;
struct rt5668_platform_data pdata;
+ struct gpio_desc *ldo1_en;
struct regmap *regmap;
struct snd_soc_jack *hs_jack;
struct regulator_bulk_data supplies[RT5668_NUM_SUPPLIES];
@@ -2393,9 +2393,6 @@ static int rt5668_parse_dt(struct rt5668_priv *rt5668, struct device *dev)
of_property_read_u32(dev->of_node, "realtek,jd-src",
&rt5668->pdata.jd_src);
- rt5668->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
- "realtek,ldo1-en-gpios", 0);
-
return 0;
}
@@ -2497,10 +2494,12 @@ static int rt5668_i2c_probe(struct i2c_client *i2c)
return ret;
}
- if (gpio_is_valid(rt5668->pdata.ldo1_en)) {
- if (devm_gpio_request_one(&i2c->dev, rt5668->pdata.ldo1_en,
- GPIOF_OUT_INIT_HIGH, "rt5668"))
- dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n");
+ rt5668->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5668->ldo1_en)) {
+ dev_err(&i2c->dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5668->ldo1_en);
}
/* Sleep for 300 ms miniumum */
diff --git a/sound/soc/codecs/rt5682-i2c.c b/sound/soc/codecs/rt5682-i2c.c
index fb8ffb5b2ff6..b05b4f73d8aa 100644
--- a/sound/soc/codecs/rt5682-i2c.c
+++ b/sound/soc/codecs/rt5682-i2c.c
@@ -15,8 +15,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -170,11 +169,9 @@ static int rt5682_i2c_probe(struct i2c_client *i2c)
return ret;
}
- if (gpio_is_valid(rt5682->pdata.ldo1_en)) {
- if (devm_gpio_request_one(&i2c->dev, rt5682->pdata.ldo1_en,
- GPIOF_OUT_INIT_HIGH, "rt5682"))
- dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n");
- }
+ ret = rt5682_get_ldo1(rt5682, &i2c->dev);
+ if (ret)
+ return ret;
/* Sleep for 300 ms miniumum */
usleep_range(300000, 350000);
diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c
index 4968a8c0064d..e67c2e19cb1a 100644
--- a/sound/soc/codecs/rt5682-sdw.c
+++ b/sound/soc/codecs/rt5682-sdw.c
@@ -12,8 +12,6 @@
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/mutex.h>
@@ -322,6 +320,14 @@ static int rt5682_sdw_init(struct device *dev, struct regmap *regmap,
return ret;
}
+
+ ret = rt5682_get_ldo1(rt5682, dev);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(rt5682->sdw_regmap, true);
+ regcache_cache_only(rt5682->regmap, true);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -336,7 +342,25 @@ static int rt5682_sdw_init(struct device *dev, struct regmap *regmap,
ret = devm_snd_soc_register_component(dev,
&rt5682_soc_component_dev,
rt5682_dai, ARRAY_SIZE(rt5682_dai));
- dev_dbg(&slave->dev, "%s\n", __func__);
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
return ret;
}
@@ -352,30 +376,20 @@ static int rt5682_io_init(struct device *dev, struct sdw_slave *slave)
if (rt5682->hw_init)
return 0;
+ regcache_cache_only(rt5682->sdw_regmap, false);
+ regcache_cache_only(rt5682->regmap, false);
+ if (rt5682->first_hw_init)
+ regcache_cache_bypass(rt5682->regmap, true);
+
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
*/
- if (!rt5682->first_hw_init) {
- /* set autosuspend parameters */
- pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
- pm_runtime_use_autosuspend(&slave->dev);
-
+ if (!rt5682->first_hw_init)
/* 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);
- if (rt5682->first_hw_init) {
- regcache_cache_only(rt5682->regmap, false);
- regcache_cache_bypass(rt5682->regmap, true);
- }
-
while (loop > 0) {
regmap_read(rt5682->regmap, RT5682_DEVICE_ID, &val);
if (val == DEVICE_ID)
@@ -674,9 +688,7 @@ static int rt5682_sdw_probe(struct sdw_slave *slave,
if (IS_ERR(regmap))
return -EINVAL;
- rt5682_sdw_init(&slave->dev, regmap, slave);
-
- return 0;
+ return rt5682_sdw_init(&slave->dev, regmap, slave);
}
static int rt5682_sdw_remove(struct sdw_slave *slave)
@@ -686,8 +698,7 @@ static int rt5682_sdw_remove(struct sdw_slave *slave)
if (rt5682->hw_init)
cancel_delayed_work_sync(&rt5682->jack_detect_work);
- if (rt5682->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
@@ -707,6 +718,7 @@ static int __maybe_unused rt5682_dev_suspend(struct device *dev)
cancel_delayed_work_sync(&rt5682->jack_detect_work);
+ regcache_cache_only(rt5682->sdw_regmap, true);
regcache_cache_only(rt5682->regmap, true);
regcache_mark_dirty(rt5682->regmap);
@@ -771,6 +783,7 @@ static int __maybe_unused rt5682_dev_resume(struct device *dev)
regmap_sync:
slave->unattach_request = 0;
+ regcache_cache_only(rt5682->sdw_regmap, false);
regcache_cache_only(rt5682->regmap, false);
regcache_sync(rt5682->regmap);
diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c
index 5d992543b791..e3aca9c785a0 100644
--- a/sound/soc/codecs/rt5682.c
+++ b/sound/soc/codecs/rt5682.c
@@ -15,8 +15,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -1017,6 +1016,9 @@ static int rt5682_set_jack_detect(struct snd_soc_component *component,
rt5682->hs_jack = hs_jack;
+ if (rt5682->is_sdw && !rt5682->first_hw_init)
+ return 0;
+
if (!hs_jack) {
regmap_update_bits(rt5682->regmap, RT5682_IRQ_CTRL_2,
RT5682_JD1_EN_MASK, RT5682_JD1_DIS);
@@ -3091,9 +3093,6 @@ int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
device_property_read_u32(dev, "realtek,dmic-delay-ms",
&rt5682->pdata.dmic_delay);
- rt5682->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
- "realtek,ldo1-en-gpios", 0);
-
if (device_property_read_string_array(dev, "clock-output-names",
rt5682->pdata.dai_clk_names,
RT5682_DAI_NUM_CLKS) < 0)
@@ -3108,6 +3107,20 @@ int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev)
}
EXPORT_SYMBOL_GPL(rt5682_parse_dt);
+int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev)
+{
+ rt5682->ldo1_en = devm_gpiod_get_optional(dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5682->ldo1_en)) {
+ dev_err(dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5682->ldo1_en);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5682_get_ldo1);
+
void rt5682_calibrate(struct rt5682_priv *rt5682)
{
int value, count;
diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h
index 1a43d595f341..b2d9e87af259 100644
--- a/sound/soc/codecs/rt5682.h
+++ b/sound/soc/codecs/rt5682.h
@@ -11,6 +11,7 @@
#include <sound/rt5682.h>
#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
@@ -1430,6 +1431,7 @@ struct rt5682_priv {
struct snd_soc_component *component;
struct device *i2c_dev;
struct rt5682_platform_data pdata;
+ struct gpio_desc *ldo1_en;
struct regmap *regmap;
struct regmap *sdw_regmap;
struct snd_soc_jack *hs_jack;
@@ -1481,6 +1483,7 @@ int rt5682_register_component(struct device *dev);
void rt5682_calibrate(struct rt5682_priv *rt5682);
void rt5682_reset(struct rt5682_priv *rt5682);
int rt5682_parse_dt(struct rt5682_priv *rt5682, struct device *dev);
+int rt5682_get_ldo1(struct rt5682_priv *rt5682, struct device *dev);
int rt5682_register_dai_clks(struct rt5682_priv *rt5682);
diff --git a/sound/soc/codecs/rt5682s.c b/sound/soc/codecs/rt5682s.c
index c77c675bd5f5..68ac5ea50396 100644
--- a/sound/soc/codecs/rt5682s.c
+++ b/sound/soc/codecs/rt5682s.c
@@ -15,8 +15,7 @@
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/mutex.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -2973,9 +2972,6 @@ static int rt5682s_parse_dt(struct rt5682s_priv *rt5682s, struct device *dev)
device_property_read_u32(dev, "realtek,amic-delay-ms",
&rt5682s->pdata.amic_delay);
- rt5682s->pdata.ldo1_en = of_get_named_gpio(dev->of_node,
- "realtek,ldo1-en-gpios", 0);
-
if (device_property_read_string_array(dev, "clock-output-names",
rt5682s->pdata.dai_clk_names,
RT5682S_DAI_NUM_CLKS) < 0)
@@ -3172,10 +3168,12 @@ static int rt5682s_i2c_probe(struct i2c_client *i2c)
return ret;
}
- if (gpio_is_valid(rt5682s->pdata.ldo1_en)) {
- if (devm_gpio_request_one(&i2c->dev, rt5682s->pdata.ldo1_en,
- GPIOF_OUT_INIT_HIGH, "rt5682s"))
- dev_err(&i2c->dev, "Fail gpio_request gpio_ldo\n");
+ rt5682s->ldo1_en = devm_gpiod_get_optional(&i2c->dev,
+ "realtek,ldo1-en",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(rt5682s->ldo1_en)) {
+ dev_err(&i2c->dev, "Fail gpio request ldo1_en\n");
+ return PTR_ERR(rt5682s->ldo1_en);
}
/* Sleep for 50 ms minimum */
diff --git a/sound/soc/codecs/rt5682s.h b/sound/soc/codecs/rt5682s.h
index caa7733b430f..1d79d432d0d8 100644
--- a/sound/soc/codecs/rt5682s.h
+++ b/sound/soc/codecs/rt5682s.h
@@ -11,6 +11,7 @@
#include <sound/rt5682s.h>
#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
@@ -1446,6 +1447,7 @@ enum {
struct rt5682s_priv {
struct snd_soc_component *component;
struct rt5682s_platform_data pdata;
+ struct gpio_desc *ldo1_en;
struct regmap *regmap;
struct snd_soc_jack *hs_jack;
struct regulator_bulk_data supplies[RT5682S_NUM_SUPPLIES];
diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c
index 8b28e47775cc..52c33d56b143 100644
--- a/sound/soc/codecs/rt700-sdw.c
+++ b/sound/soc/codecs/rt700-sdw.c
@@ -452,9 +452,7 @@ static int rt700_sdw_probe(struct sdw_slave *slave,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- rt700_init(&slave->dev, sdw_regmap, regmap, slave);
-
- return 0;
+ return rt700_init(&slave->dev, sdw_regmap, regmap, slave);
}
static int rt700_sdw_remove(struct sdw_slave *slave)
@@ -466,8 +464,7 @@ static int rt700_sdw_remove(struct sdw_slave *slave)
cancel_delayed_work_sync(&rt700->jack_btn_check_work);
}
- if (rt700->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/rt700.c b/sound/soc/codecs/rt700.c
index a04b9246256b..0ebf344a1b60 100644
--- a/sound/soc/codecs/rt700.c
+++ b/sound/soc/codecs/rt700.c
@@ -320,6 +320,10 @@ static int rt700_set_jack_detect(struct snd_soc_component *component,
rt700->hs_jack = hs_jack;
+ /* we can only resume if the device was initialized at least once */
+ if (!rt700->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
if (ret != -EACCES) {
@@ -823,6 +827,9 @@ static int rt700_probe(struct snd_soc_component *component)
rt700->component = component;
+ if (!rt700->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -1099,6 +1106,8 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap,
rt700->sdw_regmap = sdw_regmap;
rt700->regmap = regmap;
+ regcache_cache_only(rt700->regmap, true);
+
mutex_init(&rt700->disable_irq_lock);
INIT_DELAYED_WORK(&rt700->jack_detect_work,
@@ -1117,10 +1126,26 @@ int rt700_init(struct device *dev, struct regmap *sdw_regmap,
&soc_codec_dev_rt700,
rt700_dai,
ARRAY_SIZE(rt700_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
dev_dbg(&slave->dev, "%s\n", __func__);
- return ret;
+ return 0;
}
int rt700_io_init(struct device *dev, struct sdw_slave *slave)
@@ -1132,28 +1157,17 @@ int rt700_io_init(struct device *dev, struct sdw_slave *slave)
if (rt700->hw_init)
return 0;
- if (rt700->first_hw_init) {
- regcache_cache_only(rt700->regmap, false);
+ regcache_cache_only(rt700->regmap, false);
+ if (rt700->first_hw_init)
regcache_cache_bypass(rt700->regmap, true);
- }
/*
* PM runtime is only enabled when a Slave reports as Attached
*/
- if (!rt700->first_hw_init) {
- /* set autosuspend parameters */
- pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
- pm_runtime_use_autosuspend(&slave->dev);
-
- /* update count of parent 'active' children */
+ if (!rt700->first_hw_init)
+ /* PM runtime status is marked as 'active' only when a Slave reports as Attached */
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);
/* reset */
diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c
index 23f23f714b39..935e597022d3 100644
--- a/sound/soc/codecs/rt711-sdca-sdw.c
+++ b/sound/soc/codecs/rt711-sdca-sdw.c
@@ -366,8 +366,7 @@ static int rt711_sdca_sdw_remove(struct sdw_slave *slave)
cancel_delayed_work_sync(&rt711->jack_btn_check_work);
}
- if (rt711->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
mutex_destroy(&rt711->calibrate_mutex);
mutex_destroy(&rt711->disable_irq_lock);
diff --git a/sound/soc/codecs/rt711-sdca.c b/sound/soc/codecs/rt711-sdca.c
index 07640d2f6e56..447154cb6010 100644
--- a/sound/soc/codecs/rt711-sdca.c
+++ b/sound/soc/codecs/rt711-sdca.c
@@ -507,6 +507,10 @@ static int rt711_sdca_set_jack_detect(struct snd_soc_component *component,
rt711->hs_jack = hs_jack;
+ /* we can only resume if the device was initialized at least once */
+ if (!rt711->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
if (ret != -EACCES) {
@@ -1215,6 +1219,9 @@ static int rt711_sdca_probe(struct snd_soc_component *component)
rt711_sdca_parse_dt(rt711, &rt711->slave->dev);
rt711->component = component;
+ if (!rt711->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -1406,6 +1413,9 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap,
rt711->regmap = regmap;
rt711->mbq_regmap = mbq_regmap;
+ regcache_cache_only(rt711->regmap, true);
+ regcache_cache_only(rt711->mbq_regmap, true);
+
mutex_init(&rt711->calibrate_mutex);
mutex_init(&rt711->disable_irq_lock);
@@ -1431,9 +1441,27 @@ int rt711_sdca_init(struct device *dev, struct regmap *regmap,
rt711_sdca_dai,
ARRAY_SIZE(rt711_sdca_dai));
- dev_dbg(&slave->dev, "%s\n", __func__);
+ if (ret < 0)
+ return ret;
- return ret;
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
}
static void rt711_sdca_vd0_io_init(struct rt711_sdca_priv *rt711)
@@ -1500,27 +1528,19 @@ int rt711_sdca_io_init(struct device *dev, struct sdw_slave *slave)
if (rt711->hw_init)
return 0;
+ regcache_cache_only(rt711->regmap, false);
+ regcache_cache_only(rt711->mbq_regmap, false);
+
if (rt711->first_hw_init) {
- regcache_cache_only(rt711->regmap, false);
regcache_cache_bypass(rt711->regmap, true);
- regcache_cache_only(rt711->mbq_regmap, false);
regcache_cache_bypass(rt711->mbq_regmap, true);
} else {
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only 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);
diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c
index 33dced388f9e..3f5773310ae8 100644
--- a/sound/soc/codecs/rt711-sdw.c
+++ b/sound/soc/codecs/rt711-sdw.c
@@ -453,9 +453,7 @@ static int rt711_sdw_probe(struct sdw_slave *slave,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- rt711_init(&slave->dev, sdw_regmap, regmap, slave);
-
- return 0;
+ return rt711_init(&slave->dev, sdw_regmap, regmap, slave);
}
static int rt711_sdw_remove(struct sdw_slave *slave)
@@ -468,8 +466,7 @@ static int rt711_sdw_remove(struct sdw_slave *slave)
cancel_work_sync(&rt711->calibration_work);
}
- if (rt711->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
mutex_destroy(&rt711->calibrate_mutex);
mutex_destroy(&rt711->disable_irq_lock);
diff --git a/sound/soc/codecs/rt711.c b/sound/soc/codecs/rt711.c
index af53cbcc7bf2..66eaed13b0d6 100644
--- a/sound/soc/codecs/rt711.c
+++ b/sound/soc/codecs/rt711.c
@@ -462,6 +462,10 @@ static int rt711_set_jack_detect(struct snd_soc_component *component,
rt711->hs_jack = hs_jack;
+ /* we can only resume if the device was initialized at least once */
+ if (!rt711->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
if (ret != -EACCES) {
@@ -941,6 +945,9 @@ static int rt711_probe(struct snd_soc_component *component)
rt711_parse_dt(rt711, &rt711->slave->dev);
rt711->component = component;
+ if (!rt711->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -1183,6 +1190,8 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap,
rt711->sdw_regmap = sdw_regmap;
rt711->regmap = regmap;
+ regcache_cache_only(rt711->regmap, true);
+
mutex_init(&rt711->calibrate_mutex);
mutex_init(&rt711->disable_irq_lock);
@@ -1204,8 +1213,25 @@ int rt711_init(struct device *dev, struct regmap *sdw_regmap,
&soc_codec_dev_rt711,
rt711_dai,
ARRAY_SIZE(rt711_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
- dev_dbg(&slave->dev, "%s\n", __func__);
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
return ret;
}
@@ -1219,28 +1245,17 @@ int rt711_io_init(struct device *dev, struct sdw_slave *slave)
if (rt711->hw_init)
return 0;
- if (rt711->first_hw_init) {
- regcache_cache_only(rt711->regmap, false);
+ regcache_cache_only(rt711->regmap, false);
+ if (rt711->first_hw_init)
regcache_cache_bypass(rt711->regmap, true);
- }
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
*/
- if (!rt711->first_hw_init) {
- /* set autosuspend parameters */
- pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
- pm_runtime_use_autosuspend(&slave->dev);
-
+ if (!rt711->first_hw_init)
/* 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);
rt711_reset(rt711->regmap);
diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c
index 869cc7bfd178..ba08d03e717c 100644
--- a/sound/soc/codecs/rt712-sdca-dmic.c
+++ b/sound/soc/codecs/rt712-sdca-dmic.c
@@ -182,27 +182,18 @@ static int rt712_sdca_dmic_io_init(struct device *dev, struct sdw_slave *slave)
if (rt712->hw_init)
return 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_only(rt712->mbq_regmap, false);
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
+ * PM runtime status is marked as 'active' only 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);
@@ -608,6 +599,9 @@ static int rt712_sdca_dmic_probe(struct snd_soc_component *component)
rt712->component = component;
+ if (!rt712->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -777,6 +771,9 @@ static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap,
rt712->regmap = regmap;
rt712->mbq_regmap = mbq_regmap;
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -791,10 +788,27 @@ static int rt712_sdca_dmic_init(struct device *dev, struct regmap *regmap,
&soc_sdca_dev_rt712_dmic,
rt712_sdca_dmic_dai,
ARRAY_SIZE(rt712_sdca_dmic_dai));
+ if (ret < 0)
+ return ret;
- dev_dbg(&slave->dev, "%s\n", __func__);
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
- return ret;
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
}
@@ -954,10 +968,7 @@ static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *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);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c
index 6bc50396a0f6..6b644a89c589 100644
--- a/sound/soc/codecs/rt712-sdca-sdw.c
+++ b/sound/soc/codecs/rt712-sdca-sdw.c
@@ -363,8 +363,7 @@ static int rt712_sdca_sdw_remove(struct sdw_slave *slave)
cancel_delayed_work_sync(&rt712->jack_btn_check_work);
}
- if (rt712->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
mutex_destroy(&rt712->calibrate_mutex);
mutex_destroy(&rt712->disable_irq_lock);
diff --git a/sound/soc/codecs/rt712-sdca.c b/sound/soc/codecs/rt712-sdca.c
index 89d245655ca4..7077ff6ba1f4 100644
--- a/sound/soc/codecs/rt712-sdca.c
+++ b/sound/soc/codecs/rt712-sdca.c
@@ -453,6 +453,9 @@ static int rt712_sdca_set_jack_detect(struct snd_soc_component *component,
rt712->hs_jack = hs_jack;
+ if (!rt712->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume_and_get(component->dev);
if (ret < 0) {
if (ret != -EACCES) {
@@ -960,6 +963,9 @@ static int rt712_sdca_probe(struct snd_soc_component *component)
rt712_sdca_parse_dt(rt712, &rt712->slave->dev);
rt712->component = component;
+ if (!rt712->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -1183,6 +1189,9 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap,
rt712->regmap = regmap;
rt712->mbq_regmap = mbq_regmap;
+ regcache_cache_only(rt712->regmap, true);
+ regcache_cache_only(rt712->mbq_regmap, true);
+
mutex_init(&rt712->calibrate_mutex);
mutex_init(&rt712->disable_irq_lock);
@@ -1207,10 +1216,27 @@ int rt712_sdca_init(struct device *dev, struct regmap *regmap,
else
ret = devm_snd_soc_register_component(dev,
&soc_sdca_dev_rt712, rt712_sdca_dai, 1);
+ if (ret < 0)
+ return ret;
- dev_dbg(&slave->dev, "%s\n", __func__);
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
- return ret;
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ return 0;
}
int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
@@ -1224,27 +1250,18 @@ int rt712_sdca_io_init(struct device *dev, struct sdw_slave *slave)
if (rt712->hw_init)
return 0;
+ regcache_cache_only(rt712->regmap, false);
+ regcache_cache_only(rt712->mbq_regmap, false);
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
+ * PM runtime status is marked as 'active' only 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);
diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c
index df10916bab46..ab54a67a27eb 100644
--- a/sound/soc/codecs/rt715-sdca-sdw.c
+++ b/sound/soc/codecs/rt715-sdca-sdw.c
@@ -193,10 +193,7 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave,
static int rt715_sdca_sdw_remove(struct sdw_slave *slave)
{
- struct rt715_sdca_priv *rt715 = dev_get_drvdata(&slave->dev);
-
- if (rt715->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/rt715-sdca.c b/sound/soc/codecs/rt715-sdca.c
index b989f907784b..9fa96fd83d4a 100644
--- a/sound/soc/codecs/rt715-sdca.c
+++ b/sound/soc/codecs/rt715-sdca.c
@@ -761,8 +761,12 @@ static const struct snd_soc_dapm_route rt715_sdca_audio_map[] = {
static int rt715_sdca_probe(struct snd_soc_component *component)
{
+ struct rt715_sdca_priv *rt715 = snd_soc_component_get_drvdata(component);
int ret;
+ if (!rt715->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -977,6 +981,10 @@ int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
rt715->regmap = regmap;
rt715->mbq_regmap = mbq_regmap;
rt715->hw_sdw_ver = slave->id.sdw_version;
+
+ regcache_cache_only(rt715->regmap, true);
+ regcache_cache_only(rt715->mbq_regmap, true);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -988,6 +996,25 @@ int rt715_sdca_init(struct device *dev, struct regmap *mbq_regmap,
&soc_codec_dev_rt715_sdca,
rt715_sdca_dai,
ARRAY_SIZE(rt715_sdca_dai));
+ if (ret < 0)
+ return ret;
+
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ dev_dbg(dev, "%s\n", __func__);
return ret;
}
@@ -1000,22 +1027,16 @@ int rt715_sdca_io_init(struct device *dev, struct sdw_slave *slave)
if (rt715->hw_init)
return 0;
+ regcache_cache_only(rt715->regmap, false);
+ regcache_cache_only(rt715->mbq_regmap, false);
+
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
*/
if (!rt715->first_hw_init) {
- /* 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);
-
rt715->first_hw_init = true;
}
diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c
index 6db87442b783..21f37babd148 100644
--- a/sound/soc/codecs/rt715-sdw.c
+++ b/sound/soc/codecs/rt715-sdw.c
@@ -508,17 +508,12 @@ static int rt715_sdw_probe(struct sdw_slave *slave,
if (IS_ERR(regmap))
return PTR_ERR(regmap);
- rt715_init(&slave->dev, sdw_regmap, regmap, slave);
-
- return 0;
+ return rt715_init(&slave->dev, sdw_regmap, regmap, slave);
}
static int rt715_sdw_remove(struct sdw_slave *slave)
{
- struct rt715_priv *rt715 = dev_get_drvdata(&slave->dev);
-
- if (rt715->first_hw_init)
- pm_runtime_disable(&slave->dev);
+ pm_runtime_disable(&slave->dev);
return 0;
}
diff --git a/sound/soc/codecs/rt715.c b/sound/soc/codecs/rt715.c
index 6c2e165dd621..b59230c8fd32 100644
--- a/sound/soc/codecs/rt715.c
+++ b/sound/soc/codecs/rt715.c
@@ -16,14 +16,11 @@
#include <linux/pm_runtime.h>
#include <linux/pm.h>
#include <linux/soundwire/sdw.h>
-#include <linux/gpio.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/regulator/consumer.h>
-#include <linux/gpio/consumer.h>
#include <linux/of.h>
-#include <linux/of_gpio.h>
#include <linux/of_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
@@ -52,6 +49,60 @@ static int rt715_index_write(struct regmap *regmap, unsigned int reg,
return ret;
}
+static int rt715_index_write_nid(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg;
+
+ ret = regmap_write(regmap, addr, value);
+ if (ret < 0)
+ pr_err("Failed to set private value: %06x <= %04x ret=%d\n",
+ addr, value, ret);
+
+ return ret;
+}
+
+static int rt715_index_read_nid(struct regmap *regmap,
+ unsigned int nid, unsigned int reg, unsigned int *value)
+{
+ int ret;
+ unsigned int addr = ((RT715_PRIV_INDEX_W_H_2 | nid) << 8) | reg;
+
+ *value = 0;
+ ret = regmap_read(regmap, addr, value);
+ if (ret < 0)
+ pr_err("Failed to get private value: %06x => %04x ret=%d\n",
+ addr, *value, ret);
+
+ return ret;
+}
+
+static int rt715_index_update_bits(struct regmap *regmap, unsigned int nid,
+ unsigned int reg, unsigned int mask, unsigned int val)
+{
+ unsigned int tmp, orig;
+ int ret;
+
+ ret = rt715_index_read_nid(regmap, nid, reg, &orig);
+ if (ret < 0)
+ return ret;
+
+ tmp = orig & ~mask;
+ tmp |= val & mask;
+
+ return rt715_index_write_nid(regmap, nid, reg, tmp);
+}
+
+static void rt715_reset(struct regmap *regmap)
+{
+ regmap_write(regmap, RT715_FUNC_RESET, 0);
+ rt715_index_update_bits(regmap, RT715_VENDOR_REGISTERS,
+ RT715_VD_CLEAR_CTRL, RT715_CLEAR_HIDDEN_REG,
+ RT715_CLEAR_HIDDEN_REG);
+}
+
+
static void rt715_get_gain(struct rt715_priv *rt715, unsigned int addr_h,
unsigned int addr_l, unsigned int val_h,
unsigned int *r_val, unsigned int *l_val)
@@ -740,8 +791,12 @@ static int rt715_set_bias_level(struct snd_soc_component *component,
static int rt715_probe(struct snd_soc_component *component)
{
+ struct rt715_priv *rt715 = snd_soc_component_get_drvdata(component);
int ret;
+ if (!rt715->first_hw_init)
+ return 0;
+
ret = pm_runtime_resume(component->dev);
if (ret < 0 && ret != -EACCES)
return ret;
@@ -984,6 +1039,8 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap,
rt715->regmap = regmap;
rt715->sdw_regmap = sdw_regmap;
+ regcache_cache_only(rt715->regmap, true);
+
/*
* Mark hw_init to false
* HW init will be performed when device reports present
@@ -995,8 +1052,25 @@ int rt715_init(struct device *dev, struct regmap *sdw_regmap,
&soc_codec_dev_rt715,
rt715_dai,
ARRAY_SIZE(rt715_dai));
+ if (ret < 0)
+ return ret;
- return ret;
+ /* set autosuspend parameters */
+ pm_runtime_set_autosuspend_delay(dev, 3000);
+ pm_runtime_use_autosuspend(dev);
+
+ /* make sure the device does not suspend immediately */
+ pm_runtime_mark_last_busy(dev);
+
+ pm_runtime_enable(dev);
+
+ /* important note: the device is NOT tagged as 'active' and will remain
+ * 'suspended' until the hardware is enumerated/initialized. This is required
+ * to make sure the ASoC framework use of pm_runtime_get_sync() does not silently
+ * fail with -EACCESS because of race conditions between card creation and enumeration
+ */
+
+ return 0;
}
int rt715_io_init(struct device *dev, struct sdw_slave *slave)
@@ -1006,25 +1080,19 @@ int rt715_io_init(struct device *dev, struct sdw_slave *slave)
if (rt715->hw_init)
return 0;
+ regcache_cache_only(rt715->regmap, false);
+
/*
- * PM runtime is only enabled when a Slave reports as Attached
+ * PM runtime status is marked as 'active' only when a Slave reports as Attached
*/
- if (!rt715->first_hw_init) {
- /* set autosuspend parameters */
- pm_runtime_set_autosuspend_delay(&slave->dev, 3000);
- pm_runtime_use_autosuspend(&slave->dev);
-
+ if (!rt715->first_hw_init)
/* 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);
+ rt715_reset(rt715->regmap);
+
/* Mute nid=08h/09h */
regmap_write(rt715->regmap, RT715_SET_GAIN_LINE_ADC_H, 0xb080);
regmap_write(rt715->regmap, RT715_SET_GAIN_MIX_ADC_H, 0xb080);
diff --git a/sound/soc/codecs/rt715.h b/sound/soc/codecs/rt715.h
index 12a0ae656d09..6e37bf64e12f 100644
--- a/sound/soc/codecs/rt715.h
+++ b/sound/soc/codecs/rt715.h
@@ -48,6 +48,7 @@ struct rt715_priv {
#define RT715_INLINE_CMD 0x55
/* Index (NID:20h) */
+#define RT715_VD_CLEAR_CTRL 0x01
#define RT715_SDW_INPUT_SEL 0x39
#define RT715_EXT_DMIC_CLK_CTRL2 0x54
@@ -71,6 +72,8 @@ struct rt715_priv {
#define RT715_READ_HDA_0 0x2015
#define RT715_PRIV_INDEX_W_H 0x7520
#define RT715_PRIV_INDEX_W_L 0x85a0
+#define RT715_PRIV_INDEX_W_H_2 0x7500
+#define RT715_PRIV_INDEX_W_L_2 0x8580
#define RT715_PRIV_DATA_W_H 0x7420
#define RT715_PRIV_DATA_W_L 0x84a0
#define RT715_PRIV_INDEX_R_H 0x9d20
@@ -198,6 +201,10 @@ struct rt715_priv {
#define RT715_SET_DMIC4_CONFIG_DEFAULT4\
(RT715_VERB_SET_CONFIG_DEFAULT4 | RT715_DMIC4)
+/* vendor register clear ctrl-1 (0x01)(NID:20h) */
+#define RT715_CLEAR_HIDDEN_REG (0x1 << 15)
+
+
#define RT715_MUTE_SFT 7
#define RT715_DIR_IN_SFT 6
#define RT715_DIR_OUT_SFT 7
diff --git a/sound/soc/codecs/ssm3515.c b/sound/soc/codecs/ssm3515.c
index 008cb3eb5758..8c6665677a17 100644
--- a/sound/soc/codecs/ssm3515.c
+++ b/sound/soc/codecs/ssm3515.c
@@ -437,7 +437,7 @@ MODULE_DEVICE_TABLE(of, ssm3515_of_match);
static struct i2c_driver ssm3515_i2c_driver = {
.driver = {
.name = "ssm3515",
- .of_match_table = of_match_ptr(ssm3515_of_match),
+ .of_match_table = ssm3515_of_match,
},
.probe = ssm3515_i2c_probe,
};
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index 4a694d0bfd68..34ffd32ab9dc 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -1022,7 +1022,7 @@ static const struct regmap_config sta32x_regmap = {
.max_register = STA32X_FDRC2,
.reg_defaults = sta32x_regs,
.num_reg_defaults = ARRAY_SIZE(sta32x_regs),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.wr_table = &sta32x_write_regs,
.rd_table = &sta32x_read_regs,
.volatile_table = &sta32x_volatile_regs,
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index d05f3fd57661..e4a9e9241c60 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -1065,7 +1065,7 @@ static const struct regmap_config sta350_regmap = {
.max_register = STA350_MISC2,
.reg_defaults = sta350_regs,
.num_reg_defaults = ARRAY_SIZE(sta350_regs),
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.wr_table = &sta350_write_regs,
.rd_table = &sta350_read_regs,
.volatile_table = &sta350_volatile_regs,
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 0ac08478ddac..eedafef775e5 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -331,7 +331,7 @@ static const struct regmap_config sta529_regmap = {
.max_register = STA529_MAX_REGISTER,
.readable_reg = sta529_readable,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_defaults = sta529_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(sta529_reg_defaults),
};
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 1824a71fe053..2f9f10a4dfed 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -67,7 +67,7 @@ static const struct regmap_config stac9766_regmap_config = {
.reg_stride = 2,
.val_bits = 16,
.max_register = 0x78,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.volatile_reg = regmap_ac97_default_volatile,
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
index 99545bcb2ba9..c421906a0694 100644
--- a/sound/soc/codecs/sti-sas.c
+++ b/sound/soc/codecs/sti-sas.c
@@ -316,7 +316,7 @@ static const struct regmap_config stih407_sas_regmap = {
.reg_defaults = stih407_sas_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults),
.volatile_reg = sti_sas_volatile_register,
- .cache_type = REGCACHE_RBTREE,
+ .cache_type = REGCACHE_MAPLE,
.reg_read = sti_sas_read_reg,
.reg_write = sti_sas_write_reg,
};
diff --git a/sound/soc/codecs/tas5805m.c b/sound/soc/codecs/tas5805m.c
index aca3756ffab6..3b53eba38a0b 100644
--- a/sound/soc/codecs/tas5805m.c
+++ b/sound/soc/codecs/tas5805m.c
@@ -520,12 +520,11 @@ static int tas5805m_i2c_probe(struct i2c_client *i2c)
}
tas5805m->dsp_cfg_len = fw->size;
- tas5805m->dsp_cfg_data = devm_kmalloc(dev, fw->size, GFP_KERNEL);
+ tas5805m->dsp_cfg_data = devm_kmemdup(dev, fw->data, fw->size, GFP_KERNEL);
if (!tas5805m->dsp_cfg_data) {
release_firmware(fw);
return -ENOMEM;
}
- memcpy(tas5805m->dsp_cfg_data, fw->data, fw->size);
release_firmware(fw);
diff --git a/sound/soc/codecs/tlv320aic32x4-clk.c b/sound/soc/codecs/tlv320aic32x4-clk.c
index c116e82f712d..5c0a76a4a106 100644
--- a/sound/soc/codecs/tlv320aic32x4-clk.c
+++ b/sound/soc/codecs/tlv320aic32x4-clk.c
@@ -321,7 +321,7 @@ static int clk_aic32x4_div_set_rate(struct clk_hw *hw, unsigned long rate,
u8 divisor;
divisor = DIV_ROUND_UP(parent_rate, rate);
- if (divisor > 128)
+ if (divisor > AIC32X4_DIV_MAX)
return -EINVAL;
return regmap_update_bits(div->regmap, div->reg,
@@ -334,7 +334,7 @@ static int clk_aic32x4_div_determine_rate(struct clk_hw *hw,
unsigned long divisor;
divisor = DIV_ROUND_UP(req->best_parent_rate, req->rate);
- if (divisor > 128)
+ if (divisor > AIC32X4_DIV_MAX)
return -EINVAL;
req->rate = DIV_ROUND_UP(req->best_parent_rate, divisor);
@@ -345,12 +345,18 @@ static unsigned long clk_aic32x4_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_aic32x4 *div = to_clk_aic32x4(hw);
-
unsigned int val;
+ int err;
+
+ err = regmap_read(div->regmap, div->reg, &val);
+ if (err)
+ return 0;
- regmap_read(div->regmap, div->reg, &val);
+ val &= AIC32X4_DIV_MASK;
+ if (!val)
+ val = AIC32X4_DIV_MAX;
- return DIV_ROUND_UP(parent_rate, val & AIC32X4_DIV_MASK);
+ return DIV_ROUND_UP(parent_rate, val);
}
static const struct clk_ops aic32x4_div_ops = {
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index ffe1828a4b7e..6829834a412f 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -1349,7 +1349,7 @@ int aic32x4_probe(struct device *dev, struct regmap *regmap)
return -ENOMEM;
aic32x4->dev = dev;
- aic32x4->type = (enum aic32x4_type)dev_get_drvdata(dev);
+ aic32x4->type = (uintptr_t)dev_get_drvdata(dev);
dev_set_drvdata(dev, aic32x4);
diff --git a/sound/soc/codecs/tlv320aic32x4.h b/sound/soc/codecs/tlv320aic32x4.h
index 4de5bd9e8cc5..d6101ce73f80 100644
--- a/sound/soc/codecs/tlv320aic32x4.h
+++ b/sound/soc/codecs/tlv320aic32x4.h
@@ -223,8 +223,9 @@ int aic32x4_register_clocks(struct device *dev, const char *mclk_name);
#define AIC32X4_REFPOWERUP_120MS 0x07
/* Common mask and enable for all of the dividers */
-#define AIC32X4_DIVEN BIT(7)
-#define AIC32X4_DIV_MASK GENMASK(6, 0)
+#define AIC32X4_DIVEN BIT(7)
+#define AIC32X4_DIV_MASK GENMASK(6, 0)
+#define AIC32X4_DIV_MAX 128
/* Clock Limits */
#define AIC32X4_MAX_DOSR_FREQ 6200000
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
deleted file mode 100644
index 1a62bec94005..000000000000
--- a/sound/soc/codecs/uda134x.c
+++ /dev/null
@@ -1,587 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * uda134x.c -- UDA134X ALSA SoC Codec driver
- *
- * Modifications by Christian Pellegrin <chripell@evolware.org>
- *
- * Copyright 2007 Dension Audio Systems Ltd.
- * Author: Zoltan Devai
- *
- * Based on the WM87xx drivers by Liam Girdwood and Richard Purdie
- */
-
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/slab.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/initval.h>
-
-#include <sound/uda134x.h>
-#include <sound/l3.h>
-
-#include "uda134x.h"
-
-
-#define UDA134X_RATES SNDRV_PCM_RATE_8000_48000
-#define UDA134X_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE | \
- SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S20_3LE)
-
-struct uda134x_priv {
- int sysclk;
- int dai_fmt;
-
- struct snd_pcm_substream *master_substream;
- struct snd_pcm_substream *slave_substream;
-
- struct regmap *regmap;
- struct uda134x_platform_data *pd;
-};
-
-static const struct reg_default uda134x_reg_defaults[] = {
- { UDA134X_EA000, 0x04 },
- { UDA134X_EA001, 0x04 },
- { UDA134X_EA010, 0x04 },
- { UDA134X_EA011, 0x00 },
- { UDA134X_EA100, 0x00 },
- { UDA134X_EA101, 0x00 },
- { UDA134X_EA110, 0x00 },
- { UDA134X_EA111, 0x00 },
- { UDA134X_STATUS0, 0x00 },
- { UDA134X_STATUS1, 0x03 },
- { UDA134X_DATA000, 0x00 },
- { UDA134X_DATA001, 0x00 },
- { UDA134X_DATA010, 0x00 },
- { UDA134X_DATA011, 0x00 },
- { UDA134X_DATA1, 0x00 },
-};
-
-/*
- * Write to the uda134x registers
- *
- */
-static int uda134x_regmap_write(void *context, unsigned int reg,
- unsigned int value)
-{
- struct uda134x_platform_data *pd = context;
- int ret;
- u8 addr;
- u8 data = value;
-
- switch (reg) {
- case UDA134X_STATUS0:
- case UDA134X_STATUS1:
- addr = UDA134X_STATUS_ADDR;
- data |= (reg - UDA134X_STATUS0) << 7;
- break;
- case UDA134X_DATA000:
- case UDA134X_DATA001:
- case UDA134X_DATA010:
- case UDA134X_DATA011:
- addr = UDA134X_DATA0_ADDR;
- data |= (reg - UDA134X_DATA000) << 6;
- break;
- case UDA134X_DATA1:
- addr = UDA134X_DATA1_ADDR;
- break;
- default:
- /* It's an extended address register */
- addr = (reg | UDA134X_EXTADDR_PREFIX);
-
- ret = l3_write(&pd->l3,
- UDA134X_DATA0_ADDR, &addr, 1);
- if (ret != 1)
- return -EIO;
-
- addr = UDA134X_DATA0_ADDR;
- data = (value | UDA134X_EXTDATA_PREFIX);
- break;
- }
-
- ret = l3_write(&pd->l3,
- addr, &data, 1);
- if (ret != 1)
- return -EIO;
-
- return 0;
-}
-
-static inline void uda134x_reset(struct snd_soc_component *component)
-{
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
- unsigned int mask = 1<<6;
-
- regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, mask);
- msleep(1);
- regmap_update_bits(uda134x->regmap, UDA134X_STATUS0, mask, 0);
-}
-
-static int uda134x_mute(struct snd_soc_dai *dai, int mute, int direction)
-{
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(dai->component);
- unsigned int mask = 1<<2;
- unsigned int val;
-
- pr_debug("%s mute: %d\n", __func__, mute);
-
- if (mute)
- val = mask;
- else
- val = 0;
-
- return regmap_update_bits(uda134x->regmap, UDA134X_DATA010, mask, val);
-}
-
-static int uda134x_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
- struct snd_pcm_runtime *master_runtime;
-
- if (uda134x->master_substream) {
- master_runtime = uda134x->master_substream->runtime;
-
- pr_debug("%s constraining to %d bits at %d\n", __func__,
- master_runtime->sample_bits,
- master_runtime->rate);
-
- snd_pcm_hw_constraint_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_RATE,
- master_runtime->rate);
-
- snd_pcm_hw_constraint_single(substream->runtime,
- SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
- master_runtime->sample_bits);
-
- uda134x->slave_substream = substream;
- } else
- uda134x->master_substream = substream;
-
- return 0;
-}
-
-static void uda134x_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct snd_soc_component *component = dai->component;
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
-
- if (uda134x->master_substream == substream)
- uda134x->master_substream = uda134x->slave_substream;
-
- uda134x->slave_substream = NULL;
-}
-
-static int uda134x_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 uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
- unsigned int hw_params = 0;
-
- if (substream == uda134x->slave_substream) {
- pr_debug("%s ignoring hw_params for slave substream\n",
- __func__);
- return 0;
- }
-
- pr_debug("%s sysclk: %d, rate:%d\n", __func__,
- uda134x->sysclk, params_rate(params));
-
- /* set SYSCLK / fs ratio */
- switch (uda134x->sysclk / params_rate(params)) {
- case 512:
- break;
- case 384:
- hw_params |= (1<<4);
- break;
- case 256:
- hw_params |= (1<<5);
- break;
- default:
- printk(KERN_ERR "%s unsupported fs\n", __func__);
- return -EINVAL;
- }
-
- pr_debug("%s dai_fmt: %d, params_format:%d\n", __func__,
- uda134x->dai_fmt, params_format(params));
-
- /* set DAI format and word length */
- switch (uda134x->dai_fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- switch (params_width(params)) {
- case 16:
- hw_params |= (1<<1);
- break;
- case 18:
- hw_params |= (1<<2);
- break;
- case 20:
- hw_params |= ((1<<2) | (1<<1));
- break;
- default:
- printk(KERN_ERR "%s unsupported format (right)\n",
- __func__);
- return -EINVAL;
- }
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- hw_params |= (1<<3);
- break;
- default:
- printk(KERN_ERR "%s unsupported format\n", __func__);
- return -EINVAL;
- }
-
- return regmap_update_bits(uda134x->regmap, UDA134X_STATUS0,
- STATUS0_SYSCLK_MASK | STATUS0_DAIFMT_MASK, hw_params);
-}
-
-static int uda134x_set_dai_sysclk(struct snd_soc_dai *codec_dai,
- int clk_id, unsigned int freq, int dir)
-{
- struct snd_soc_component *component = codec_dai->component;
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
-
- pr_debug("%s clk_id: %d, freq: %u, dir: %d\n", __func__,
- clk_id, freq, dir);
-
- /* Anything between 256fs*8Khz and 512fs*48Khz should be acceptable
- because the codec is slave. Of course limitations of the clock
- master (the IIS controller) apply.
- We'll error out on set_hw_params if it's not OK */
- if ((freq >= (256 * 8000)) && (freq <= (512 * 48000))) {
- uda134x->sysclk = freq;
- return 0;
- }
-
- printk(KERN_ERR "%s unsupported sysclk\n", __func__);
- return -EINVAL;
-}
-
-static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
- unsigned int fmt)
-{
- struct snd_soc_component *component = codec_dai->component;
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
-
- pr_debug("%s fmt: %08X\n", __func__, fmt);
-
- /* codec supports only full consumer mode */
- if ((fmt & SND_SOC_DAIFMT_CLOCK_PROVIDER_MASK) != SND_SOC_DAIFMT_CBC_CFC) {
- printk(KERN_ERR "%s unsupported clocking mode\n", __func__);
- return -EINVAL;
- }
-
- /* no support for clock inversion */
- if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) {
- printk(KERN_ERR "%s unsupported clock inversion\n", __func__);
- return -EINVAL;
- }
-
- /* We can't setup DAI format here as it depends on the word bit num */
- /* so let's just store the value for later */
- uda134x->dai_fmt = fmt;
-
- return 0;
-}
-
-static int uda134x_set_bias_level(struct snd_soc_component *component,
- enum snd_soc_bias_level level)
-{
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
- struct uda134x_platform_data *pd = uda134x->pd;
- pr_debug("%s bias level %d\n", __func__, level);
-
- switch (level) {
- case SND_SOC_BIAS_ON:
- break;
- case SND_SOC_BIAS_PREPARE:
- /* power on */
- if (pd->power) {
- pd->power(1);
- regcache_sync(uda134x->regmap);
- }
- break;
- case SND_SOC_BIAS_STANDBY:
- break;
- case SND_SOC_BIAS_OFF:
- /* power off */
- if (pd->power) {
- pd->power(0);
- regcache_mark_dirty(uda134x->regmap);
- }
- break;
- }
- return 0;
-}
-
-static const char *uda134x_dsp_setting[] = {"Flat", "Minimum1",
- "Minimum2", "Maximum"};
-static const char *uda134x_deemph[] = {"None", "32Khz", "44.1Khz", "48Khz"};
-static const char *uda134x_mixmode[] = {"Differential", "Analog1",
- "Analog2", "Both"};
-
-static const struct soc_enum uda134x_mixer_enum[] = {
-SOC_ENUM_SINGLE(UDA134X_DATA010, 0, 0x04, uda134x_dsp_setting),
-SOC_ENUM_SINGLE(UDA134X_DATA010, 3, 0x04, uda134x_deemph),
-SOC_ENUM_SINGLE(UDA134X_EA010, 0, 0x04, uda134x_mixmode),
-};
-
-static const struct snd_kcontrol_new uda1341_snd_controls[] = {
-SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
-SOC_SINGLE("Capture Volume", UDA134X_EA010, 2, 0x07, 0),
-SOC_SINGLE("Analog1 Volume", UDA134X_EA000, 0, 0x1F, 1),
-SOC_SINGLE("Analog2 Volume", UDA134X_EA001, 0, 0x1F, 1),
-
-SOC_SINGLE("Mic Sensitivity", UDA134X_EA010, 2, 7, 0),
-SOC_SINGLE("Mic Volume", UDA134X_EA101, 0, 0x1F, 0),
-
-SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
-SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
-
-SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
-SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
-SOC_ENUM("Input Mux", uda134x_mixer_enum[2]),
-
-SOC_SINGLE("AGC Switch", UDA134X_EA100, 4, 1, 0),
-SOC_SINGLE("AGC Target Volume", UDA134X_EA110, 0, 0x03, 1),
-SOC_SINGLE("AGC Timing", UDA134X_EA110, 2, 0x07, 0),
-
-SOC_SINGLE("DAC +6dB Switch", UDA134X_STATUS1, 6, 1, 0),
-SOC_SINGLE("ADC +6dB Switch", UDA134X_STATUS1, 5, 1, 0),
-SOC_SINGLE("ADC Polarity Switch", UDA134X_STATUS1, 4, 1, 0),
-SOC_SINGLE("DAC Polarity Switch", UDA134X_STATUS1, 3, 1, 0),
-SOC_SINGLE("Double Speed Playback Switch", UDA134X_STATUS1, 2, 1, 0),
-SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new uda1340_snd_controls[] = {
-SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
-
-SOC_SINGLE("Tone Control - Bass", UDA134X_DATA001, 2, 0xF, 0),
-SOC_SINGLE("Tone Control - Treble", UDA134X_DATA001, 0, 3, 0),
-
-SOC_ENUM("Sound Processing Filter", uda134x_mixer_enum[0]),
-SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
-
-SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
-};
-
-static const struct snd_kcontrol_new uda1345_snd_controls[] = {
-SOC_SINGLE("Master Playback Volume", UDA134X_DATA000, 0, 0x3F, 1),
-
-SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
-
-SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
-};
-
-/* UDA1341 has the DAC/ADC power down in STATUS1 */
-static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0),
-};
-
-/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */
-static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = {
- SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0),
- SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0),
-};
-
-/* Common DAPM widgets */
-static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = {
- SND_SOC_DAPM_INPUT("VINL1"),
- SND_SOC_DAPM_INPUT("VINR1"),
- SND_SOC_DAPM_INPUT("VINL2"),
- SND_SOC_DAPM_INPUT("VINR2"),
- SND_SOC_DAPM_OUTPUT("VOUTL"),
- SND_SOC_DAPM_OUTPUT("VOUTR"),
-};
-
-static const struct snd_soc_dapm_route uda134x_dapm_routes[] = {
- { "ADC", NULL, "VINL1" },
- { "ADC", NULL, "VINR1" },
- { "ADC", NULL, "VINL2" },
- { "ADC", NULL, "VINR2" },
- { "VOUTL", NULL, "DAC" },
- { "VOUTR", NULL, "DAC" },
-};
-
-static const struct snd_soc_dai_ops uda134x_dai_ops = {
- .startup = uda134x_startup,
- .shutdown = uda134x_shutdown,
- .hw_params = uda134x_hw_params,
- .mute_stream = uda134x_mute,
- .set_sysclk = uda134x_set_dai_sysclk,
- .set_fmt = uda134x_set_dai_fmt,
- .no_capture_mute = 1,
-};
-
-static struct snd_soc_dai_driver uda134x_dai = {
- .name = "uda134x-hifi",
- /* playback capabilities */
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 2,
- .rates = UDA134X_RATES,
- .formats = UDA134X_FORMATS,
- },
- /* capture capabilities */
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 2,
- .rates = UDA134X_RATES,
- .formats = UDA134X_FORMATS,
- },
- /* pcm operations */
- .ops = &uda134x_dai_ops,
-};
-
-static int uda134x_soc_probe(struct snd_soc_component *component)
-{
- struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component);
- struct uda134x_priv *uda134x = snd_soc_component_get_drvdata(component);
- struct uda134x_platform_data *pd = uda134x->pd;
- const struct snd_soc_dapm_widget *widgets;
- unsigned int num_widgets;
- int ret;
-
- printk(KERN_INFO "UDA134X SoC Audio Codec\n");
-
- switch (pd->model) {
- case UDA134X_UDA1340:
- case UDA134X_UDA1341:
- case UDA134X_UDA1344:
- case UDA134X_UDA1345:
- break;
- default:
- printk(KERN_ERR "UDA134X SoC codec: "
- "unsupported model %d\n",
- pd->model);
- return -EINVAL;
- }
-
- if (pd->power)
- pd->power(1);
-
- uda134x_reset(component);
-
- if (pd->model == UDA134X_UDA1341) {
- widgets = uda1341_dapm_widgets;
- num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
- } else {
- widgets = uda1340_dapm_widgets;
- num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
- }
-
- ret = snd_soc_dapm_new_controls(dapm, widgets, num_widgets);
- if (ret) {
- printk(KERN_ERR "%s failed to register dapm controls: %d",
- __func__, ret);
- return ret;
- }
-
- switch (pd->model) {
- case UDA134X_UDA1340:
- case UDA134X_UDA1344:
- ret = snd_soc_add_component_controls(component, uda1340_snd_controls,
- ARRAY_SIZE(uda1340_snd_controls));
- break;
- case UDA134X_UDA1341:
- ret = snd_soc_add_component_controls(component, uda1341_snd_controls,
- ARRAY_SIZE(uda1341_snd_controls));
- break;
- case UDA134X_UDA1345:
- ret = snd_soc_add_component_controls(component, uda1345_snd_controls,
- ARRAY_SIZE(uda1345_snd_controls));
- break;
- default:
- printk(KERN_ERR "%s unknown codec type: %d",
- __func__, pd->model);
- return -EINVAL;
- }
-
- if (ret < 0) {
- printk(KERN_ERR "UDA134X: failed to register controls\n");
- return ret;
- }
-
- return 0;
-}
-
-static const struct snd_soc_component_driver soc_component_dev_uda134x = {
- .probe = uda134x_soc_probe,
- .set_bias_level = uda134x_set_bias_level,
- .dapm_widgets = uda134x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
- .dapm_routes = uda134x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
- .suspend_bias_off = 1,
- .idle_bias_on = 1,
- .use_pmdown_time = 1,
- .endianness = 1,
-};
-
-static const struct regmap_config uda134x_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .max_register = UDA134X_DATA1,
- .reg_defaults = uda134x_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(uda134x_reg_defaults),
- .cache_type = REGCACHE_RBTREE,
-
- .reg_write = uda134x_regmap_write,
-};
-
-static int uda134x_codec_probe(struct platform_device *pdev)
-{
- struct uda134x_platform_data *pd = pdev->dev.platform_data;
- struct uda134x_priv *uda134x;
- int ret;
-
- if (!pd) {
- dev_err(&pdev->dev, "Missing L3 bitbang function\n");
- return -ENODEV;
- }
-
- uda134x = devm_kzalloc(&pdev->dev, sizeof(*uda134x), GFP_KERNEL);
- if (!uda134x)
- return -ENOMEM;
-
- uda134x->pd = pd;
- platform_set_drvdata(pdev, uda134x);
-
- if (pd->l3.use_gpios) {
- ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3);
- if (ret < 0)
- return ret;
- }
-
- uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd,
- &uda134x_regmap_config);
- if (IS_ERR(uda134x->regmap))
- return PTR_ERR(uda134x->regmap);
-
- return devm_snd_soc_register_component(&pdev->dev,
- &soc_component_dev_uda134x, &uda134x_dai, 1);
-}
-
-static struct platform_driver uda134x_codec_driver = {
- .driver = {
- .name = "uda134x-codec",
- },
- .probe = uda134x_codec_probe,
-};
-
-module_platform_driver(uda134x_codec_driver);
-
-MODULE_DESCRIPTION("UDA134X ALSA soc codec driver");
-MODULE_AUTHOR("Zoltan Devai, Christian Pellegrin <chripell@evolware.org>");
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/uda134x.h b/sound/soc/codecs/uda134x.h
deleted file mode 100644
index 664618c2571c..000000000000
--- a/sound/soc/codecs/uda134x.h
+++ /dev/null
@@ -1,33 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-#ifndef _UDA134X_CODEC_H
-#define _UDA134X_CODEC_H
-
-#define UDA134X_L3ADDR 5
-#define UDA134X_DATA0_ADDR ((UDA134X_L3ADDR << 2) | 0)
-#define UDA134X_DATA1_ADDR ((UDA134X_L3ADDR << 2) | 1)
-#define UDA134X_STATUS_ADDR ((UDA134X_L3ADDR << 2) | 2)
-
-#define UDA134X_EXTADDR_PREFIX 0xC0
-#define UDA134X_EXTDATA_PREFIX 0xE0
-
-/* UDA134X registers */
-#define UDA134X_EA000 0
-#define UDA134X_EA001 1
-#define UDA134X_EA010 2
-#define UDA134X_EA011 3
-#define UDA134X_EA100 4
-#define UDA134X_EA101 5
-#define UDA134X_EA110 6
-#define UDA134X_EA111 7
-#define UDA134X_STATUS0 8
-#define UDA134X_STATUS1 9
-#define UDA134X_DATA000 10
-#define UDA134X_DATA001 11
-#define UDA134X_DATA010 12
-#define UDA134X_DATA011 13
-#define UDA134X_DATA1 14
-
-#define STATUS0_DAIFMT_MASK (~(7<<1))
-#define STATUS0_SYSCLK_MASK (~(3<<4))
-
-#endif
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index 36cdf97993a5..9679906c6bd5 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -1770,11 +1770,6 @@ static int wm2200_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static const struct snd_soc_dai_ops wm2200_dai_ops = {
- .set_fmt = wm2200_set_fmt,
- .hw_params = wm2200_hw_params,
-};
-
static int wm2200_set_sysclk(struct snd_soc_component *component, int clk_id,
int source, unsigned int freq, int dir)
{
@@ -2068,6 +2063,12 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai)
return 0;
}
+static const struct snd_soc_dai_ops wm2200_dai_ops = {
+ .probe = wm2200_dai_probe,
+ .set_fmt = wm2200_set_fmt,
+ .hw_params = wm2200_hw_params,
+};
+
#define WM2200_RATES SNDRV_PCM_RATE_8000_48000
#define WM2200_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
@@ -2075,7 +2076,6 @@ static int wm2200_dai_probe(struct snd_soc_dai *dai)
static struct snd_soc_dai_driver wm2200_dai = {
.name = "wm2200",
- .probe = wm2200_dai_probe,
.playback = {
.stream_name = "Playback",
.channels_min = 2,
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 3bdbdf3770b5..4ecf07c7448c 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1773,6 +1773,10 @@ static int wm5102_set_fll(struct snd_soc_component *component, int fll_id,
#define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const struct snd_soc_dai_ops wm5102_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver wm5102_dai[] = {
{
.name = "wm5102-aif1",
@@ -1906,7 +1910,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &wm5102_dai_ops,
},
{
.name = "wm5102-dsp-trace",
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index ad670300de8d..ac1f2c850346 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -2073,6 +2073,10 @@ static int wm5110_set_fll(struct snd_soc_component *component, int fll_id,
#define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+static const struct snd_soc_dai_ops wm5110_dai_ops = {
+ .compress_new = snd_soc_new_compress,
+};
+
static struct snd_soc_dai_driver wm5110_dai[] = {
{
.name = "wm5110-aif1",
@@ -2206,7 +2210,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &wm5110_dai_ops,
},
{
.name = "wm5110-dsp-voicectrl",
@@ -2227,7 +2231,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
.rates = WM5110_RATES,
.formats = WM5110_FORMATS,
},
- .compress_new = snd_soc_new_compress,
+ .ops = &wm5110_dai_ops,
},
{
.name = "wm5110-dsp-trace",
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 60319b468fb2..829bf055622a 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2202,7 +2202,7 @@ static int wm8904_i2c_probe(struct i2c_client *i2c)
match = of_match_node(wm8904_of_match, i2c->dev.of_node);
if (match == NULL)
return -EINVAL;
- wm8904->devtype = (enum wm8904_type)match->data;
+ wm8904->devtype = (uintptr_t)match->data;
} else {
const struct i2c_device_id *id =
i2c_match_id(wm8904_i2c_id, i2c);
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index c2bd9ef41ebb..0a50180750e8 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -120,6 +120,15 @@ static bool wm8960_volatile(struct device *dev, unsigned int reg)
}
}
+#define WM8960_NUM_SUPPLIES 5
+static const char *wm8960_supply_names[WM8960_NUM_SUPPLIES] = {
+ "DCVDD",
+ "DBVDD",
+ "AVDD",
+ "SPKVDD1",
+ "SPKVDD2",
+};
+
struct wm8960_priv {
struct clk *mclk;
struct regmap *regmap;
@@ -137,6 +146,7 @@ struct wm8960_priv {
bool is_stream_in_use[2];
struct wm8960_data pdata;
ktime_t dsch_start;
+ struct regulator_bulk_data supplies[WM8960_NUM_SUPPLIES];
};
#define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0)
@@ -1417,6 +1427,7 @@ static int wm8960_i2c_probe(struct i2c_client *i2c)
{
struct wm8960_data *pdata = dev_get_platdata(&i2c->dev);
struct wm8960_priv *wm8960;
+ unsigned int i;
int ret;
u8 val;
@@ -1429,6 +1440,31 @@ static int wm8960_i2c_probe(struct i2c_client *i2c)
if (IS_ERR(wm8960->mclk)) {
if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER)
return -EPROBE_DEFER;
+ } else {
+ ret = clk_get_rate(wm8960->mclk);
+ if (ret >= 0) {
+ wm8960->freq_in = ret;
+ } else {
+ dev_err(&i2c->dev, "Failed to read MCLK rate: %d\n",
+ ret);
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(wm8960->supplies); i++)
+ wm8960->supplies[i].supply = wm8960_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8960->supplies),
+ wm8960->supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8960->supplies),
+ wm8960->supplies);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
}
wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap);
@@ -1497,7 +1533,11 @@ static int wm8960_i2c_probe(struct i2c_client *i2c)
}
static void wm8960_i2c_remove(struct i2c_client *client)
-{}
+{
+ struct wm8960_priv *wm8960 = i2c_get_clientdata(client);
+
+ regulator_bulk_disable(ARRAY_SIZE(wm8960->supplies), wm8960->supplies);
+}
static const struct i2c_device_id wm8960_i2c_id[] = {
{ "wm8960", 0 },
diff --git a/sound/soc/codecs/wm8960.h b/sound/soc/codecs/wm8960.h
index 63ba6c03c488..e8ff33b188e9 100644
--- a/sound/soc/codecs/wm8960.h
+++ b/sound/soc/codecs/wm8960.h
@@ -77,9 +77,9 @@
#define WM8960_SYSCLK_DIV_1 (0 << 1)
#define WM8960_SYSCLK_DIV_2 (2 << 1)
-#define WM8960_SYSCLK_MCLK (0 << 0)
+#define WM8960_SYSCLK_AUTO (0 << 0)
#define WM8960_SYSCLK_PLL (1 << 0)
-#define WM8960_SYSCLK_AUTO (2 << 0)
+#define WM8960_SYSCLK_MCLK (2 << 0)
#define WM8960_DAC_DIV_1 (0 << 3)
#define WM8960_DAC_DIV_1_5 (1 << 3)
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index bca3ebe0dac4..a48e904a9740 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -3215,6 +3215,7 @@ static const struct snd_soc_dai_ops wm8994_aif1_dai_ops = {
};
static const struct snd_soc_dai_ops wm8994_aif2_dai_ops = {
+ .probe = wm8994_aif2_probe,
.set_sysclk = wm8994_set_dai_sysclk,
.set_fmt = wm8994_set_dai_fmt,
.hw_params = wm8994_hw_params,
@@ -3269,7 +3270,6 @@ static struct snd_soc_dai_driver wm8994_dai[] = {
.formats = WM8994_FORMATS,
.sig_bits = 24,
},
- .probe = wm8994_aif2_probe,
.ops = &wm8994_aif2_dai_ops,
},
{
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 5a89abfe8784..6fc34f41b175 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -998,7 +998,7 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
-int wm_adsp_power_up(struct wm_adsp *dsp)
+int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware)
{
int ret = 0;
char *wmfw_filename = NULL;
@@ -1006,11 +1006,13 @@ int wm_adsp_power_up(struct wm_adsp *dsp)
char *coeff_filename = NULL;
const struct firmware *coeff_firmware = NULL;
- ret = wm_adsp_request_firmware_files(dsp,
- &wmfw_firmware, &wmfw_filename,
- &coeff_firmware, &coeff_filename);
- if (ret)
- return ret;
+ if (load_firmware) {
+ ret = wm_adsp_request_firmware_files(dsp,
+ &wmfw_firmware, &wmfw_filename,
+ &coeff_firmware, &coeff_filename);
+ if (ret)
+ return ret;
+ }
ret = cs_dsp_power_up(&dsp->cs_dsp,
wmfw_firmware, wmfw_filename,
@@ -1025,13 +1027,19 @@ int wm_adsp_power_up(struct wm_adsp *dsp)
}
EXPORT_SYMBOL_GPL(wm_adsp_power_up);
+void wm_adsp_power_down(struct wm_adsp *dsp)
+{
+ cs_dsp_power_down(&dsp->cs_dsp);
+}
+EXPORT_SYMBOL_GPL(wm_adsp_power_down);
+
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);
+ wm_adsp_power_up(dsp, true);
}
int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
@@ -1046,7 +1054,7 @@ int wm_adsp_early_event(struct snd_soc_dapm_widget *w,
queue_work(system_unbound_wq, &dsp->boot_work);
break;
case SND_SOC_DAPM_PRE_PMD:
- cs_dsp_power_down(&dsp->cs_dsp);
+ wm_adsp_power_down(dsp);
break;
default:
break;
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 769904d34a87..067d807a7ca8 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -91,7 +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);
+int wm_adsp_power_up(struct wm_adsp *dsp, bool load_firmware);
+void wm_adsp_power_down(struct wm_adsp *dsp);
irqreturn_t wm_adsp2_bus_error(int irq, void *data);
irqreturn_t wm_halo_bus_error(int irq, void *data);