summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig10
-rw-r--r--sound/soc/codecs/Makefile4
-rw-r--r--sound/soc/codecs/ab8500-codec.c2
-rw-r--r--sound/soc/codecs/adau17x1.c2
-rw-r--r--sound/soc/codecs/arizona.c153
-rw-r--r--sound/soc/codecs/arizona.h109
-rw-r--r--sound/soc/codecs/cs35l34.c1251
-rw-r--r--sound/soc/codecs/cs35l34.h269
-rw-r--r--sound/soc/codecs/cs4270.c8
-rw-r--r--sound/soc/codecs/cs42l42.c1986
-rw-r--r--sound/soc/codecs/cs42l42.h776
-rw-r--r--sound/soc/codecs/cs42l73.c4
-rw-r--r--sound/soc/codecs/cs42xx8.c10
-rw-r--r--sound/soc/codecs/cs47l24.c55
-rw-r--r--sound/soc/codecs/da7219-aad.c18
-rw-r--r--sound/soc/codecs/da7219.c142
-rw-r--r--sound/soc/codecs/da7219.h5
-rw-r--r--sound/soc/codecs/hdmi-codec.c7
-rw-r--r--sound/soc/codecs/rt298.c5
-rw-r--r--sound/soc/codecs/rt5663.c4
-rw-r--r--sound/soc/codecs/sti-sas.c2
-rw-r--r--sound/soc/codecs/tas571x.c37
-rw-r--r--sound/soc/codecs/tlv320aic3x.c2
-rw-r--r--sound/soc/codecs/wm5102.c59
-rw-r--r--sound/soc/codecs/wm5110.c61
-rw-r--r--sound/soc/codecs/wm8997.c39
-rw-r--r--sound/soc/codecs/wm8998.c38
-rw-r--r--sound/soc/codecs/wm9712.c2
-rw-r--r--sound/soc/codecs/wm9713.c2
-rw-r--r--sound/soc/codecs/wm_adsp.c354
-rw-r--r--sound/soc/codecs/wm_adsp.h27
-rw-r--r--sound/soc/codecs/wmfw.h4
32 files changed, 5110 insertions, 337 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index c67667bb970f..2aa709a0e87a 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -48,6 +48,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS35L32 if I2C
select SND_SOC_CS35L33 if I2C
+ select SND_SOC_CS35L34 if I2C
+ select SND_SOC_CS42L42 if I2C
select SND_SOC_CS42L51_I2C if I2C
select SND_SOC_CS42L52 if I2C && INPUT
select SND_SOC_CS42L56 if I2C && INPUT
@@ -399,6 +401,14 @@ config SND_SOC_CS35L33
tristate "Cirrus Logic CS35L33 CODEC"
depends on I2C
+config SND_SOC_CS35L34
+ tristate "Cirrus Logic CS35L34 CODEC"
+ depends on I2C
+
+config SND_SOC_CS42L42
+ tristate "Cirrus Logic CS42L42 CODEC"
+ depends on I2C
+
config SND_SOC_CS42L51
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 958cd4912fbc..4bcafc345248 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -38,6 +38,8 @@ snd-soc-bt-sco-objs := bt-sco.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
snd-soc-cs35l33-objs := cs35l33.o
+snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
@@ -263,6 +265,8 @@ obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o
+obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.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
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 935ff7cb71c5..312b2a11abb6 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2587,8 +2587,6 @@ static struct platform_driver ab8500_codec_platform_driver = {
},
.probe = ab8500_codec_driver_probe,
.remove = ab8500_codec_driver_remove,
- .suspend = NULL,
- .resume = NULL,
};
module_platform_driver(ab8500_codec_platform_driver);
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index 439aa3ff1f99..b36511d965c8 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -160,7 +160,7 @@ static int adau17x1_dsp_mux_enum_put(struct snd_kcontrol *kcontrol,
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct adau *adau = snd_soc_codec_get_drvdata(codec);
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
unsigned int stream = e->shift_l;
unsigned int val, change;
int reg;
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 846ca079845f..0a734d910850 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -191,6 +191,14 @@ int arizona_init_spk(struct snd_soc_codec *codec)
break;
}
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_spk);
+
+int arizona_init_spk_irqs(struct arizona *arizona)
+{
+ int ret;
+
ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN,
"Thermal warning", arizona_thermal_warn,
arizona);
@@ -209,19 +217,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
return 0;
}
-EXPORT_SYMBOL_GPL(arizona_init_spk);
+EXPORT_SYMBOL_GPL(arizona_init_spk_irqs);
-int arizona_free_spk(struct snd_soc_codec *codec)
+int arizona_free_spk_irqs(struct arizona *arizona)
{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->arizona;
-
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT_WARN, arizona);
arizona_free_irq(arizona, ARIZONA_IRQ_SPK_OVERHEAT, arizona);
return 0;
}
-EXPORT_SYMBOL_GPL(arizona_free_spk);
+EXPORT_SYMBOL_GPL(arizona_free_spk_irqs);
static const struct snd_soc_dapm_route arizona_mono_routes[] = {
{ "OUT1R", NULL, "OUT1L" },
@@ -252,6 +257,7 @@ EXPORT_SYMBOL_GPL(arizona_init_mono);
int arizona_init_gpio(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
struct arizona *arizona = priv->arizona;
int i;
@@ -259,21 +265,24 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
switch (arizona->type) {
case WM5110:
case WM8280:
- snd_soc_dapm_disable_pin(dapm, "DRC2 Signal Activity");
+ snd_soc_component_disable_pin(component,
+ "DRC2 Signal Activity");
break;
default:
break;
}
- snd_soc_dapm_disable_pin(dapm, "DRC1 Signal Activity");
+ snd_soc_component_disable_pin(component, "DRC1 Signal Activity");
for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
- snd_soc_dapm_enable_pin(dapm, "DRC1 Signal Activity");
+ snd_soc_component_enable_pin(component,
+ "DRC1 Signal Activity");
break;
case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
- snd_soc_dapm_enable_pin(dapm, "DRC2 Signal Activity");
+ snd_soc_component_enable_pin(component,
+ "DRC2 Signal Activity");
break;
default:
break;
@@ -1233,6 +1242,46 @@ static int arizona_set_opclk(struct snd_soc_codec *codec, unsigned int clk,
return -EINVAL;
}
+int arizona_clk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ unsigned int val;
+ int clk_idx;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, w->reg, &val);
+ if (ret) {
+ dev_err(codec->dev, "Failed to check clock source: %d\n", ret);
+ return ret;
+ }
+
+ val = (val & ARIZONA_SYSCLK_SRC_MASK) >> ARIZONA_SYSCLK_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_CLK_SRC_MCLK1:
+ clk_idx = ARIZONA_MCLK1;
+ break;
+ case ARIZONA_CLK_SRC_MCLK2:
+ clk_idx = ARIZONA_MCLK2;
+ break;
+ default:
+ return 0;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ return clk_prepare_enable(arizona->mclk[clk_idx]);
+ case SND_SOC_DAPM_POST_PMD:
+ clk_disable_unprepare(arizona->mclk[clk_idx]);
+ return 0;
+ default:
+ return 0;
+ }
+}
+EXPORT_SYMBOL_GPL(arizona_clk_ev);
+
int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
@@ -2242,6 +2291,42 @@ static int arizona_is_enabled_fll(struct arizona_fll *fll, int base)
return reg & ARIZONA_FLL1_ENA;
}
+static int arizona_set_fll_clks(struct arizona_fll *fll, int base, bool ena)
+{
+ struct arizona *arizona = fll->arizona;
+ unsigned int val;
+ struct clk *clk;
+ int ret;
+
+ ret = regmap_read(arizona->regmap, base + 6, &val);
+ if (ret != 0) {
+ arizona_fll_err(fll, "Failed to read current source: %d\n",
+ ret);
+ return ret;
+ }
+
+ val &= ARIZONA_FLL1_CLK_REF_SRC_MASK;
+ val >>= ARIZONA_FLL1_CLK_REF_SRC_SHIFT;
+
+ switch (val) {
+ case ARIZONA_FLL_SRC_MCLK1:
+ clk = arizona->mclk[ARIZONA_MCLK1];
+ break;
+ case ARIZONA_FLL_SRC_MCLK2:
+ clk = arizona->mclk[ARIZONA_MCLK2];
+ break;
+ default:
+ return 0;
+ }
+
+ if (ena) {
+ return clk_prepare_enable(clk);
+ } else {
+ clk_disable_unprepare(clk);
+ return 0;
+ }
+}
+
static int arizona_enable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
@@ -2264,6 +2349,10 @@ static int arizona_enable_fll(struct arizona_fll *fll)
udelay(32);
regmap_update_bits_async(fll->arizona->regmap, fll->base + 0x9,
ARIZONA_FLL1_GAIN_MASK, 0);
+
+ if (arizona_is_enabled_fll(fll, fll->base + 0x10) > 0)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+ arizona_set_fll_clks(fll, fll->base, false);
}
/*
@@ -2318,10 +2407,13 @@ static int arizona_enable_fll(struct arizona_fll *fll)
if (!already_enabled)
pm_runtime_get_sync(arizona->dev);
- if (use_sync)
+ if (use_sync) {
+ arizona_set_fll_clks(fll, fll->base + 0x10, true);
regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA,
ARIZONA_FLL1_SYNC_ENA);
+ }
+ arizona_set_fll_clks(fll, fll->base, true);
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_ENA, ARIZONA_FLL1_ENA);
@@ -2354,19 +2446,24 @@ static int arizona_enable_fll(struct arizona_fll *fll)
static void arizona_disable_fll(struct arizona_fll *fll)
{
struct arizona *arizona = fll->arizona;
- bool change;
+ bool ref_change, sync_change;
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, ARIZONA_FLL1_FREERUN);
regmap_update_bits_check(arizona->regmap, fll->base + 1,
- ARIZONA_FLL1_ENA, 0, &change);
- regmap_update_bits(arizona->regmap, fll->base + 0x11,
- ARIZONA_FLL1_SYNC_ENA, 0);
+ ARIZONA_FLL1_ENA, 0, &ref_change);
+ regmap_update_bits_check(arizona->regmap, fll->base + 0x11,
+ ARIZONA_FLL1_SYNC_ENA, 0, &sync_change);
regmap_update_bits_async(arizona->regmap, fll->base + 1,
ARIZONA_FLL1_FREERUN, 0);
- if (change)
+ if (sync_change)
+ arizona_set_fll_clks(fll, fll->base + 0x10, false);
+
+ if (ref_change) {
+ arizona_set_fll_clks(fll, fll->base, false);
pm_runtime_put_autosuspend(arizona->dev);
+ }
}
int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
@@ -2598,30 +2695,6 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
-int arizona_register_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb,
- int (*notify)(struct notifier_block *nb,
- unsigned long action, void *data))
-{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->arizona;
-
- nb->notifier_call = notify;
-
- return blocking_notifier_chain_register(&arizona->notifier, nb);
-}
-EXPORT_SYMBOL_GPL(arizona_register_notifier);
-
-int arizona_unregister_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb)
-{
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->arizona;
-
- return blocking_notifier_chain_unregister(&arizona->notifier, nb);
-}
-EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
-
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 850aa338ba29..56707860657c 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -14,6 +14,8 @@
#define _ASOC_ARIZONA_H
#include <linux/completion.h>
+#include <linux/notifier.h>
+#include <linux/mfd/arizona/core.h>
#include <sound/soc.h>
@@ -66,7 +68,6 @@
/* Notifier events */
#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
-struct arizona;
struct wm_adsp;
struct arizona_dai_priv {
@@ -255,26 +256,24 @@ extern const struct soc_enum arizona_output_anc_src[];
extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
-extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_out_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_hp_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-extern int arizona_anc_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol,
- int event);
-
-extern int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-extern int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
- struct snd_ctl_elem_value *ucontrol);
-
-extern int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
- int source, unsigned int freq, int dir);
+int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_out_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_hp_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_anc_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+
+int arizona_eq_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+
+int arizona_clk_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
+ int event);
+int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id, int source,
+ unsigned int freq, int dir);
extern const struct snd_soc_dai_ops arizona_dai_ops;
extern const struct snd_soc_dai_ops arizona_simple_dai_ops;
@@ -297,41 +296,57 @@ struct arizona_fll {
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
};
-extern int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
-extern int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
-extern int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *kcontrol, int event);
-extern void arizona_init_dvfs(struct arizona_priv *priv);
-
-extern int arizona_init_fll(struct arizona *arizona, int id, int base,
- int lock_irq, int ok_irq, struct arizona_fll *fll);
-extern int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
- unsigned int Fref, unsigned int Fout);
-extern int arizona_set_fll(struct arizona_fll *fll, int source,
+int arizona_dvfs_up(struct snd_soc_codec *codec, unsigned int flags);
+int arizona_dvfs_down(struct snd_soc_codec *codec, unsigned int flags);
+int arizona_dvfs_sysclk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+void arizona_init_dvfs(struct arizona_priv *priv);
+
+int arizona_init_fll(struct arizona *arizona, int id, int base,
+ int lock_irq, int ok_irq, struct arizona_fll *fll);
+int arizona_set_fll_refclk(struct arizona_fll *fll, int source,
unsigned int Fref, unsigned int Fout);
+int arizona_set_fll(struct arizona_fll *fll, int source,
+ unsigned int Fref, unsigned int Fout);
-extern int arizona_init_spk(struct snd_soc_codec *codec);
-extern int arizona_init_gpio(struct snd_soc_codec *codec);
-extern int arizona_init_mono(struct snd_soc_codec *codec);
-extern int arizona_init_notifiers(struct snd_soc_codec *codec);
+int arizona_init_spk(struct snd_soc_codec *codec);
+int arizona_init_gpio(struct snd_soc_codec *codec);
+int arizona_init_mono(struct snd_soc_codec *codec);
+int arizona_init_notifiers(struct snd_soc_codec *codec);
-extern int arizona_free_spk(struct snd_soc_codec *codec);
+int arizona_init_spk_irqs(struct arizona *arizona);
+int arizona_free_spk_irqs(struct arizona *arizona);
-extern int arizona_init_dai(struct arizona_priv *priv, int dai);
+int arizona_init_dai(struct arizona_priv *priv, int dai);
int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
bool diff);
-extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+static inline int arizona_register_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb,
+ int (*notify)
+ (struct notifier_block *nb,
+ unsigned long action, void *data))
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ nb->notifier_call = notify;
+
+ return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
-extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+static inline int arizona_unregister_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
-extern int arizona_register_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb,
- int (*notify)(struct notifier_block *nb,
- unsigned long action,
- void *data));
-extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
- struct notifier_block *nb);
+ return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
#endif
diff --git a/sound/soc/codecs/cs35l34.c b/sound/soc/codecs/cs35l34.c
new file mode 100644
index 000000000000..7c5d1510cf2c
--- /dev/null
+++ b/sound/soc/codecs/cs35l34.c
@@ -0,0 +1,1251 @@
+/*
+ * cs35l34.c -- CS35l34 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regulator/machine.h>
+#include <linux/pm_runtime.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l34.h>
+
+#include "cs35l34.h"
+
+#define PDN_DONE_ATTEMPTS 10
+#define CS35L34_START_DELAY 50
+
+struct cs35l34_private {
+ struct snd_soc_codec *codec;
+ struct cs35l34_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data core_supplies[2];
+ int num_core_supplies;
+ int mclk_int;
+ bool tdm_mode;
+ struct gpio_desc *reset_gpio; /* Active-low reset GPIO */
+};
+
+static const struct reg_default cs35l34_reg[] = {
+ {CS35L34_PWRCTL1, 0x01},
+ {CS35L34_PWRCTL2, 0x19},
+ {CS35L34_PWRCTL3, 0x01},
+ {CS35L34_ADSP_CLK_CTL, 0x08},
+ {CS35L34_MCLK_CTL, 0x11},
+ {CS35L34_AMP_INP_DRV_CTL, 0x01},
+ {CS35L34_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L34_AMP_DIG_VOL, 0x00},
+ {CS35L34_AMP_ANLG_GAIN_CTL, 0x0F},
+ {CS35L34_PROTECT_CTL, 0x06},
+ {CS35L34_AMP_KEEP_ALIVE_CTL, 0x04},
+ {CS35L34_BST_CVTR_V_CTL, 0x00},
+ {CS35L34_BST_PEAK_I, 0x10},
+ {CS35L34_BST_RAMP_CTL, 0x87},
+ {CS35L34_BST_CONV_COEF_1, 0x24},
+ {CS35L34_BST_CONV_COEF_2, 0x24},
+ {CS35L34_BST_CONV_SLOPE_COMP, 0x4E},
+ {CS35L34_BST_CONV_SW_FREQ, 0x08},
+ {CS35L34_CLASS_H_CTL, 0x0D},
+ {CS35L34_CLASS_H_HEADRM_CTL, 0x0D},
+ {CS35L34_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L34_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L34_CLASS_H_STATUS, 0x05},
+ {CS35L34_VPBR_CTL, 0x0A},
+ {CS35L34_VPBR_VOL_CTL, 0x90},
+ {CS35L34_VPBR_TIMING_CTL, 0x6A},
+ {CS35L34_PRED_MAX_ATTEN_SPK_LOAD, 0x95},
+ {CS35L34_PRED_BROWNOUT_THRESH, 0x1C},
+ {CS35L34_PRED_BROWNOUT_VOL_CTL, 0x00},
+ {CS35L34_PRED_BROWNOUT_RATE_CTL, 0x10},
+ {CS35L34_PRED_WAIT_CTL, 0x10},
+ {CS35L34_PRED_ZVP_INIT_IMP_CTL, 0x08},
+ {CS35L34_PRED_MAN_SAFE_VPI_CTL, 0x80},
+ {CS35L34_VPBR_ATTEN_STATUS, 0x00},
+ {CS35L34_PRED_BRWNOUT_ATT_STATUS, 0x00},
+ {CS35L34_SPKR_MON_CTL, 0xC6},
+ {CS35L34_ADSP_I2S_CTL, 0x00},
+ {CS35L34_ADSP_TDM_CTL, 0x00},
+ {CS35L34_TDM_TX_CTL_1_VMON, 0x00},
+ {CS35L34_TDM_TX_CTL_2_IMON, 0x04},
+ {CS35L34_TDM_TX_CTL_3_VPMON, 0x03},
+ {CS35L34_TDM_TX_CTL_4_VBSTMON, 0x07},
+ {CS35L34_TDM_TX_CTL_5_FLAG1, 0x08},
+ {CS35L34_TDM_TX_CTL_6_FLAG2, 0x09},
+ {CS35L34_TDM_TX_SLOT_EN_1, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_2, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_3, 0x00},
+ {CS35L34_TDM_TX_SLOT_EN_4, 0x00},
+ {CS35L34_TDM_RX_CTL_1_AUDIN, 0x40},
+ {CS35L34_TDM_RX_CTL_3_ALIVE, 0x04},
+ {CS35L34_MULT_DEV_SYNCH1, 0x00},
+ {CS35L34_MULT_DEV_SYNCH2, 0x80},
+ {CS35L34_PROT_RELEASE_CTL, 0x00},
+ {CS35L34_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L34_DIAG_MODE_CTL_1, 0x00},
+ {CS35L34_DIAG_MODE_CTL_2, 0x00},
+ {CS35L34_INT_MASK_1, 0xFF},
+ {CS35L34_INT_MASK_2, 0xFF},
+ {CS35L34_INT_MASK_3, 0xFF},
+ {CS35L34_INT_MASK_4, 0xFF},
+ {CS35L34_INT_STATUS_1, 0x30},
+ {CS35L34_INT_STATUS_2, 0x05},
+ {CS35L34_INT_STATUS_3, 0x00},
+ {CS35L34_INT_STATUS_4, 0x00},
+ {CS35L34_OTP_TRIM_STATUS, 0x00},
+};
+
+static bool cs35l34_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_DEVID_AB:
+ case CS35L34_DEVID_CD:
+ case CS35L34_DEVID_E:
+ case CS35L34_FAB_ID:
+ case CS35L34_REV_ID:
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ case CS35L34_CLASS_H_STATUS:
+ case CS35L34_VPBR_ATTEN_STATUS:
+ case CS35L34_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l34_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_DEVID_AB:
+ case CS35L34_DEVID_CD:
+ case CS35L34_DEVID_E:
+ case CS35L34_FAB_ID:
+ case CS35L34_REV_ID:
+ case CS35L34_PWRCTL1:
+ case CS35L34_PWRCTL2:
+ case CS35L34_PWRCTL3:
+ case CS35L34_ADSP_CLK_CTL:
+ case CS35L34_MCLK_CTL:
+ case CS35L34_AMP_INP_DRV_CTL:
+ case CS35L34_AMP_DIG_VOL_CTL:
+ case CS35L34_AMP_DIG_VOL:
+ case CS35L34_AMP_ANLG_GAIN_CTL:
+ case CS35L34_PROTECT_CTL:
+ case CS35L34_AMP_KEEP_ALIVE_CTL:
+ case CS35L34_BST_CVTR_V_CTL:
+ case CS35L34_BST_PEAK_I:
+ case CS35L34_BST_RAMP_CTL:
+ case CS35L34_BST_CONV_COEF_1:
+ case CS35L34_BST_CONV_COEF_2:
+ case CS35L34_BST_CONV_SLOPE_COMP:
+ case CS35L34_BST_CONV_SW_FREQ:
+ case CS35L34_CLASS_H_CTL:
+ case CS35L34_CLASS_H_HEADRM_CTL:
+ case CS35L34_CLASS_H_RELEASE_RATE:
+ case CS35L34_CLASS_H_FET_DRIVE_CTL:
+ case CS35L34_CLASS_H_STATUS:
+ case CS35L34_VPBR_CTL:
+ case CS35L34_VPBR_VOL_CTL:
+ case CS35L34_VPBR_TIMING_CTL:
+ case CS35L34_PRED_MAX_ATTEN_SPK_LOAD:
+ case CS35L34_PRED_BROWNOUT_THRESH:
+ case CS35L34_PRED_BROWNOUT_VOL_CTL:
+ case CS35L34_PRED_BROWNOUT_RATE_CTL:
+ case CS35L34_PRED_WAIT_CTL:
+ case CS35L34_PRED_ZVP_INIT_IMP_CTL:
+ case CS35L34_PRED_MAN_SAFE_VPI_CTL:
+ case CS35L34_VPBR_ATTEN_STATUS:
+ case CS35L34_PRED_BRWNOUT_ATT_STATUS:
+ case CS35L34_SPKR_MON_CTL:
+ case CS35L34_ADSP_I2S_CTL:
+ case CS35L34_ADSP_TDM_CTL:
+ case CS35L34_TDM_TX_CTL_1_VMON:
+ case CS35L34_TDM_TX_CTL_2_IMON:
+ case CS35L34_TDM_TX_CTL_3_VPMON:
+ case CS35L34_TDM_TX_CTL_4_VBSTMON:
+ case CS35L34_TDM_TX_CTL_5_FLAG1:
+ case CS35L34_TDM_TX_CTL_6_FLAG2:
+ case CS35L34_TDM_TX_SLOT_EN_1:
+ case CS35L34_TDM_TX_SLOT_EN_2:
+ case CS35L34_TDM_TX_SLOT_EN_3:
+ case CS35L34_TDM_TX_SLOT_EN_4:
+ case CS35L34_TDM_RX_CTL_1_AUDIN:
+ case CS35L34_TDM_RX_CTL_3_ALIVE:
+ case CS35L34_MULT_DEV_SYNCH1:
+ case CS35L34_MULT_DEV_SYNCH2:
+ case CS35L34_PROT_RELEASE_CTL:
+ case CS35L34_DIAG_MODE_REG_LOCK:
+ case CS35L34_DIAG_MODE_CTL_1:
+ case CS35L34_DIAG_MODE_CTL_2:
+ case CS35L34_INT_MASK_1:
+ case CS35L34_INT_MASK_2:
+ case CS35L34_INT_MASK_3:
+ case CS35L34_INT_MASK_4:
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ case CS35L34_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l34_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L34_INT_STATUS_1:
+ case CS35L34_INT_STATUS_2:
+ case CS35L34_INT_STATUS_3:
+ case CS35L34_INT_STATUS_4:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs35l34_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (priv->tdm_mode)
+ regmap_update_bits(priv->regmap, CS35L34_PWRCTL3,
+ CS35L34_PDN_TDM, 0x00);
+
+ ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, 0);
+ if (ret < 0) {
+ dev_err(codec->dev, "Cannot set Power bits %d\n", ret);
+ return ret;
+ }
+ usleep_range(5000, 5100);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (priv->tdm_mode) {
+ regmap_update_bits(priv->regmap, CS35L34_PWRCTL3,
+ CS35L34_PDN_TDM, CS35L34_PDN_TDM);
+ }
+ ret = regmap_update_bits(priv->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static int cs35l34_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg, bit_pos;
+ int slot, slot_num;
+
+ if (slot_width != 8)
+ return -EINVAL;
+
+ priv->tdm_mode = true;
+ /* scan rx_mask for aud slot */
+ slot = ffs(rx_mask) - 1;
+ if (slot >= 0)
+ snd_soc_update_bits(codec, CS35L34_TDM_RX_CTL_1_AUDIN,
+ CS35L34_X_LOC, slot);
+
+ /* scan tx_mask: vmon(2 slots); imon (2 slots); vpmon (1 slot)
+ * vbstmon (1 slot)
+ */
+ slot = ffs(tx_mask) - 1;
+ slot_num = 0;
+
+ /* disable vpmon/vbstmon: enable later if set in tx_mask */
+ snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_3_VPMON,
+ CS35L34_X_STATE | CS35L34_X_LOC,
+ CS35L34_X_STATE | CS35L34_X_LOC);
+ snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_4_VBSTMON,
+ CS35L34_X_STATE | CS35L34_X_LOC,
+ CS35L34_X_STATE | CS35L34_X_LOC);
+
+ /* disconnect {vp,vbst}_mon routes: eanble later if set in tx_mask*/
+ while (slot >= 0) {
+ /* configure VMON_TX_LOC */
+ if (slot_num == 0)
+ snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_1_VMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+
+ /* configure IMON_TX_LOC */
+ if (slot_num == 4) {
+ snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_2_IMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+ /* configure VPMON_TX_LOC */
+ if (slot_num == 3) {
+ snd_soc_update_bits(codec, CS35L34_TDM_TX_CTL_3_VPMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+ /* configure VBSTMON_TX_LOC */
+ if (slot_num == 7) {
+ snd_soc_update_bits(codec,
+ CS35L34_TDM_TX_CTL_4_VBSTMON,
+ CS35L34_X_STATE | CS35L34_X_LOC, slot);
+ }
+
+ /* Enable the relevant tx slot */
+ reg = CS35L34_TDM_TX_SLOT_EN_4 - (slot/8);
+ bit_pos = slot - ((slot / 8) * (8));
+ snd_soc_update_bits(codec, reg,
+ 1 << bit_pos, 1 << bit_pos);
+
+ tx_mask &= ~(1 << slot);
+ slot = ffs(tx_mask) - 1;
+ slot_num++;
+ }
+
+ return 0;
+}
+
+static int cs35l34_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL,
+ CS35L34_BST_CVTL_MASK, priv->pdata.boost_vtge);
+ usleep_range(5000, 5100);
+ regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(priv->regmap, CS35L34_BST_CVTR_V_CTL,
+ CS35L34_BST_CVTL_MASK, 0);
+ regmap_update_bits(priv->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, CS35L34_MUTE);
+ usleep_range(5000, 5100);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 300, 100, 0);
+
+
+static const struct snd_kcontrol_new cs35l34_snd_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Volume", CS35L34_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Amp Gain Volume", CS35L34_AMP_ANLG_GAIN_CTL,
+ 0, 0xF, 0, amp_gain_tlv),
+};
+
+
+static int cs35l34_mclk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ int ret, i;
+ unsigned int reg;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ ret = regmap_read(priv->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ &reg);
+ if (ret != 0) {
+ pr_err("%s regmap read failure %d\n", __func__, ret);
+ return ret;
+ }
+ if (reg & CS35L34_AMP_DIGSFT)
+ msleep(40);
+ else
+ usleep_range(2000, 2100);
+
+ for (i = 0; i < PDN_DONE_ATTEMPTS; i++) {
+ ret = regmap_read(priv->regmap, CS35L34_INT_STATUS_2,
+ &reg);
+ if (ret != 0) {
+ pr_err("%s regmap read failure %d\n",
+ __func__, ret);
+ return ret;
+ }
+ if (reg & CS35L34_PDN_DONE)
+ break;
+
+ usleep_range(5000, 5100);
+ }
+ if (i == PDN_DONE_ATTEMPTS)
+ pr_err("%s Device did not power down properly\n",
+ __func__);
+ break;
+ default:
+ pr_err("Invalid event = 0x%x\n", event);
+ break;
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs35l34_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L34_PWRCTL3,
+ 1, 1, cs35l34_sdin_event,
+ SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L34_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_SUPPLY("EXTCLK", CS35L34_PWRCTL3, 7, 1,
+ cs35l34_mclk_event, SND_SOC_DAPM_PRE_PMD),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VPST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L34_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L34_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L34_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L34_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L34_PWRCTL2, 5, 1),
+ SND_SOC_DAPM_ADC("BOOST", NULL, CS35L34_PWRCTL2, 2, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L34_PWRCTL2, 0, 1, NULL, 0,
+ cs35l34_main_amp_event, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l34_audio_map[] = {
+ {"SDIN", NULL, "AMP Playback"},
+ {"BOOST", NULL, "SDIN"},
+ {"CLASS H", NULL, "BOOST"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+
+ {"VPMON ADC", NULL, "CLASS H"},
+ {"VBSTMON ADC", NULL, "CLASS H"},
+ {"SPK", NULL, "VPMON ADC"},
+ {"SPK", NULL, "VBSTMON ADC"},
+
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "EXTCLK"},
+ {"SDOUT", NULL, "EXTCLK"},
+};
+
+struct cs35l34_mclk_div {
+ int mclk;
+ int srate;
+ u8 adsp_rate;
+};
+
+static struct cs35l34_mclk_div cs35l34_mclk_coeffs[] = {
+
+ /* MCLK, Sample Rate, adsp_rate */
+
+ {5644800, 11025, 0x1},
+ {5644800, 22050, 0x4},
+ {5644800, 44100, 0x7},
+
+ {6000000, 8000, 0x0},
+ {6000000, 11025, 0x1},
+ {6000000, 12000, 0x2},
+ {6000000, 16000, 0x3},
+ {6000000, 22050, 0x4},
+ {6000000, 24000, 0x5},
+ {6000000, 32000, 0x6},
+ {6000000, 44100, 0x7},
+ {6000000, 48000, 0x8},
+
+ {6144000, 8000, 0x0},
+ {6144000, 11025, 0x1},
+ {6144000, 12000, 0x2},
+ {6144000, 16000, 0x3},
+ {6144000, 22050, 0x4},
+ {6144000, 24000, 0x5},
+ {6144000, 32000, 0x6},
+ {6144000, 44100, 0x7},
+ {6144000, 48000, 0x8},
+};
+
+static int cs35l34_get_mclk_coeff(int mclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l34_mclk_coeffs); i++) {
+ if (cs35l34_mclk_coeffs[i].mclk == mclk &&
+ cs35l34_mclk_coeffs[i].srate == srate)
+ return i;
+ }
+ return -EINVAL;
+}
+
+static int cs35l34_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ 0x80, 0x80);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ 0x80, 0x00);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l34_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l34_private *priv = snd_soc_codec_get_drvdata(codec);
+ int srate = params_rate(params);
+ int ret;
+
+ int coeff = cs35l34_get_mclk_coeff(priv->mclk_int, srate);
+
+ if (coeff < 0) {
+ dev_err(codec->dev, "ERROR: Invalid mclk %d and/or srate %d\n",
+ priv->mclk_int, srate);
+ return coeff;
+ }
+
+ ret = regmap_update_bits(priv->regmap, CS35L34_ADSP_CLK_CTL,
+ CS35L34_ADSP_RATE, cs35l34_mclk_coeffs[coeff].adsp_rate);
+ if (ret != 0)
+ dev_err(codec->dev, "Failed to set clock state %d\n", ret);
+
+ return ret;
+}
+
+static unsigned int cs35l34_src_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+
+static struct snd_pcm_hw_constraint_list cs35l34_constraints = {
+ .count = ARRAY_SIZE(cs35l34_src_rates),
+ .list = cs35l34_src_rates,
+};
+
+static int cs35l34_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cs35l34_constraints);
+ return 0;
+}
+
+
+static int cs35l34_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+
+ struct snd_soc_codec *codec = dai->codec;
+
+ if (tristate)
+ snd_soc_update_bits(codec, CS35L34_PWRCTL3,
+ CS35L34_PDN_SDOUT, CS35L34_PDN_SDOUT);
+ else
+ snd_soc_update_bits(codec, CS35L34_PWRCTL3,
+ CS35L34_PDN_SDOUT, 0);
+ return 0;
+}
+
+static int cs35l34_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l34_private *cs35l34 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value;
+
+ switch (freq) {
+ case CS35L34_MCLK_5644:
+ value = CS35L34_MCLK_RATE_5P6448;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_6:
+ value = CS35L34_MCLK_RATE_6P0000;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_6144:
+ value = CS35L34_MCLK_RATE_6P1440;
+ cs35l34->mclk_int = freq;
+ break;
+ case CS35L34_MCLK_11289:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_5P6448;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ case CS35L34_MCLK_12:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P0000;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ case CS35L34_MCLK_12288:
+ value = CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_6P1440;
+ cs35l34->mclk_int = freq / 2;
+ break;
+ default:
+ dev_err(codec->dev, "ERROR: Invalid Frequency %d\n", freq);
+ cs35l34->mclk_int = 0;
+ return -EINVAL;
+ }
+ regmap_update_bits(cs35l34->regmap, CS35L34_MCLK_CTL,
+ CS35L34_MCLK_DIV | CS35L34_MCLK_RATE_MASK, value);
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l34_ops = {
+ .startup = cs35l34_pcm_startup,
+ .set_tristate = cs35l34_set_tristate,
+ .set_fmt = cs35l34_set_dai_fmt,
+ .hw_params = cs35l34_pcm_hw_params,
+ .set_sysclk = cs35l34_dai_set_sysclk,
+ .set_tdm_slot = cs35l34_set_tdm_slot,
+};
+
+static struct snd_soc_dai_driver cs35l34_dai = {
+ .name = "cs35l34",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L34_RATES,
+ .formats = CS35L34_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = CS35L34_RATES,
+ .formats = CS35L34_FORMATS,
+ },
+ .ops = &cs35l34_ops,
+ .symmetric_rates = 1,
+};
+
+static int cs35l34_boost_inductor(struct cs35l34_private *cs35l34,
+ unsigned int inductor)
+{
+ struct snd_soc_codec *codec = cs35l34->codec;
+
+ switch (inductor) {
+ case 1000: /* 1 uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x24);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x24);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x4E);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 0);
+ break;
+ case 1200: /* 1.2 uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x47);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 1);
+ break;
+ case 1500: /* 1.5uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x20);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x3C);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 2);
+ break;
+ case 2200: /* 2.2uH */
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_1, 0x19);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_COEF_2, 0x25);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SLOPE_COMP,
+ 0x23);
+ regmap_write(cs35l34->regmap, CS35L34_BST_CONV_SW_FREQ, 3);
+ break;
+ default:
+ dev_err(codec->dev, "%s Invalid Inductor Value %d uH\n",
+ __func__, inductor);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int cs35l34_probe(struct snd_soc_codec *codec)
+{
+ int ret = 0;
+ struct cs35l34_private *cs35l34 = snd_soc_codec_get_drvdata(codec);
+
+ pm_runtime_get_sync(codec->dev);
+
+ /* Set over temperature warning attenuation to 6 dB */
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_OTW_ATTN_MASK, 0x8);
+
+ /* Set Power control registers 2 and 3 to have everything
+ * powered down at initialization
+ */
+ regmap_write(cs35l34->regmap, CS35L34_PWRCTL2, 0xFD);
+ regmap_write(cs35l34->regmap, CS35L34_PWRCTL3, 0x1F);
+
+ /* Set mute bit at startup */
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_MUTE, CS35L34_MUTE);
+
+ /* Set Platform Data */
+ if (cs35l34->pdata.boost_peak)
+ regmap_update_bits(cs35l34->regmap, CS35L34_BST_PEAK_I,
+ CS35L34_BST_PEAK_MASK,
+ cs35l34->pdata.boost_peak);
+
+ if (cs35l34->pdata.gain_zc_disable)
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_GAIN_ZC_MASK, 0);
+ else
+ regmap_update_bits(cs35l34->regmap, CS35L34_PROTECT_CTL,
+ CS35L34_GAIN_ZC_MASK, CS35L34_GAIN_ZC_MASK);
+
+ if (cs35l34->pdata.aif_half_drv)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_CLK_CTL,
+ CS35L34_ADSP_DRIVE, 0);
+
+ if (cs35l34->pdata.digsft_disable)
+ regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ CS35L34_AMP_DIGSFT, 0);
+
+ if (cs35l34->pdata.amp_inv)
+ regmap_update_bits(cs35l34->regmap, CS35L34_AMP_DIG_VOL_CTL,
+ CS35L34_INV, CS35L34_INV);
+
+ if (cs35l34->pdata.boost_ind)
+ ret = cs35l34_boost_inductor(cs35l34, cs35l34->pdata.boost_ind);
+
+ if (cs35l34->pdata.i2s_sdinloc)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_I2S_CTL,
+ CS35L34_I2S_LOC_MASK,
+ cs35l34->pdata.i2s_sdinloc << CS35L34_I2S_LOC_SHIFT);
+
+ if (cs35l34->pdata.tdm_rising_edge)
+ regmap_update_bits(cs35l34->regmap, CS35L34_ADSP_TDM_CTL,
+ 1, 1);
+
+ pm_runtime_put_sync(codec->dev);
+
+ return ret;
+}
+
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l34 = {
+ .probe = cs35l34_probe,
+
+ .component_driver = {
+ .dapm_widgets = cs35l34_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l34_dapm_widgets),
+ .dapm_routes = cs35l34_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l34_audio_map),
+ .controls = cs35l34_snd_controls,
+ .num_controls = ARRAY_SIZE(cs35l34_snd_controls),
+ },
+};
+
+static struct regmap_config cs35l34_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L34_MAX_REGISTER,
+ .reg_defaults = cs35l34_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l34_reg),
+ .volatile_reg = cs35l34_volatile_register,
+ .readable_reg = cs35l34_readable_register,
+ .precious_reg = cs35l34_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs35l34_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l34_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+
+ if (of_property_read_u32(np, "cirrus,boost-vtge-millivolt",
+ &val) >= 0) {
+ /* Boost Voltage has a maximum of 8V */
+ if (val > 8000 || (val < 3300 && val > 0)) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val);
+ return -EINVAL;
+ }
+ if (val == 0)
+ pdata->boost_vtge = 0; /* Use VP */
+ else
+ pdata->boost_vtge = ((val - 3300)/100) + 1;
+ } else {
+ dev_warn(&i2c_client->dev,
+ "Boost Voltage not specified. Using VP\n");
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) {
+ pdata->boost_ind = val;
+ } else {
+ dev_err(&i2c_client->dev, "Inductor not specified.\n");
+ return -EINVAL;
+ }
+
+ if (of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val) >= 0) {
+ if (val > 3840 || val < 1200) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %d mA\n", val);
+ return -EINVAL;
+ }
+ pdata->boost_peak = ((val - 1200)/80) + 1;
+ }
+
+ pdata->aif_half_drv = of_property_read_bool(np,
+ "cirrus,aif-half-drv");
+ pdata->digsft_disable = of_property_read_bool(np,
+ "cirrus,digsft-disable");
+
+ pdata->gain_zc_disable = of_property_read_bool(np,
+ "cirrus,gain-zc-disable");
+ pdata->amp_inv = of_property_read_bool(np, "cirrus,amp-inv");
+
+ if (of_property_read_u32(np, "cirrus,i2s-sdinloc", &val) >= 0)
+ pdata->i2s_sdinloc = val;
+ if (of_property_read_u32(np, "cirrus,tdm-rising-edge", &val) >= 0)
+ pdata->tdm_rising_edge = val;
+
+ return 0;
+}
+
+static irqreturn_t cs35l34_irq_thread(int irq, void *data)
+{
+ struct cs35l34_private *cs35l34 = data;
+ struct snd_soc_codec *codec = cs35l34->codec;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_4, &mask4);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_3, &mask3);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_2, &mask2);
+ regmap_read(cs35l34->regmap, CS35L34_INT_MASK_1, &mask1);
+
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ regmap_read(cs35l34->regmap, CS35L34_INT_STATUS_1, &current1);
+
+ if (sticky1 & CS35L34_CAL_ERR) {
+ dev_err(codec->dev, "Cal error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_CAL_ERR)) {
+ dev_dbg(codec->dev, "Cal error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS,
+ CS35L34_CAL_ERR_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_CAL_ERR_RLS, 0);
+ /* note: amp will re-calibrate on next resume */
+ }
+ }
+
+ if (sticky1 & CS35L34_ALIVE_ERR)
+ dev_err(codec->dev, "Alive error\n");
+
+ if (sticky1 & CS35L34_AMP_SHORT) {
+ dev_crit(codec->dev, "Amp short error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_AMP_SHORT)) {
+ dev_dbg(codec->dev,
+ "Amp short error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS,
+ CS35L34_SHORT_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L34_OTW) {
+ dev_crit(codec->dev, "Over temperature warning\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_OTW)) {
+ dev_dbg(codec->dev,
+ "Over temperature warning release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS,
+ CS35L34_OTW_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L34_OTE) {
+ dev_crit(codec->dev, "Over temperature error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L34_OTE)) {
+ dev_dbg(codec->dev,
+ "Over temperature error release\n");
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS, 0);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS,
+ CS35L34_OTE_RLS);
+ regmap_update_bits(cs35l34->regmap,
+ CS35L34_PROT_RELEASE_CTL,
+ CS35L34_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L34_BST_HIGH) {
+ dev_crit(codec->dev, "VBST too high error; powering off!\n");
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2,
+ CS35L34_PDN_AMP, CS35L34_PDN_AMP);
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L34_LBST_SHORT) {
+ dev_crit(codec->dev, "LBST short error; powering off!\n");
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL2,
+ CS35L34_PDN_AMP, CS35L34_PDN_AMP);
+ regmap_update_bits(cs35l34->regmap, CS35L34_PWRCTL1,
+ CS35L34_PDN_ALL, CS35L34_PDN_ALL);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static const char * const cs35l34_core_supplies[] = {
+ "VA",
+ "VP",
+};
+
+static int cs35l34_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs35l34_private *cs35l34;
+ struct cs35l34_platform_data *pdata =
+ dev_get_platdata(&i2c_client->dev);
+ int i;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs35l34 = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs35l34_private),
+ GFP_KERNEL);
+ if (!cs35l34) {
+ dev_err(&i2c_client->dev, "could not allocate codec\n");
+ return -ENOMEM;
+ }
+
+ i2c_set_clientdata(i2c_client, cs35l34);
+ cs35l34->regmap = devm_regmap_init_i2c(i2c_client, &cs35l34_regmap);
+ if (IS_ERR(cs35l34->regmap)) {
+ ret = PTR_ERR(cs35l34->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ cs35l34->num_core_supplies = ARRAY_SIZE(cs35l34_core_supplies);
+ for (i = 0; i < ARRAY_SIZE(cs35l34_core_supplies); i++)
+ cs35l34->core_supplies[i].supply = cs35l34_core_supplies[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request core supplies %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l34->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(&i2c_client->dev,
+ sizeof(struct cs35l34_platform_data),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&i2c_client->dev,
+ "could not allocate pdata\n");
+ return -ENOMEM;
+ }
+ if (i2c_client->dev.of_node) {
+ ret = cs35l34_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+ cs35l34->pdata = *pdata;
+ }
+
+ ret = devm_request_threaded_irq(&i2c_client->dev, i2c_client->irq, NULL,
+ cs35l34_irq_thread, IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs35l34", cs35l34);
+ if (ret != 0)
+ dev_err(&i2c_client->dev, "Failed to request IRQ: %d\n", ret);
+
+ cs35l34->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset-gpios", GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l34->reset_gpio))
+ return PTR_ERR(cs35l34->reset_gpio);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 1);
+
+ msleep(CS35L34_START_DELAY);
+
+ ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_AB, &reg);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_CD, &reg);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs35l34->regmap, CS35L34_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS35L34_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35l34 Device ID (%X). Expected ID %X\n",
+ devid, CS35L34_CHIP_ID);
+ ret = -ENODEV;
+ goto err_regulator;
+ }
+
+ ret = regmap_read(cs35l34->regmap, CS35L34_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ goto err_regulator;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS35l34 (%x), Revision: %02X\n", devid,
+ reg & 0xFF);
+
+ /* Unmask critical interrupts */
+ regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_1,
+ CS35L34_M_CAL_ERR | CS35L34_M_ALIVE_ERR |
+ CS35L34_M_AMP_SHORT | CS35L34_M_OTW |
+ CS35L34_M_OTE, 0);
+ regmap_update_bits(cs35l34->regmap, CS35L34_INT_MASK_3,
+ CS35L34_M_BST_HIGH | CS35L34_M_LBST_SHORT, 0);
+
+ pm_runtime_set_autosuspend_delay(&i2c_client->dev, 100);
+ pm_runtime_use_autosuspend(&i2c_client->dev);
+ pm_runtime_set_active(&i2c_client->dev);
+ pm_runtime_enable(&i2c_client->dev);
+
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs35l34, &cs35l34_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev,
+ "%s: Register codec failed\n", __func__);
+ goto err_regulator;
+ }
+
+ return 0;
+
+err_regulator:
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return ret;
+}
+
+static int cs35l34_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l34_private *cs35l34 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ if (cs35l34->reset_gpio)
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return 0;
+}
+
+static int __maybe_unused cs35l34_runtime_resume(struct device *dev)
+{
+ struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_bulk_enable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ regcache_cache_only(cs35l34->regmap, false);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 1);
+ msleep(CS35L34_START_DELAY);
+
+ ret = regcache_sync(cs35l34->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+ return 0;
+err:
+ regcache_cache_only(cs35l34->regmap, true);
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return ret;
+}
+
+static int __maybe_unused cs35l34_runtime_suspend(struct device *dev)
+{
+ struct cs35l34_private *cs35l34 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs35l34->regmap, true);
+ regcache_mark_dirty(cs35l34->regmap);
+
+ gpiod_set_value_cansleep(cs35l34->reset_gpio, 0);
+
+ regulator_bulk_disable(cs35l34->num_core_supplies,
+ cs35l34->core_supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l34_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l34_runtime_suspend,
+ cs35l34_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs35l34_of_match[] = {
+ {.compatible = "cirrus,cs35l34"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l34_of_match);
+
+static const struct i2c_device_id cs35l34_id[] = {
+ {"cs35l34", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, cs35l34_id);
+
+static struct i2c_driver cs35l34_i2c_driver = {
+ .driver = {
+ .name = "cs35l34",
+ .pm = &cs35l34_pm_ops,
+ .of_match_table = cs35l34_of_match,
+
+ },
+ .id_table = cs35l34_id,
+ .probe = cs35l34_i2c_probe,
+ .remove = cs35l34_i2c_remove,
+
+};
+
+static int __init cs35l34_modinit(void)
+{
+ int ret;
+
+ ret = i2c_add_driver(&cs35l34_i2c_driver);
+ if (ret != 0) {
+ pr_err("Failed to register CS35l34 I2C driver: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+module_init(cs35l34_modinit);
+
+static void __exit cs35l34_exit(void)
+{
+ i2c_del_driver(&cs35l34_i2c_driver);
+}
+module_exit(cs35l34_exit);
+
+MODULE_DESCRIPTION("ASoC CS35l34 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <Paul.Handrigan@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l34.h b/sound/soc/codecs/cs35l34.h
new file mode 100644
index 000000000000..bcd54f127559
--- /dev/null
+++ b/sound/soc/codecs/cs35l34.h
@@ -0,0 +1,269 @@
+/*
+ * cs35l34.h -- CS35L34 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L34_H__
+#define __CS35L34_H__
+
+#define CS35L34_CHIP_ID 0x00035A34
+#define CS35L34_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L34_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L34_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L34_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L34_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L34_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L34_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L34_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L34_ADSP_CLK_CTL 0x0A /* (ADSP) Clock Ctl */
+#define CS35L34_MCLK_CTL 0x0B /* Master Clocking Ctl */
+#define CS35L34_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L34_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L34_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L34_AMP_ANLG_GAIN_CTL 0x17 /* Amplifier Analog Gain Ctl */
+#define CS35L34_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L34_AMP_KEEP_ALIVE_CTL 0x1A /* Amplifier Keep Alive Ctl */
+#define CS35L34_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L34_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L34_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L34_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L34_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L34_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L34_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L34_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L34_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L34_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L34_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L34_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L34_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L34_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L34_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L34_PRED_MAX_ATTEN_SPK_LOAD 0x40 /* PRD Max Atten / Spkr Load */
+#define CS35L34_PRED_BROWNOUT_THRESH 0x41 /* PRD Brownout Threshold */
+#define CS35L34_PRED_BROWNOUT_VOL_CTL 0x42 /* PRD Brownout Volume Ctl */
+#define CS35L34_PRED_BROWNOUT_RATE_CTL 0x43 /* PRD Brownout Rate Ctl */
+#define CS35L34_PRED_WAIT_CTL 0x44 /* PRD Wait Ctl */
+#define CS35L34_PRED_ZVP_INIT_IMP_CTL 0x46 /* PRD ZVP Initial Imp Ctl */
+#define CS35L34_PRED_MAN_SAFE_VPI_CTL 0x47 /* PRD Manual Safe VPI Ctl */
+#define CS35L34_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L34_PRED_BRWNOUT_ATT_STATUS 0x4C /* PRD Brownout Atten Status */
+#define CS35L34_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L34_ADSP_I2S_CTL 0x50 /* ADSP I2S Ctl */
+#define CS35L34_ADSP_TDM_CTL 0x51 /* ADSP TDM Ctl */
+#define CS35L34_TDM_TX_CTL_1_VMON 0x52 /* TDM TX Ctl 1 (VMON) */
+#define CS35L34_TDM_TX_CTL_2_IMON 0x53 /* TDM TX Ctl 2 (IMON) */
+#define CS35L34_TDM_TX_CTL_3_VPMON 0x54 /* TDM TX Ctl 3 (VPMON) */
+#define CS35L34_TDM_TX_CTL_4_VBSTMON 0x55 /* TDM TX Ctl 4 (VBSTMON) */
+#define CS35L34_TDM_TX_CTL_5_FLAG1 0x56 /* TDM TX Ctl 5 (FLAG1) */
+#define CS35L34_TDM_TX_CTL_6_FLAG2 0x57 /* TDM TX Ctl 6 (FLAG2) */
+#define CS35L34_TDM_TX_SLOT_EN_1 0x5A /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_2 0x5B /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_3 0x5C /* TDM TX Slot Enable */
+#define CS35L34_TDM_TX_SLOT_EN_4 0x5D /* TDM TX Slot Enable */
+#define CS35L34_TDM_RX_CTL_1_AUDIN 0x5E /* TDM RX Ctl 1 */
+#define CS35L34_TDM_RX_CTL_3_ALIVE 0x60 /* TDM RX Ctl 3 (ALIVE) */
+#define CS35L34_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L34_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L34_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L34_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L34_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L34_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L34_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L34_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L34_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L34_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L34_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L34_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L34_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L34_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L34_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L34_MAX_REGISTER 0x7F
+#define CS35L34_REGISTER_COUNT 0x4E
+
+#define CS35L34_MCLK_5644 5644800
+#define CS35L34_MCLK_6144 6144000
+#define CS35L34_MCLK_6 6000000
+#define CS35L34_MCLK_11289 11289600
+#define CS35L34_MCLK_12 12000000
+#define CS35L34_MCLK_12288 12288000
+
+/* CS35L34_PWRCTL1 */
+#define CS35L34_SFT_RST (1 << 7)
+#define CS35L34_DISCHG_FLT (1 << 1)
+#define CS35L34_PDN_ALL 1
+
+/* CS35L34_PWRCTL2 */
+#define CS35L34_PDN_VMON (1 << 7)
+#define CS35L34_PDN_IMON (1 << 6)
+#define CS35L34_PDN_CLASSH (1 << 5)
+#define CS35L34_PDN_VPBR (1 << 4)
+#define CS35L34_PDN_PRED (1 << 3)
+#define CS35L34_PDN_BST (1 << 2)
+#define CS35L34_PDN_AMP 1
+
+/* CS35L34_PWRCTL3 */
+#define CS35L34_MCLK_DIS (1 << 7)
+#define CS35L34_PDN_VBSTMON_OUT (1 << 4)
+#define CS35L34_PDN_VMON_OUT (1 << 3)
+/* Tristate the ADSP SDOUT when in I2C mode */
+#define CS35L34_PDN_SDOUT (1 << 2)
+#define CS35L34_PDN_SDIN (1 << 1)
+#define CS35L34_PDN_TDM 1
+
+/* CS35L34_ADSP_CLK_CTL */
+#define CS35L34_ADSP_RATE 0xF
+#define CS35L34_ADSP_DRIVE (1 << 4)
+#define CS35L34_ADSP_M_S (1 << 7)
+
+/* CS35L34_MCLK_CTL */
+#define CS35L34_MCLK_DIV (1 << 4)
+#define CS35L34_MCLK_RATE_MASK 0x7
+#define CS35L34_MCLK_RATE_6P1440 0x2
+#define CS35L34_MCLK_RATE_6P0000 0x1
+#define CS35L34_MCLK_RATE_5P6448 0x0
+#define CS35L34_MCLKDIS (1 << 7)
+#define CS35L34_MCLKDIV2 (1 << 6)
+#define CS35L34_SDOUT_3ST_TDM (1 << 5)
+#define CS35L34_INT_FS_RATE (1 << 4)
+#define CS35L34_ADSP_FS 0xF
+
+/* CS35L34_AMP_INP_DRV_CTL */
+#define CS35L34_DRV_STR_SRC (1 << 1)
+#define CS35L34_DRV_STR 1
+
+/* CS35L34_AMP_DIG_VOL_CTL */
+#define CS35L34_AMP_DSR_RATE_MASK 0xF0
+#define CS35L34_AMP_DSR_RATE_SHIFT (1 << 4)
+#define CS35L34_NOTCH_DIS (1 << 3)
+#define CS35L34_AMP_DIGSFT (1 << 1)
+#define CS35L34_INV 1
+
+/* CS35L34_PROTECT_CTL */
+#define CS35L34_OTW_ATTN_MASK 0xC
+#define CS35L34_OTW_THRD_MASK 0x3
+#define CS35L34_MUTE (1 << 5)
+#define CS35L34_GAIN_ZC (1 << 4)
+#define CS35L34_GAIN_ZC_MASK 0x10
+#define CS35L34_GAIN_ZC_SHIFT 4
+
+/* CS35L34_AMP_KEEP_ALIVE_CTL */
+#define CS35L34_ALIVE_WD_DIS (1 << 2)
+
+/* CS35L34_BST_CVTR_V_CTL */
+#define CS35L34_BST_CVTL_MASK 0x3F
+
+/* CS35L34_BST_PEAK_I */
+#define CS35L34_BST_PEAK_MASK 0x3F
+
+/* CS35L34_ADSP_I2S_CTL */
+#define CS35L34_I2S_LOC_MASK 0xC
+#define CS35L34_I2S_LOC_SHIFT 2
+
+/* CS35L34_MULT_DEV_SYNCH2 */
+#define CS35L34_SYNC2_MASK 0xF
+
+/* CS35L34_PROT_RELEASE_CTL */
+#define CS35L34_CAL_ERR_RLS (1 << 7)
+#define CS35L34_SHORT_RLS (1 << 2)
+#define CS35L34_OTW_RLS (1 << 1)
+#define CS35L34_OTE_RLS 1
+
+/* CS35L34_INT_MASK_1 */
+#define CS35L34_M_CAL_ERR_SHIFT 7
+#define CS35L34_M_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT)
+#define CS35L34_M_ALIVE_ERR_SHIFT 5
+#define CS35L34_M_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT)
+#define CS35L34_M_ADSP_CLK_SHIFT 4
+#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT)
+#define CS35L34_M_MCLK_SHIFT 3
+#define CS35L34_M_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT)
+#define CS35L34_M_AMP_SHORT_SHIFT 2
+#define CS35L34_M_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT)
+#define CS35L34_M_OTW_SHIFT 1
+#define CS35L34_M_OTW (1 << CS35L34_M_OTW_SHIFT)
+#define CS35L34_M_OTE_SHIFT 0
+#define CS35L34_M_OTE (1 << CS35L34_M_OTE_SHIFT)
+
+/* CS35L34_INT_MASK_2 */
+#define CS35L34_M_PDN_DONE_SHIFT 4
+#define CS35L34_M_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT)
+#define CS35L34_M_PRED_SHIFT 3
+#define CS35L34_M_PRED_ERR (1 << CS35L34_M_PRED_SHIFT)
+#define CS35L34_M_PRED_CLR_SHIFT 2
+#define CS35L34_M_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT)
+#define CS35L34_M_VPBR_SHIFT 1
+#define CS35L34_M_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT)
+#define CS35L34_M_VPBR_CLR_SHIFT 0
+#define CS35L34_M_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT)
+
+/* CS35L34_INT_MASK_3 */
+#define CS35L34_M_BST_HIGH_SHIFT 4
+#define CS35L34_M_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT)
+#define CS35L34_M_BST_HIGH_FLAG_SHIFT 3
+#define CS35L34_M_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT)
+#define CS35L34_M_BST_IPK_FLAG_SHIFT 2
+#define CS35L34_M_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT)
+#define CS35L34_M_LBST_SHORT_SHIFT 0
+#define CS35L34_M_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT)
+
+/* CS35L34_INT_MASK_4 */
+#define CS35L34_M_VMON_OVFL_SHIFT 3
+#define CS35L34_M_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT)
+#define CS35L34_M_IMON_OVFL_SHIFT 2
+#define CS35L34_M_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT)
+#define CS35L34_M_VPMON_OVFL_SHIFT 1
+#define CS35L34_M_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT)
+#define CS35L34_M_VBSTMON_OVFL_SHIFT 1
+#define CS35L34_M_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT)
+
+/* CS35L34_INT_1 */
+#define CS35L34_CAL_ERR (1 << CS35L34_M_CAL_ERR_SHIFT)
+#define CS35L34_ALIVE_ERR (1 << CS35L34_M_ALIVE_ERR_SHIFT)
+#define CS35L34_M_ADSP_CLK_ERR (1 << CS35L34_M_ADSP_CLK_SHIFT)
+#define CS35L34_MCLK_ERR (1 << CS35L34_M_MCLK_SHIFT)
+#define CS35L34_AMP_SHORT (1 << CS35L34_M_AMP_SHORT_SHIFT)
+#define CS35L34_OTW (1 << CS35L34_M_OTW_SHIFT)
+#define CS35L34_OTE (1 << CS35L34_M_OTE_SHIFT)
+
+/* CS35L34_INT_2 */
+#define CS35L34_PDN_DONE (1 << CS35L34_M_PDN_DONE_SHIFT)
+#define CS35L34_PRED_ERR (1 << CS35L34_M_PRED_SHIFT)
+#define CS35L34_PRED_CLR (1 << CS35L34_M_PRED_CLR_SHIFT)
+#define CS35L34_VPBR_ERR (1 << CS35L34_M_VPBR_SHIFT)
+#define CS35L34_VPBR_CLR (1 << CS35L34_M_VPBR_CLR_SHIFT)
+
+/* CS35L34_INT_3 */
+#define CS35L34_BST_HIGH (1 << CS35L34_M_BST_HIGH_SHIFT)
+#define CS35L34_BST_HIGH_FLAG (1 << CS35L34_M_BST_HIGH_FLAG_SHIFT)
+#define CS35L34_BST_IPK_FLAG (1 << CS35L34_M_BST_IPK_FLAG_SHIFT)
+#define CS35L34_LBST_SHORT (1 << CS35L34_M_LBST_SHORT_SHIFT)
+
+/* CS35L34_INT_4 */
+#define CS35L34_VMON_OVFL (1 << CS35L34_M_VMON_OVFL_SHIFT)
+#define CS35L34_IMON_OVFL (1 << CS35L34_M_IMON_OVFL_SHIFT)
+#define CS35L34_VPMON_OVFL (1 << CS35L34_M_VPMON_OVFL_SHIFT)
+#define CS35L34_VBSTMON_OVFL (1 << CS35L34_M_VBSTMON_OVFL_SHIFT)
+
+/* CS35L34_{RX,TX}_X */
+#define CS35L34_X_STATE_SHIFT 7
+#define CS35L34_X_STATE (1 << CS35L34_X_STATE_SHIFT)
+#define CS35L34_X_LOC_SHIFT 0
+#define CS35L34_X_LOC (0x1F << CS35L34_X_LOC_SHIFT)
+
+#define CS35L34_RATES (SNDRV_PCM_RATE_48000 | \
+ SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_32000)
+#define CS35L34_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+#endif
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index 18baea2f7d65..84f86745c30e 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -148,11 +148,11 @@ SND_SOC_DAPM_OUTPUT("AOUTR"),
};
static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
- { "Capture", NULL, "AINA" },
- { "Capture", NULL, "AINB" },
+ { "Capture", NULL, "AINL" },
+ { "Capture", NULL, "AINR" },
- { "AOUTA", NULL, "Playback" },
- { "AOUTB", NULL, "Playback" },
+ { "AOUTL", NULL, "Playback" },
+ { "AOUTR", NULL, "Playback" },
};
/**
diff --git a/sound/soc/codecs/cs42l42.c b/sound/soc/codecs/cs42l42.c
new file mode 100644
index 000000000000..55e4520cdcaf
--- /dev/null
+++ b/sound/soc/codecs/cs42l42.c
@@ -0,0 +1,1986 @@
+/*
+ * cs42l42.c -- CS42L42 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.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>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <dt-bindings/sound/cs42l42.h>
+
+#include "cs42l42.h"
+
+static const struct reg_default cs42l42_reg_defaults[] = {
+ { CS42L42_FRZ_CTL, 0x00 },
+ { CS42L42_SRC_CTL, 0x10 },
+ { CS42L42_MCLK_STATUS, 0x02 },
+ { CS42L42_MCLK_CTL, 0x02 },
+ { CS42L42_SFTRAMP_RATE, 0xA4 },
+ { CS42L42_I2C_DEBOUNCE, 0x88 },
+ { CS42L42_I2C_STRETCH, 0x03 },
+ { CS42L42_I2C_TIMEOUT, 0xB7 },
+ { CS42L42_PWR_CTL1, 0xFF },
+ { CS42L42_PWR_CTL2, 0x84 },
+ { CS42L42_PWR_CTL3, 0x20 },
+ { CS42L42_RSENSE_CTL1, 0x40 },
+ { CS42L42_RSENSE_CTL2, 0x00 },
+ { CS42L42_OSC_SWITCH, 0x00 },
+ { CS42L42_OSC_SWITCH_STATUS, 0x05 },
+ { CS42L42_RSENSE_CTL3, 0x1B },
+ { CS42L42_TSENSE_CTL, 0x1B },
+ { CS42L42_TSRS_INT_DISABLE, 0x00 },
+ { CS42L42_TRSENSE_STATUS, 0x00 },
+ { CS42L42_HSDET_CTL1, 0x77 },
+ { CS42L42_HSDET_CTL2, 0x00 },
+ { CS42L42_HS_SWITCH_CTL, 0xF3 },
+ { CS42L42_HS_DET_STATUS, 0x00 },
+ { CS42L42_HS_CLAMP_DISABLE, 0x00 },
+ { CS42L42_MCLK_SRC_SEL, 0x00 },
+ { CS42L42_SPDIF_CLK_CFG, 0x00 },
+ { CS42L42_FSYNC_PW_LOWER, 0x00 },
+ { CS42L42_FSYNC_PW_UPPER, 0x00 },
+ { CS42L42_FSYNC_P_LOWER, 0xF9 },
+ { CS42L42_FSYNC_P_UPPER, 0x00 },
+ { CS42L42_ASP_CLK_CFG, 0x00 },
+ { CS42L42_ASP_FRM_CFG, 0x10 },
+ { CS42L42_FS_RATE_EN, 0x00 },
+ { CS42L42_IN_ASRC_CLK, 0x00 },
+ { CS42L42_OUT_ASRC_CLK, 0x00 },
+ { CS42L42_PLL_DIV_CFG1, 0x00 },
+ { CS42L42_ADC_OVFL_STATUS, 0x00 },
+ { CS42L42_MIXER_STATUS, 0x00 },
+ { CS42L42_SRC_STATUS, 0x00 },
+ { CS42L42_ASP_RX_STATUS, 0x00 },
+ { CS42L42_ASP_TX_STATUS, 0x00 },
+ { CS42L42_CODEC_STATUS, 0x00 },
+ { CS42L42_DET_INT_STATUS1, 0x00 },
+ { CS42L42_DET_INT_STATUS2, 0x00 },
+ { CS42L42_SRCPL_INT_STATUS, 0x00 },
+ { CS42L42_VPMON_STATUS, 0x00 },
+ { CS42L42_PLL_LOCK_STATUS, 0x00 },
+ { CS42L42_TSRS_PLUG_STATUS, 0x00 },
+ { CS42L42_ADC_OVFL_INT_MASK, 0x01 },
+ { CS42L42_MIXER_INT_MASK, 0x0F },
+ { CS42L42_SRC_INT_MASK, 0x0F },
+ { CS42L42_ASP_RX_INT_MASK, 0x1F },
+ { CS42L42_ASP_TX_INT_MASK, 0x0F },
+ { CS42L42_CODEC_INT_MASK, 0x03 },
+ { CS42L42_SRCPL_INT_MASK, 0xFF },
+ { CS42L42_VPMON_INT_MASK, 0x01 },
+ { CS42L42_PLL_LOCK_INT_MASK, 0x01 },
+ { CS42L42_TSRS_PLUG_INT_MASK, 0x0F },
+ { CS42L42_PLL_CTL1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC0, 0x00 },
+ { CS42L42_PLL_DIV_FRAC1, 0x00 },
+ { CS42L42_PLL_DIV_FRAC2, 0x00 },
+ { CS42L42_PLL_DIV_INT, 0x40 },
+ { CS42L42_PLL_CTL3, 0x10 },
+ { CS42L42_PLL_CAL_RATIO, 0x80 },
+ { CS42L42_PLL_CTL4, 0x03 },
+ { CS42L42_LOAD_DET_RCSTAT, 0x00 },
+ { CS42L42_LOAD_DET_DONE, 0x00 },
+ { CS42L42_LOAD_DET_EN, 0x00 },
+ { CS42L42_HSBIAS_SC_AUTOCTL, 0x03 },
+ { CS42L42_WAKE_CTL, 0xC0 },
+ { CS42L42_ADC_DISABLE_MUTE, 0x00 },
+ { CS42L42_TIPSENSE_CTL, 0x02 },
+ { CS42L42_MISC_DET_CTL, 0x03 },
+ { CS42L42_MIC_DET_CTL1, 0x1F },
+ { CS42L42_MIC_DET_CTL2, 0x2F },
+ { CS42L42_DET_STATUS1, 0x00 },
+ { CS42L42_DET_STATUS2, 0x00 },
+ { CS42L42_DET_INT1_MASK, 0xE0 },
+ { CS42L42_DET_INT2_MASK, 0xFF },
+ { CS42L42_HS_BIAS_CTL, 0xC2 },
+ { CS42L42_ADC_CTL, 0x00 },
+ { CS42L42_ADC_VOLUME, 0x00 },
+ { CS42L42_ADC_WNF_HPF_CTL, 0x71 },
+ { CS42L42_DAC_CTL1, 0x00 },
+ { CS42L42_DAC_CTL2, 0x02 },
+ { CS42L42_HP_CTL, 0x0D },
+ { CS42L42_CLASSH_CTL, 0x07 },
+ { CS42L42_MIXER_CHA_VOL, 0x3F },
+ { CS42L42_MIXER_ADC_VOL, 0x3F },
+ { CS42L42_MIXER_CHB_VOL, 0x3F },
+ { CS42L42_EQ_COEF_IN0, 0x22 },
+ { CS42L42_EQ_COEF_IN1, 0x00 },
+ { CS42L42_EQ_COEF_IN2, 0x00 },
+ { CS42L42_EQ_COEF_IN3, 0x00 },
+ { CS42L42_EQ_COEF_RW, 0x00 },
+ { CS42L42_EQ_COEF_OUT0, 0x00 },
+ { CS42L42_EQ_COEF_OUT1, 0x00 },
+ { CS42L42_EQ_COEF_OUT2, 0x00 },
+ { CS42L42_EQ_COEF_OUT3, 0x00 },
+ { CS42L42_EQ_INIT_STAT, 0x00 },
+ { CS42L42_EQ_START_FILT, 0x00 },
+ { CS42L42_EQ_MUTE_CTL, 0x00 },
+ { CS42L42_SP_RX_CH_SEL, 0x04 },
+ { CS42L42_SP_RX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_RX_FS, 0x8C },
+ { CS42l42_SPDIF_CH_SEL, 0x0E },
+ { CS42L42_SP_TX_ISOC_CTL, 0x04 },
+ { CS42L42_SP_TX_FS, 0xCC },
+ { CS42L42_SPDIF_SW_CTL1, 0x3F },
+ { CS42L42_SRC_SDIN_FS, 0x40 },
+ { CS42L42_SRC_SDOUT_FS, 0x40 },
+ { CS42L42_SPDIF_CTL1, 0x01 },
+ { CS42L42_SPDIF_CTL2, 0x00 },
+ { CS42L42_SPDIF_CTL3, 0x00 },
+ { CS42L42_SPDIF_CTL4, 0x42 },
+ { CS42L42_ASP_TX_SZ_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_EN, 0x00 },
+ { CS42L42_ASP_TX_CH_AP_RES, 0x0F },
+ { CS42L42_ASP_TX_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_TX_HIZ_DLY_CFG, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_TX_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_EN, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH2_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH3_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI0_CH4_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH1_BIT_LSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_AP_RES, 0x03 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_MSB, 0x00 },
+ { CS42L42_ASP_RX_DAI1_CH2_BIT_LSB, 0x00 },
+ { CS42L42_SUB_REVID, 0x03 },
+};
+
+static bool cs42l42_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L42_PAGE_REGISTER:
+ case CS42L42_DEVID_AB:
+ case CS42L42_DEVID_CD:
+ case CS42L42_DEVID_E:
+ case CS42L42_FABID:
+ case CS42L42_REVID:
+ case CS42L42_FRZ_CTL:
+ case CS42L42_SRC_CTL:
+ case CS42L42_MCLK_STATUS:
+ case CS42L42_MCLK_CTL:
+ case CS42L42_SFTRAMP_RATE:
+ case CS42L42_I2C_DEBOUNCE:
+ case CS42L42_I2C_STRETCH:
+ case CS42L42_I2C_TIMEOUT:
+ case CS42L42_PWR_CTL1:
+ case CS42L42_PWR_CTL2:
+ case CS42L42_PWR_CTL3:
+ case CS42L42_RSENSE_CTL1:
+ case CS42L42_RSENSE_CTL2:
+ case CS42L42_OSC_SWITCH:
+ case CS42L42_OSC_SWITCH_STATUS:
+ case CS42L42_RSENSE_CTL3:
+ case CS42L42_TSENSE_CTL:
+ case CS42L42_TSRS_INT_DISABLE:
+ case CS42L42_TRSENSE_STATUS:
+ case CS42L42_HSDET_CTL1:
+ case CS42L42_HSDET_CTL2:
+ case CS42L42_HS_SWITCH_CTL:
+ case CS42L42_HS_DET_STATUS:
+ case CS42L42_HS_CLAMP_DISABLE:
+ case CS42L42_MCLK_SRC_SEL:
+ case CS42L42_SPDIF_CLK_CFG:
+ case CS42L42_FSYNC_PW_LOWER:
+ case CS42L42_FSYNC_PW_UPPER:
+ case CS42L42_FSYNC_P_LOWER:
+ case CS42L42_FSYNC_P_UPPER:
+ case CS42L42_ASP_CLK_CFG:
+ case CS42L42_ASP_FRM_CFG:
+ case CS42L42_FS_RATE_EN:
+ case CS42L42_IN_ASRC_CLK:
+ case CS42L42_OUT_ASRC_CLK:
+ case CS42L42_PLL_DIV_CFG1:
+ case CS42L42_ADC_OVFL_STATUS:
+ case CS42L42_MIXER_STATUS:
+ case CS42L42_SRC_STATUS:
+ case CS42L42_ASP_RX_STATUS:
+ case CS42L42_ASP_TX_STATUS:
+ case CS42L42_CODEC_STATUS:
+ case CS42L42_DET_INT_STATUS1:
+ case CS42L42_DET_INT_STATUS2:
+ case CS42L42_SRCPL_INT_STATUS:
+ case CS42L42_VPMON_STATUS:
+ case CS42L42_PLL_LOCK_STATUS:
+ case CS42L42_TSRS_PLUG_STATUS:
+ case CS42L42_ADC_OVFL_INT_MASK:
+ case CS42L42_MIXER_INT_MASK:
+ case CS42L42_SRC_INT_MASK:
+ case CS42L42_ASP_RX_INT_MASK:
+ case CS42L42_ASP_TX_INT_MASK:
+ case CS42L42_CODEC_INT_MASK:
+ case CS42L42_SRCPL_INT_MASK:
+ case CS42L42_VPMON_INT_MASK:
+ case CS42L42_PLL_LOCK_INT_MASK:
+ case CS42L42_TSRS_PLUG_INT_MASK:
+ case CS42L42_PLL_CTL1:
+ case CS42L42_PLL_DIV_FRAC0:
+ case CS42L42_PLL_DIV_FRAC1:
+ case CS42L42_PLL_DIV_FRAC2:
+ case CS42L42_PLL_DIV_INT:
+ case CS42L42_PLL_CTL3:
+ case CS42L42_PLL_CAL_RATIO:
+ case CS42L42_PLL_CTL4:
+ case CS42L42_LOAD_DET_RCSTAT:
+ case CS42L42_LOAD_DET_DONE:
+ case CS42L42_LOAD_DET_EN:
+ case CS42L42_HSBIAS_SC_AUTOCTL:
+ case CS42L42_WAKE_CTL:
+ case CS42L42_ADC_DISABLE_MUTE:
+ case CS42L42_TIPSENSE_CTL:
+ case CS42L42_MISC_DET_CTL:
+ case CS42L42_MIC_DET_CTL1:
+ case CS42L42_MIC_DET_CTL2:
+ case CS42L42_DET_STATUS1:
+ case CS42L42_DET_STATUS2:
+ case CS42L42_DET_INT1_MASK:
+ case CS42L42_DET_INT2_MASK:
+ case CS42L42_HS_BIAS_CTL:
+ case CS42L42_ADC_CTL:
+ case CS42L42_ADC_VOLUME:
+ case CS42L42_ADC_WNF_HPF_CTL:
+ case CS42L42_DAC_CTL1:
+ case CS42L42_DAC_CTL2:
+ case CS42L42_HP_CTL:
+ case CS42L42_CLASSH_CTL:
+ case CS42L42_MIXER_CHA_VOL:
+ case CS42L42_MIXER_ADC_VOL:
+ case CS42L42_MIXER_CHB_VOL:
+ case CS42L42_EQ_COEF_IN0:
+ case CS42L42_EQ_COEF_IN1:
+ case CS42L42_EQ_COEF_IN2:
+ case CS42L42_EQ_COEF_IN3:
+ case CS42L42_EQ_COEF_RW:
+ case CS42L42_EQ_COEF_OUT0:
+ case CS42L42_EQ_COEF_OUT1:
+ case CS42L42_EQ_COEF_OUT2:
+ case CS42L42_EQ_COEF_OUT3:
+ case CS42L42_EQ_INIT_STAT:
+ case CS42L42_EQ_START_FILT:
+ case CS42L42_EQ_MUTE_CTL:
+ case CS42L42_SP_RX_CH_SEL:
+ case CS42L42_SP_RX_ISOC_CTL:
+ case CS42L42_SP_RX_FS:
+ case CS42l42_SPDIF_CH_SEL:
+ case CS42L42_SP_TX_ISOC_CTL:
+ case CS42L42_SP_TX_FS:
+ case CS42L42_SPDIF_SW_CTL1:
+ case CS42L42_SRC_SDIN_FS:
+ case CS42L42_SRC_SDOUT_FS:
+ case CS42L42_SPDIF_CTL1:
+ case CS42L42_SPDIF_CTL2:
+ case CS42L42_SPDIF_CTL3:
+ case CS42L42_SPDIF_CTL4:
+ case CS42L42_ASP_TX_SZ_EN:
+ case CS42L42_ASP_TX_CH_EN:
+ case CS42L42_ASP_TX_CH_AP_RES:
+ case CS42L42_ASP_TX_CH1_BIT_MSB:
+ case CS42L42_ASP_TX_CH1_BIT_LSB:
+ case CS42L42_ASP_TX_HIZ_DLY_CFG:
+ case CS42L42_ASP_TX_CH2_BIT_MSB:
+ case CS42L42_ASP_TX_CH2_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_EN:
+ case CS42L42_ASP_RX_DAI0_CH1_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH1_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH1_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH2_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH2_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH2_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH3_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH3_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH3_BIT_LSB:
+ case CS42L42_ASP_RX_DAI0_CH4_AP_RES:
+ case CS42L42_ASP_RX_DAI0_CH4_BIT_MSB:
+ case CS42L42_ASP_RX_DAI0_CH4_BIT_LSB:
+ case CS42L42_ASP_RX_DAI1_CH1_AP_RES:
+ case CS42L42_ASP_RX_DAI1_CH1_BIT_MSB:
+ case CS42L42_ASP_RX_DAI1_CH1_BIT_LSB:
+ case CS42L42_ASP_RX_DAI1_CH2_AP_RES:
+ case CS42L42_ASP_RX_DAI1_CH2_BIT_MSB:
+ case CS42L42_ASP_RX_DAI1_CH2_BIT_LSB:
+ case CS42L42_SUB_REVID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs42l42_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS42L42_DEVID_AB:
+ case CS42L42_DEVID_CD:
+ case CS42L42_DEVID_E:
+ case CS42L42_MCLK_STATUS:
+ case CS42L42_TRSENSE_STATUS:
+ case CS42L42_HS_DET_STATUS:
+ case CS42L42_ADC_OVFL_STATUS:
+ case CS42L42_MIXER_STATUS:
+ case CS42L42_SRC_STATUS:
+ case CS42L42_ASP_RX_STATUS:
+ case CS42L42_ASP_TX_STATUS:
+ case CS42L42_CODEC_STATUS:
+ case CS42L42_DET_INT_STATUS1:
+ case CS42L42_DET_INT_STATUS2:
+ case CS42L42_SRCPL_INT_STATUS:
+ case CS42L42_VPMON_STATUS:
+ case CS42L42_PLL_LOCK_STATUS:
+ case CS42L42_TSRS_PLUG_STATUS:
+ case CS42L42_LOAD_DET_RCSTAT:
+ case CS42L42_LOAD_DET_DONE:
+ case CS42L42_DET_STATUS1:
+ case CS42L42_DET_STATUS2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_range_cfg cs42l42_page_range = {
+ .name = "Pages",
+ .range_min = 0,
+ .range_max = CS42L42_MAX_REGISTER,
+ .selector_reg = CS42L42_PAGE_REGISTER,
+ .selector_mask = 0xff,
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 256,
+};
+
+static const struct regmap_config cs42l42_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .readable_reg = cs42l42_readable_register,
+ .volatile_reg = cs42l42_volatile_register,
+
+ .ranges = &cs42l42_page_range,
+ .num_ranges = 1,
+
+ .max_register = CS42L42_MAX_REGISTER,
+ .reg_defaults = cs42l42_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs42l42_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static DECLARE_TLV_DB_SCALE(adc_tlv, -9600, 100, false);
+static DECLARE_TLV_DB_SCALE(mixer_tlv, -6200, 100, false);
+
+static const char * const cs42l42_hpf_freq_text[] = {
+ "1.86Hz", "120Hz", "235Hz", "466Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l42_hpf_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_HPF_CF_SHIFT,
+ cs42l42_hpf_freq_text);
+
+static const char * const cs42l42_wnf3_freq_text[] = {
+ "160Hz", "180Hz", "200Hz", "220Hz",
+ "240Hz", "260Hz", "280Hz", "300Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l42_wnf3_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_WNF_CF_SHIFT,
+ cs42l42_wnf3_freq_text);
+
+static const char * const cs42l42_wnf05_freq_text[] = {
+ "280Hz", "315Hz", "350Hz", "385Hz",
+ "420Hz", "455Hz", "490Hz", "525Hz"
+};
+
+static SOC_ENUM_SINGLE_DECL(cs42l42_wnf05_freq_enum, CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_WNF_CF_SHIFT,
+ cs42l42_wnf05_freq_text);
+
+static const struct snd_kcontrol_new cs42l42_snd_controls[] = {
+ /* ADC Volume and Filter Controls */
+ SOC_SINGLE("ADC Notch Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_NOTCH_DIS_SHIFT, true, false),
+ SOC_SINGLE("ADC Weak Force Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_FORCE_WEAK_VCM_SHIFT, true, false),
+ SOC_SINGLE("ADC Invert Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_INV_SHIFT, true, false),
+ SOC_SINGLE("ADC Boost Switch", CS42L42_ADC_CTL,
+ CS42L42_ADC_DIG_BOOST_SHIFT, true, false),
+ SOC_SINGLE_SX_TLV("ADC Volume", CS42L42_ADC_VOLUME,
+ CS42L42_ADC_VOL_SHIFT, 0xA0, 0x6C, adc_tlv),
+ SOC_SINGLE("ADC WNF Switch", CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_WNF_EN_SHIFT, true, false),
+ SOC_SINGLE("ADC HPF Switch", CS42L42_ADC_WNF_HPF_CTL,
+ CS42L42_ADC_HPF_EN_SHIFT, true, false),
+ SOC_ENUM("HPF Corner Freq", cs42l42_hpf_freq_enum),
+ SOC_ENUM("WNF 3dB Freq", cs42l42_wnf3_freq_enum),
+ SOC_ENUM("WNF 05dB Freq", cs42l42_wnf05_freq_enum),
+
+ /* DAC Volume and Filter Controls */
+ SOC_SINGLE("DACA Invert Switch", CS42L42_DAC_CTL1,
+ CS42L42_DACA_INV_SHIFT, true, false),
+ SOC_SINGLE("DACB Invert Switch", CS42L42_DAC_CTL1,
+ CS42L42_DACB_INV_SHIFT, true, false),
+ SOC_SINGLE("DAC HPF Switch", CS42L42_DAC_CTL2,
+ CS42L42_DAC_HPF_EN_SHIFT, true, false),
+ SOC_DOUBLE_R_TLV("Mixer Volume", CS42L42_MIXER_CHA_VOL,
+ CS42L42_MIXER_CHB_VOL, CS42L42_MIXER_CH_VOL_SHIFT,
+ 0x3e, 1, mixer_tlv)
+};
+
+static int cs42l42_hpdrv_evt(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ if (event & SND_SOC_DAPM_POST_PMU) {
+ /* Enable the channels */
+ snd_soc_update_bits(codec, CS42L42_ASP_RX_DAI0_EN,
+ CS42L42_ASP_RX0_CH_EN_MASK,
+ (CS42L42_ASP_RX0_CH1_EN |
+ CS42L42_ASP_RX0_CH2_EN) <<
+ CS42L42_ASP_RX0_CH_EN_SHIFT);
+
+ /* Power up */
+ snd_soc_update_bits(codec, CS42L42_PWR_CTL1,
+ CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
+ CS42L42_HP_PDN_MASK, 0);
+ } else if (event & SND_SOC_DAPM_PRE_PMD) {
+ /* Disable the channels */
+ snd_soc_update_bits(codec, CS42L42_ASP_RX_DAI0_EN,
+ CS42L42_ASP_RX0_CH_EN_MASK, 0);
+
+ /* Power down */
+ snd_soc_update_bits(codec, CS42L42_PWR_CTL1,
+ CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
+ CS42L42_HP_PDN_MASK,
+ CS42L42_ASP_DAI_PDN_MASK | CS42L42_MIXER_PDN_MASK |
+ CS42L42_HP_PDN_MASK);
+ } else {
+ dev_err(codec->dev, "Invalid event 0x%x\n", event);
+ }
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget cs42l42_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("HP"),
+ SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS42L42_ASP_CLK_CFG,
+ CS42L42_ASP_SCLK_EN_SHIFT, false),
+ SND_SOC_DAPM_OUT_DRV_E("HPDRV", SND_SOC_NOPM, 0,
+ 0, NULL, 0, cs42l42_hpdrv_evt,
+ SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD)
+};
+
+static const struct snd_soc_dapm_route cs42l42_audio_map[] = {
+ {"SDIN", NULL, "Playback"},
+ {"HPDRV", NULL, "SDIN"},
+ {"HP", NULL, "HPDRV"}
+};
+
+static int cs42l42_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ regcache_cache_only(cs42l42->regmap, false);
+ regcache_sync(cs42l42->regmap);
+ ret = regulator_bulk_enable(
+ ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "Failed to enable regulators: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ break;
+ case SND_SOC_BIAS_OFF:
+
+ regcache_cache_only(cs42l42->regmap, true);
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ break;
+ }
+
+ return 0;
+}
+
+static int cs42l42_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs42l42_private *cs42l42 =
+ (struct cs42l42_private *)snd_soc_codec_get_drvdata(codec);
+
+ cs42l42->codec = codec;
+
+ return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_dev_cs42l42 = {
+ .probe = cs42l42_codec_probe,
+ .set_bias_level = cs42l42_set_bias_level,
+ .ignore_pmdown_time = true,
+
+ .component_driver = {
+ .dapm_widgets = cs42l42_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l42_dapm_widgets),
+ .dapm_routes = cs42l42_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs42l42_audio_map),
+
+ .controls = cs42l42_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l42_snd_controls),
+ },
+};
+
+struct cs42l42_pll_params {
+ u32 sclk;
+ u8 mclk_div;
+ u8 mclk_src_sel;
+ u8 sclk_prediv;
+ u8 pll_div_int;
+ u32 pll_div_frac;
+ u8 pll_mode;
+ u8 pll_divout;
+ u32 mclk_int;
+ u8 pll_cal_ratio;
+};
+
+/*
+ * Common PLL Settings for given SCLK
+ * Table 4-5 from the Datasheet
+ */
+static const struct cs42l42_pll_params pll_ratio_table[] = {
+ { 1536000, 0, 1, 0x00, 0x7D, 0x000000, 0x03, 0x10, 12000000, 125 },
+ { 2822400, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 },
+ { 3000000, 0, 1, 0x00, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 },
+ { 3072000, 0, 1, 0x00, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 },
+ { 4000000, 0, 1, 0x00, 0x30, 0x800000, 0x03, 0x10, 12000000, 96 },
+ { 4096000, 0, 1, 0x00, 0x2E, 0xE00000, 0x03, 0x10, 12000000, 94 },
+ { 5644800, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 11289600, 128 },
+ { 6000000, 0, 1, 0x01, 0x40, 0x000000, 0x03, 0x10, 12000000, 128 },
+ { 6144000, 0, 1, 0x01, 0x3E, 0x800000, 0x03, 0x10, 12000000, 125 },
+ { 11289600, 0, 0, 0, 0, 0, 0, 0, 11289600, 0 },
+ { 12000000, 0, 0, 0, 0, 0, 0, 0, 12000000, 0 },
+ { 12288000, 0, 0, 0, 0, 0, 0, 0, 12288000, 0 },
+ { 22579200, 1, 0, 0, 0, 0, 0, 0, 22579200, 0 },
+ { 24000000, 1, 0, 0, 0, 0, 0, 0, 24000000, 0 },
+ { 24576000, 1, 0, 0, 0, 0, 0, 0, 24576000, 0 }
+};
+
+static int cs42l42_pll_config(struct snd_soc_codec *codec)
+{
+ struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
+ int i;
+ u32 fsync;
+
+ for (i = 0; i < ARRAY_SIZE(pll_ratio_table); i++) {
+ if (pll_ratio_table[i].sclk == cs42l42->sclk) {
+ /* Configure the internal sample rate */
+ snd_soc_update_bits(codec, CS42L42_MCLK_CTL,
+ CS42L42_INTERNAL_FS_MASK,
+ ((pll_ratio_table[i].mclk_int !=
+ 12000000) &&
+ (pll_ratio_table[i].mclk_int !=
+ 24000000)) <<
+ CS42L42_INTERNAL_FS_SHIFT);
+ /* Set the MCLK src (PLL or SCLK) and the divide
+ * ratio
+ */
+ snd_soc_update_bits(codec, CS42L42_MCLK_SRC_SEL,
+ CS42L42_MCLK_SRC_SEL_MASK |
+ CS42L42_MCLKDIV_MASK,
+ (pll_ratio_table[i].mclk_src_sel
+ << CS42L42_MCLK_SRC_SEL_SHIFT) |
+ (pll_ratio_table[i].mclk_div <<
+ CS42L42_MCLKDIV_SHIFT));
+ /* Set up the LRCLK */
+ fsync = cs42l42->sclk / cs42l42->srate;
+ if (((fsync * cs42l42->srate) != cs42l42->sclk)
+ || ((fsync % 2) != 0)) {
+ dev_err(codec->dev,
+ "Unsupported sclk %d/sample rate %d\n",
+ cs42l42->sclk,
+ cs42l42->srate);
+ return -EINVAL;
+ }
+ /* Set the LRCLK period */
+ snd_soc_update_bits(codec,
+ CS42L42_FSYNC_P_LOWER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_FSYNC_P_UPPER,
+ CS42L42_FSYNC_PERIOD_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PERIOD_SHIFT);
+ /* Set the LRCLK to 50% duty cycle */
+ fsync = fsync / 2;
+ snd_soc_update_bits(codec,
+ CS42L42_FSYNC_PW_LOWER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC0_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_FSYNC_PW_UPPER,
+ CS42L42_FSYNC_PULSE_WIDTH_MASK,
+ CS42L42_FRAC1_VAL(fsync - 1) <<
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_ASP_FRM_CFG,
+ CS42L42_ASP_5050_MASK,
+ CS42L42_ASP_5050_MASK);
+ /* Set the frame delay to 1.0 SCLK clocks */
+ snd_soc_update_bits(codec, CS42L42_ASP_FRM_CFG,
+ CS42L42_ASP_FSD_MASK,
+ CS42L42_ASP_FSD_1_0 <<
+ CS42L42_ASP_FSD_SHIFT);
+ /* Set the sample rates (96k or lower) */
+ snd_soc_update_bits(codec, CS42L42_FS_RATE_EN,
+ CS42L42_FS_EN_MASK,
+ (CS42L42_FS_EN_IASRC_96K |
+ CS42L42_FS_EN_OASRC_96K) <<
+ CS42L42_FS_EN_SHIFT);
+ /* Set the input/output internal MCLK clock ~12 MHz */
+ snd_soc_update_bits(codec, CS42L42_IN_ASRC_CLK,
+ CS42L42_CLK_IASRC_SEL_MASK,
+ CS42L42_CLK_IASRC_SEL_12 <<
+ CS42L42_CLK_IASRC_SEL_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_OUT_ASRC_CLK,
+ CS42L42_CLK_OASRC_SEL_MASK,
+ CS42L42_CLK_OASRC_SEL_12 <<
+ CS42L42_CLK_OASRC_SEL_SHIFT);
+ /* channel 1 on low LRCLK, 32 bit */
+ snd_soc_update_bits(codec,
+ CS42L42_ASP_RX_DAI0_CH1_AP_RES,
+ CS42L42_ASP_RX_CH_AP_MASK |
+ CS42L42_ASP_RX_CH_RES_MASK,
+ (CS42L42_ASP_RX_CH_AP_LOW <<
+ CS42L42_ASP_RX_CH_AP_SHIFT) |
+ (CS42L42_ASP_RX_CH_RES_32 <<
+ CS42L42_ASP_RX_CH_RES_SHIFT));
+ /* Channel 2 on high LRCLK, 32 bit */
+ snd_soc_update_bits(codec,
+ CS42L42_ASP_RX_DAI0_CH2_AP_RES,
+ CS42L42_ASP_RX_CH_AP_MASK |
+ CS42L42_ASP_RX_CH_RES_MASK,
+ (CS42L42_ASP_RX_CH_AP_HI <<
+ CS42L42_ASP_RX_CH_AP_SHIFT) |
+ (CS42L42_ASP_RX_CH_RES_32 <<
+ CS42L42_ASP_RX_CH_RES_SHIFT));
+ if (pll_ratio_table[i].mclk_src_sel == 0) {
+ /* Pass the clock straight through */
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK, 0);
+ } else {
+ /* Configure PLL per table 4-5 */
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_DIV_CFG1,
+ CS42L42_SCLK_PREDIV_MASK,
+ pll_ratio_table[i].sclk_prediv
+ << CS42L42_SCLK_PREDIV_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_DIV_INT,
+ CS42L42_PLL_DIV_INT_MASK,
+ pll_ratio_table[i].pll_div_int
+ << CS42L42_PLL_DIV_INT_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_DIV_FRAC0,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC0_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_DIV_FRAC1,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC1_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_DIV_FRAC2,
+ CS42L42_PLL_DIV_FRAC_MASK,
+ CS42L42_FRAC2_VAL(
+ pll_ratio_table[i].pll_div_frac)
+ << CS42L42_PLL_DIV_FRAC_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_CTL4,
+ CS42L42_PLL_MODE_MASK,
+ pll_ratio_table[i].pll_mode
+ << CS42L42_PLL_MODE_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_CTL3,
+ CS42L42_PLL_DIVOUT_MASK,
+ pll_ratio_table[i].pll_divout
+ << CS42L42_PLL_DIVOUT_SHIFT);
+ snd_soc_update_bits(codec,
+ CS42L42_PLL_CAL_RATIO,
+ CS42L42_PLL_CAL_RATIO_MASK,
+ pll_ratio_table[i].pll_cal_ratio
+ << CS42L42_PLL_CAL_RATIO_SHIFT);
+ }
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int cs42l42_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ u32 asp_cfg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFM:
+ asp_cfg_val |= CS42L42_ASP_MASTER_MODE <<
+ CS42L42_ASP_MODE_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ asp_cfg_val |= CS42L42_ASP_SLAVE_MODE <<
+ CS42L42_ASP_MODE_SHIFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Bitclock/frame inversion */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ asp_cfg_val |= CS42L42_ASP_POL_INV <<
+ CS42L42_ASP_LCPOL_IN_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ asp_cfg_val |= CS42L42_ASP_POL_INV <<
+ CS42L42_ASP_SCPOL_IN_DAC_SHIFT;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ asp_cfg_val |= CS42L42_ASP_POL_INV <<
+ CS42L42_ASP_LCPOL_IN_SHIFT;
+ asp_cfg_val |= CS42L42_ASP_POL_INV <<
+ CS42L42_ASP_SCPOL_IN_DAC_SHIFT;
+ break;
+ }
+
+ snd_soc_update_bits(codec, CS42L42_ASP_CLK_CFG,
+ CS42L42_ASP_MODE_MASK |
+ CS42L42_ASP_SCPOL_IN_DAC_MASK |
+ CS42L42_ASP_LCPOL_IN_MASK, asp_cfg_val);
+
+ return 0;
+}
+
+static int cs42l42_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
+ int retval;
+
+ cs42l42->srate = params_rate(params);
+ cs42l42->swidth = params_width(params);
+
+ retval = cs42l42_pll_config(codec);
+
+ return retval;
+}
+
+static int cs42l42_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs42l42_private *cs42l42 = snd_soc_codec_get_drvdata(codec);
+
+ cs42l42->sclk = freq;
+
+ return 0;
+}
+
+static int cs42l42_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int regval;
+ u8 fullScaleVol;
+
+ if (mute) {
+ /* Mark SCLK as not present to turn on the internal
+ * oscillator.
+ */
+ snd_soc_update_bits(codec, CS42L42_OSC_SWITCH,
+ CS42L42_SCLK_PRESENT_MASK, 0);
+
+ snd_soc_update_bits(codec, CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK,
+ 0 << CS42L42_PLL_START_SHIFT);
+
+ /* Mute the headphone */
+ snd_soc_update_bits(codec, CS42L42_HP_CTL,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK);
+ } else {
+ snd_soc_update_bits(codec, CS42L42_PLL_CTL1,
+ CS42L42_PLL_START_MASK,
+ 1 << CS42L42_PLL_START_SHIFT);
+ /* Read the headphone load */
+ regval = snd_soc_read(codec, CS42L42_LOAD_DET_RCSTAT);
+ if (((regval & CS42L42_RLA_STAT_MASK) >>
+ CS42L42_RLA_STAT_SHIFT) == CS42L42_RLA_STAT_15_OHM) {
+ fullScaleVol = CS42L42_HP_FULL_SCALE_VOL_MASK;
+ } else {
+ fullScaleVol = 0;
+ }
+
+ /* Un-mute the headphone, set the full scale volume flag */
+ snd_soc_update_bits(codec, CS42L42_HP_CTL,
+ CS42L42_HP_ANA_AMUTE_MASK |
+ CS42L42_HP_ANA_BMUTE_MASK |
+ CS42L42_HP_FULL_SCALE_VOL_MASK, fullScaleVol);
+
+ /* Mark SCLK as present, turn off internal oscillator */
+ snd_soc_update_bits(codec, CS42L42_OSC_SWITCH,
+ CS42L42_SCLK_PRESENT_MASK,
+ CS42L42_SCLK_PRESENT_MASK);
+ }
+
+ return 0;
+}
+
+#define CS42L42_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S18_3LE | \
+ SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE | \
+ SNDRV_PCM_FMTBIT_S32_LE)
+
+
+static struct snd_soc_dai_ops cs42l42_ops = {
+ .hw_params = cs42l42_pcm_hw_params,
+ .set_fmt = cs42l42_set_dai_fmt,
+ .set_sysclk = cs42l42_set_sysclk,
+ .digital_mute = cs42l42_digital_mute
+};
+
+static struct snd_soc_dai_driver cs42l42_dai = {
+ .name = "cs42l42",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = CS42L42_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = CS42L42_FORMATS,
+ },
+ .ops = &cs42l42_ops,
+};
+
+static void cs42l42_process_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int hs_det_status;
+ unsigned int int_status;
+
+ /* Mask the auto detect interrupt */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ /* Set hs detect to automatic, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (2 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ /* Read and save the hs detection result */
+ regmap_read(cs42l42->regmap, CS42L42_HS_DET_STATUS, &hs_det_status);
+
+ cs42l42->hs_type = (hs_det_status & CS42L42_HSDET_TYPE_MASK) >>
+ CS42L42_HSDET_TYPE_SHIFT;
+
+ /* Set up button detection */
+ if ((cs42l42->hs_type == CS42L42_PLUG_CTIA) ||
+ (cs42l42->hs_type == CS42L42_PLUG_OMTP)) {
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set up hs detect level sensitivity */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (1 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (1 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Turn on level detect circuitry */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK |
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (0 << CS42L42_DETECT_MODE_SHIFT) |
+ (3 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (0 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ msleep(cs42l42->btn_det_init_dbnce);
+
+ /* Clear any button interrupts before unmasking them */
+ regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2,
+ &int_status);
+
+ /* Unmask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (0 << CS42L42_M_DETECT_TF_SHIFT) |
+ (0 << CS42L42_M_DETECT_FT_SHIFT) |
+ (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+ } else {
+ /* Make sure button detect and HS bias circuits are off */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK |
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (0 << CS42L42_DETECT_MODE_SHIFT) |
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+ }
+
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DAC_CTL2,
+ CS42L42_HPOUT_PULLDOWN_MASK |
+ CS42L42_HPOUT_LOAD_MASK |
+ CS42L42_HPOUT_CLAMP_MASK |
+ CS42L42_DAC_HPF_EN_MASK |
+ CS42L42_DAC_MON_EN_MASK,
+ (0 << CS42L42_HPOUT_PULLDOWN_SHIFT) |
+ (0 << CS42L42_HPOUT_LOAD_SHIFT) |
+ (0 << CS42L42_HPOUT_CLAMP_SHIFT) |
+ (1 << CS42L42_DAC_HPF_EN_SHIFT) |
+ (0 << CS42L42_DAC_MON_EN_SHIFT));
+
+ /* Unmask tip sense interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_RS_PLUG_MASK |
+ CS42L42_RS_UNPLUG_MASK |
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_RS_PLUG_SHIFT) |
+ (1 << CS42L42_RS_UNPLUG_SHIFT) |
+ (0 << CS42L42_TS_PLUG_SHIFT) |
+ (0 << CS42L42_TS_UNPLUG_SHIFT));
+}
+
+static void cs42l42_init_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ /* Mask tip sense interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_RS_PLUG_MASK |
+ CS42L42_RS_UNPLUG_MASK |
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_RS_PLUG_SHIFT) |
+ (1 << CS42L42_RS_UNPLUG_SHIFT) |
+ (1 << CS42L42_TS_PLUG_SHIFT) |
+ (1 << CS42L42_TS_UNPLUG_SHIFT));
+
+ /* Make sure button detect and HS bias circuits are off */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK |
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (0 << CS42L42_DETECT_MODE_SHIFT) |
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set hs detect to manual, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DAC_CTL2,
+ CS42L42_HPOUT_PULLDOWN_MASK |
+ CS42L42_HPOUT_LOAD_MASK |
+ CS42L42_HPOUT_CLAMP_MASK |
+ CS42L42_DAC_HPF_EN_MASK |
+ CS42L42_DAC_MON_EN_MASK,
+ (8 << CS42L42_HPOUT_PULLDOWN_SHIFT) |
+ (0 << CS42L42_HPOUT_LOAD_SHIFT) |
+ (1 << CS42L42_HPOUT_CLAMP_SHIFT) |
+ (1 << CS42L42_DAC_HPF_EN_SHIFT) |
+ (1 << CS42L42_DAC_MON_EN_SHIFT));
+
+ /* Power up HS bias to 2.7V */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK |
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (0 << CS42L42_DETECT_MODE_SHIFT) |
+ (3 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Wait for HS bias to ramp up */
+ msleep(cs42l42->hs_bias_ramp_time);
+
+ /* Unmask auto detect interrupt */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (0 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ /* Set hs detect to automatic, enabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (3 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+}
+
+static void cs42l42_cancel_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ /* Mask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ /* Ground HS bias */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MISC_DET_CTL,
+ CS42L42_DETECT_MODE_MASK |
+ CS42L42_HSBIAS_CTL_MASK |
+ CS42L42_PDN_MIC_LVL_DET_MASK,
+ (0 << CS42L42_DETECT_MODE_SHIFT) |
+ (1 << CS42L42_HSBIAS_CTL_SHIFT) |
+ (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT));
+
+ /* Set auto HS bias settings to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSBIAS_SC_AUTOCTL,
+ CS42L42_HSBIAS_SENSE_EN_MASK |
+ CS42L42_AUTO_HSBIAS_HIZ_MASK |
+ CS42L42_TIP_SENSE_EN_MASK |
+ CS42L42_HSBIAS_SENSE_TRIP_MASK,
+ (0 << CS42L42_HSBIAS_SENSE_EN_SHIFT) |
+ (0 << CS42L42_AUTO_HSBIAS_HIZ_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_EN_SHIFT) |
+ (3 << CS42L42_HSBIAS_SENSE_TRIP_SHIFT));
+
+ /* Set hs detect to manual, disabled mode */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HSDET_CTL2,
+ CS42L42_HSDET_CTRL_MASK |
+ CS42L42_HSDET_SET_MASK |
+ CS42L42_HSBIAS_REF_MASK |
+ CS42L42_HSDET_AUTO_TIME_MASK,
+ (0 << CS42L42_HSDET_CTRL_SHIFT) |
+ (2 << CS42L42_HSDET_SET_SHIFT) |
+ (0 << CS42L42_HSBIAS_REF_SHIFT) |
+ (3 << CS42L42_HSDET_AUTO_TIME_SHIFT));
+}
+
+static void cs42l42_handle_button_press(struct cs42l42_private *cs42l42)
+{
+ int bias_level;
+ unsigned int detect_status;
+
+ /* Mask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ usleep_range(cs42l42->btn_det_event_dbnce * 1000,
+ cs42l42->btn_det_event_dbnce * 2000);
+
+ /* Test all 4 level detect biases */
+ bias_level = 1;
+ do {
+ /* Adjust button detect level sensitivity */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[bias_level] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ regmap_read(cs42l42->regmap, CS42L42_DET_STATUS2,
+ &detect_status);
+ } while ((detect_status & CS42L42_HS_TRUE_MASK) &&
+ (++bias_level < CS42L42_NUM_BIASES));
+
+ switch (bias_level) {
+ case 1: /* Function C button press */
+ dev_dbg(cs42l42->codec->dev, "Function C button press\n");
+ break;
+ case 2: /* Function B button press */
+ dev_dbg(cs42l42->codec->dev, "Function B button press\n");
+ break;
+ case 3: /* Function D button press */
+ dev_dbg(cs42l42->codec->dev, "Function D button press\n");
+ break;
+ case 4: /* Function A button press */
+ dev_dbg(cs42l42->codec->dev, "Function A button press\n");
+ break;
+ }
+
+ /* Set button detect level sensitivity back to default */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] << CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Clear any button interrupts before unmasking them */
+ regmap_read(cs42l42->regmap, CS42L42_DET_INT_STATUS2,
+ &detect_status);
+
+ /* Unmask button detect interrupts */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (0 << CS42L42_M_DETECT_TF_SHIFT) |
+ (0 << CS42L42_M_DETECT_FT_SHIFT) |
+ (0 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+}
+
+struct cs42l42_irq_params {
+ u16 status_addr;
+ u16 mask_addr;
+ u8 mask;
+};
+
+static const struct cs42l42_irq_params irq_params_table[] = {
+ {CS42L42_ADC_OVFL_STATUS, CS42L42_ADC_OVFL_INT_MASK,
+ CS42L42_ADC_OVFL_VAL_MASK},
+ {CS42L42_MIXER_STATUS, CS42L42_MIXER_INT_MASK,
+ CS42L42_MIXER_VAL_MASK},
+ {CS42L42_SRC_STATUS, CS42L42_SRC_INT_MASK,
+ CS42L42_SRC_VAL_MASK},
+ {CS42L42_ASP_RX_STATUS, CS42L42_ASP_RX_INT_MASK,
+ CS42L42_ASP_RX_VAL_MASK},
+ {CS42L42_ASP_TX_STATUS, CS42L42_ASP_TX_INT_MASK,
+ CS42L42_ASP_TX_VAL_MASK},
+ {CS42L42_CODEC_STATUS, CS42L42_CODEC_INT_MASK,
+ CS42L42_CODEC_VAL_MASK},
+ {CS42L42_DET_INT_STATUS1, CS42L42_DET_INT1_MASK,
+ CS42L42_DET_INT_VAL1_MASK},
+ {CS42L42_DET_INT_STATUS2, CS42L42_DET_INT2_MASK,
+ CS42L42_DET_INT_VAL2_MASK},
+ {CS42L42_SRCPL_INT_STATUS, CS42L42_SRCPL_INT_MASK,
+ CS42L42_SRCPL_VAL_MASK},
+ {CS42L42_VPMON_STATUS, CS42L42_VPMON_INT_MASK,
+ CS42L42_VPMON_VAL_MASK},
+ {CS42L42_PLL_LOCK_STATUS, CS42L42_PLL_LOCK_INT_MASK,
+ CS42L42_PLL_LOCK_VAL_MASK},
+ {CS42L42_TSRS_PLUG_STATUS, CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_TSRS_PLUG_VAL_MASK}
+};
+
+static irqreturn_t cs42l42_irq_thread(int irq, void *data)
+{
+ struct cs42l42_private *cs42l42 = (struct cs42l42_private *)data;
+ struct snd_soc_codec *codec = cs42l42->codec;
+ unsigned int stickies[12];
+ unsigned int masks[12];
+ unsigned int current_plug_status;
+ unsigned int current_button_status;
+ unsigned int i;
+
+ /* Read sticky registers to clear interurpt */
+ for (i = 0; i < ARRAY_SIZE(stickies); i++) {
+ regmap_read(cs42l42->regmap, irq_params_table[i].status_addr,
+ &(stickies[i]));
+ regmap_read(cs42l42->regmap, irq_params_table[i].mask_addr,
+ &(masks[i]));
+ stickies[i] = stickies[i] & (~masks[i]) &
+ irq_params_table[i].mask;
+ }
+
+ /* Read tip sense status before handling type detect */
+ current_plug_status = (stickies[11] &
+ (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >>
+ CS42L42_TS_PLUG_SHIFT;
+
+ /* Read button sense status */
+ current_button_status = stickies[7] &
+ (CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK);
+
+ /* Check auto-detect status */
+ if ((~masks[5]) & irq_params_table[5].mask) {
+ if (stickies[5] & CS42L42_HSDET_AUTO_DONE_MASK) {
+ cs42l42_process_hs_type_detect(cs42l42);
+ dev_dbg(codec->dev,
+ "Auto detect done (%d)\n",
+ cs42l42->hs_type);
+ }
+ }
+
+ /* Check tip sense status */
+ if ((~masks[11]) & irq_params_table[11].mask) {
+ switch (current_plug_status) {
+ case CS42L42_TS_PLUG:
+ if (cs42l42->plug_state != CS42L42_TS_PLUG) {
+ cs42l42->plug_state = CS42L42_TS_PLUG;
+ cs42l42_init_hs_type_detect(cs42l42);
+ }
+ break;
+
+ case CS42L42_TS_UNPLUG:
+ if (cs42l42->plug_state != CS42L42_TS_UNPLUG) {
+ cs42l42->plug_state = CS42L42_TS_UNPLUG;
+ cs42l42_cancel_hs_type_detect(cs42l42);
+ dev_dbg(codec->dev,
+ "Unplug event\n");
+ }
+ break;
+
+ default:
+ if (cs42l42->plug_state != CS42L42_TS_TRANS)
+ cs42l42->plug_state = CS42L42_TS_TRANS;
+ }
+ }
+
+ /* Check button detect status */
+ if ((~masks[7]) & irq_params_table[7].mask) {
+ if (!(current_button_status &
+ CS42L42_M_HSBIAS_HIZ_MASK)) {
+
+ if (current_button_status &
+ CS42L42_M_DETECT_TF_MASK) {
+ dev_dbg(codec->dev,
+ "Button released\n");
+ } else if (current_button_status &
+ CS42L42_M_DETECT_FT_MASK) {
+ cs42l42_handle_button_press(cs42l42);
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void cs42l42_set_interrupt_masks(struct cs42l42_private *cs42l42)
+{
+ regmap_update_bits(cs42l42->regmap, CS42L42_ADC_OVFL_INT_MASK,
+ CS42L42_ADC_OVFL_MASK,
+ (1 << CS42L42_ADC_OVFL_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_MIXER_INT_MASK,
+ CS42L42_MIX_CHB_OVFL_MASK |
+ CS42L42_MIX_CHA_OVFL_MASK |
+ CS42L42_EQ_OVFL_MASK |
+ CS42L42_EQ_BIQUAD_OVFL_MASK,
+ (1 << CS42L42_MIX_CHB_OVFL_SHIFT) |
+ (1 << CS42L42_MIX_CHA_OVFL_SHIFT) |
+ (1 << CS42L42_EQ_OVFL_SHIFT) |
+ (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_SRC_INT_MASK,
+ CS42L42_SRC_ILK_MASK |
+ CS42L42_SRC_OLK_MASK |
+ CS42L42_SRC_IUNLK_MASK |
+ CS42L42_SRC_OUNLK_MASK,
+ (1 << CS42L42_SRC_ILK_SHIFT) |
+ (1 << CS42L42_SRC_OLK_SHIFT) |
+ (1 << CS42L42_SRC_IUNLK_SHIFT) |
+ (1 << CS42L42_SRC_OUNLK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_ASP_RX_INT_MASK,
+ CS42L42_ASPRX_NOLRCK_MASK |
+ CS42L42_ASPRX_EARLY_MASK |
+ CS42L42_ASPRX_LATE_MASK |
+ CS42L42_ASPRX_ERROR_MASK |
+ CS42L42_ASPRX_OVLD_MASK,
+ (1 << CS42L42_ASPRX_NOLRCK_SHIFT) |
+ (1 << CS42L42_ASPRX_EARLY_SHIFT) |
+ (1 << CS42L42_ASPRX_LATE_SHIFT) |
+ (1 << CS42L42_ASPRX_ERROR_SHIFT) |
+ (1 << CS42L42_ASPRX_OVLD_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_ASP_TX_INT_MASK,
+ CS42L42_ASPTX_NOLRCK_MASK |
+ CS42L42_ASPTX_EARLY_MASK |
+ CS42L42_ASPTX_LATE_MASK |
+ CS42L42_ASPTX_SMERROR_MASK,
+ (1 << CS42L42_ASPTX_NOLRCK_SHIFT) |
+ (1 << CS42L42_ASPTX_EARLY_SHIFT) |
+ (1 << CS42L42_ASPTX_LATE_SHIFT) |
+ (1 << CS42L42_ASPTX_SMERROR_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_CODEC_INT_MASK,
+ CS42L42_PDN_DONE_MASK |
+ CS42L42_HSDET_AUTO_DONE_MASK,
+ (1 << CS42L42_PDN_DONE_SHIFT) |
+ (1 << CS42L42_HSDET_AUTO_DONE_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_SRCPL_INT_MASK,
+ CS42L42_SRCPL_ADC_LK_MASK |
+ CS42L42_SRCPL_DAC_LK_MASK |
+ CS42L42_SRCPL_ADC_UNLK_MASK |
+ CS42L42_SRCPL_DAC_UNLK_MASK,
+ (1 << CS42L42_SRCPL_ADC_LK_SHIFT) |
+ (1 << CS42L42_SRCPL_DAC_LK_SHIFT) |
+ (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT) |
+ (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT1_MASK,
+ CS42L42_TIP_SENSE_UNPLUG_MASK |
+ CS42L42_TIP_SENSE_PLUG_MASK |
+ CS42L42_HSBIAS_SENSE_MASK,
+ (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT) |
+ (1 << CS42L42_TIP_SENSE_PLUG_SHIFT) |
+ (1 << CS42L42_HSBIAS_SENSE_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_DET_INT2_MASK,
+ CS42L42_M_DETECT_TF_MASK |
+ CS42L42_M_DETECT_FT_MASK |
+ CS42L42_M_HSBIAS_HIZ_MASK |
+ CS42L42_M_SHORT_RLS_MASK |
+ CS42L42_M_SHORT_DET_MASK,
+ (1 << CS42L42_M_DETECT_TF_SHIFT) |
+ (1 << CS42L42_M_DETECT_FT_SHIFT) |
+ (1 << CS42L42_M_HSBIAS_HIZ_SHIFT) |
+ (1 << CS42L42_M_SHORT_RLS_SHIFT) |
+ (1 << CS42L42_M_SHORT_DET_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_VPMON_INT_MASK,
+ CS42L42_VPMON_MASK,
+ (1 << CS42L42_VPMON_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_PLL_LOCK_INT_MASK,
+ CS42L42_PLL_LOCK_MASK,
+ (1 << CS42L42_PLL_LOCK_SHIFT));
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSRS_PLUG_INT_MASK,
+ CS42L42_RS_PLUG_MASK |
+ CS42L42_RS_UNPLUG_MASK |
+ CS42L42_TS_PLUG_MASK |
+ CS42L42_TS_UNPLUG_MASK,
+ (1 << CS42L42_RS_PLUG_SHIFT) |
+ (1 << CS42L42_RS_UNPLUG_SHIFT) |
+ (0 << CS42L42_TS_PLUG_SHIFT) |
+ (0 << CS42L42_TS_UNPLUG_SHIFT));
+}
+
+static void cs42l42_setup_hs_type_detect(struct cs42l42_private *cs42l42)
+{
+ unsigned int reg;
+
+ cs42l42->hs_type = CS42L42_PLUG_INVALID;
+
+ /* Latch analog controls to VP power domain */
+ regmap_update_bits(cs42l42->regmap, CS42L42_MIC_DET_CTL1,
+ CS42L42_LATCH_TO_VP_MASK |
+ CS42L42_EVENT_STAT_SEL_MASK |
+ CS42L42_HS_DET_LEVEL_MASK,
+ (1 << CS42L42_LATCH_TO_VP_SHIFT) |
+ (0 << CS42L42_EVENT_STAT_SEL_SHIFT) |
+ (cs42l42->bias_thresholds[0] <<
+ CS42L42_HS_DET_LEVEL_SHIFT));
+
+ /* Remove ground noise-suppression clamps */
+ regmap_update_bits(cs42l42->regmap,
+ CS42L42_HS_CLAMP_DISABLE,
+ CS42L42_HS_CLAMP_DISABLE_MASK,
+ (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT));
+
+ /* Enable the tip sense circuit */
+ regmap_update_bits(cs42l42->regmap, CS42L42_TIPSENSE_CTL,
+ CS42L42_TIP_SENSE_CTRL_MASK |
+ CS42L42_TIP_SENSE_INV_MASK |
+ CS42L42_TIP_SENSE_DEBOUNCE_MASK,
+ (3 << CS42L42_TIP_SENSE_CTRL_SHIFT) |
+ (0 << CS42L42_TIP_SENSE_INV_SHIFT) |
+ (2 << CS42L42_TIP_SENSE_DEBOUNCE_SHIFT));
+
+ /* Save the initial status of the tip sense */
+ regmap_read(cs42l42->regmap,
+ CS42L42_TSRS_PLUG_STATUS,
+ &reg);
+ cs42l42->plug_state = (((char) reg) &
+ (CS42L42_TS_PLUG_MASK | CS42L42_TS_UNPLUG_MASK)) >>
+ CS42L42_TS_PLUG_SHIFT;
+}
+
+static const unsigned int threshold_defaults[] = {
+ CS42L42_HS_DET_LEVEL_15,
+ CS42L42_HS_DET_LEVEL_8,
+ CS42L42_HS_DET_LEVEL_4,
+ CS42L42_HS_DET_LEVEL_1
+};
+
+static int cs42l42_handle_device_data(struct i2c_client *i2c_client,
+ struct cs42l42_private *cs42l42)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ unsigned int val;
+ unsigned int thresholds[CS42L42_NUM_BIASES];
+ int ret;
+ int i;
+
+ ret = of_property_read_u32(np, "cirrus,ts-inv", &val);
+
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_INV_EN:
+ case CS42L42_TS_INV_DIS:
+ cs42l42->ts_inv = val;
+ break;
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,ts-inv DT value %d\n",
+ val);
+ cs42l42->ts_inv = CS42L42_TS_INV_DIS;
+ }
+ } else {
+ cs42l42->ts_inv = CS42L42_TS_INV_DIS;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_INV_MASK,
+ (cs42l42->ts_inv << CS42L42_TS_INV_SHIFT));
+
+ ret = of_property_read_u32(np, "cirrus,ts-dbnc-rise", &val);
+
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_DBNCE_0:
+ case CS42L42_TS_DBNCE_125:
+ case CS42L42_TS_DBNCE_250:
+ case CS42L42_TS_DBNCE_500:
+ case CS42L42_TS_DBNCE_750:
+ case CS42L42_TS_DBNCE_1000:
+ case CS42L42_TS_DBNCE_1250:
+ case CS42L42_TS_DBNCE_1500:
+ cs42l42->ts_dbnc_rise = val;
+ break;
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,ts-dbnc-rise DT value %d\n",
+ val);
+ cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
+ }
+ } else {
+ cs42l42->ts_dbnc_rise = CS42L42_TS_DBNCE_1000;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_RISE_DBNCE_TIME_MASK,
+ (cs42l42->ts_dbnc_rise <<
+ CS42L42_TS_RISE_DBNCE_TIME_SHIFT));
+
+ ret = of_property_read_u32(np, "cirrus,ts-dbnc-fall", &val);
+
+ if (!ret) {
+ switch (val) {
+ case CS42L42_TS_DBNCE_0:
+ case CS42L42_TS_DBNCE_125:
+ case CS42L42_TS_DBNCE_250:
+ case CS42L42_TS_DBNCE_500:
+ case CS42L42_TS_DBNCE_750:
+ case CS42L42_TS_DBNCE_1000:
+ case CS42L42_TS_DBNCE_1250:
+ case CS42L42_TS_DBNCE_1500:
+ cs42l42->ts_dbnc_fall = val;
+ break;
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,ts-dbnc-fall DT value %d\n",
+ val);
+ cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
+ }
+ } else {
+ cs42l42->ts_dbnc_fall = CS42L42_TS_DBNCE_0;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_TSENSE_CTL,
+ CS42L42_TS_FALL_DBNCE_TIME_MASK,
+ (cs42l42->ts_dbnc_fall <<
+ CS42L42_TS_FALL_DBNCE_TIME_SHIFT));
+
+ ret = of_property_read_u32(np, "cirrus,btn-det-init-dbnce", &val);
+
+ if (!ret) {
+ if ((val >= CS42L42_BTN_DET_INIT_DBNCE_MIN) &&
+ (val <= CS42L42_BTN_DET_INIT_DBNCE_MAX))
+ cs42l42->btn_det_init_dbnce = val;
+ else {
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,btn-det-init-dbnce DT value %d\n",
+ val);
+ cs42l42->btn_det_init_dbnce =
+ CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
+ }
+ } else {
+ cs42l42->btn_det_init_dbnce =
+ CS42L42_BTN_DET_INIT_DBNCE_DEFAULT;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,btn-det-event-dbnce", &val);
+
+ if (!ret) {
+ if ((val >= CS42L42_BTN_DET_EVENT_DBNCE_MIN) &&
+ (val <= CS42L42_BTN_DET_EVENT_DBNCE_MAX))
+ cs42l42->btn_det_event_dbnce = val;
+ else {
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,btn-det-event-dbnce DT value %d\n", val);
+ cs42l42->btn_det_event_dbnce =
+ CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
+ }
+ } else {
+ cs42l42->btn_det_event_dbnce =
+ CS42L42_BTN_DET_EVENT_DBNCE_DEFAULT;
+ }
+
+ ret = of_property_read_u32_array(np, "cirrus,bias-lvls",
+ (u32 *)thresholds, CS42L42_NUM_BIASES);
+
+ if (!ret) {
+ for (i = 0; i < CS42L42_NUM_BIASES; i++) {
+ if ((thresholds[i] >= CS42L42_HS_DET_LEVEL_MIN) &&
+ (thresholds[i] <= CS42L42_HS_DET_LEVEL_MAX))
+ cs42l42->bias_thresholds[i] = thresholds[i];
+ else {
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,bias-lvls[%d] DT value %d\n", i,
+ thresholds[i]);
+ cs42l42->bias_thresholds[i] =
+ threshold_defaults[i];
+ }
+ }
+ } else {
+ for (i = 0; i < CS42L42_NUM_BIASES; i++)
+ cs42l42->bias_thresholds[i] = threshold_defaults[i];
+ }
+
+ ret = of_property_read_u32(np, "cirrus,hs-bias-ramp-rate", &val);
+
+ if (!ret) {
+ switch (val) {
+ case CS42L42_HSBIAS_RAMP_FAST_RISE_SLOW_FALL:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME0;
+ break;
+ case CS42L42_HSBIAS_RAMP_FAST:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME1;
+ break;
+ case CS42L42_HSBIAS_RAMP_SLOW:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ break;
+ case CS42L42_HSBIAS_RAMP_SLOWEST:
+ cs42l42->hs_bias_ramp_rate = val;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME3;
+ break;
+ default:
+ dev_err(&i2c_client->dev,
+ "Wrong cirrus,hs-bias-ramp-rate DT value %d\n",
+ val);
+ cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ }
+ } else {
+ cs42l42->hs_bias_ramp_rate = CS42L42_HSBIAS_RAMP_SLOW;
+ cs42l42->hs_bias_ramp_time = CS42L42_HSBIAS_RAMP_TIME2;
+ }
+
+ regmap_update_bits(cs42l42->regmap, CS42L42_HS_BIAS_CTL,
+ CS42L42_HSBIAS_RAMP_MASK,
+ (cs42l42->hs_bias_ramp_rate <<
+ CS42L42_HSBIAS_RAMP_SHIFT));
+
+ return 0;
+}
+
+static int cs42l42_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs42l42_private *cs42l42;
+ int ret, i;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs42l42 = devm_kzalloc(&i2c_client->dev, sizeof(struct cs42l42_private),
+ GFP_KERNEL);
+ if (!cs42l42)
+ return -ENOMEM;
+
+ i2c_set_clientdata(i2c_client, cs42l42);
+
+ cs42l42->regmap = devm_regmap_init_i2c(i2c_client, &cs42l42_regmap);
+ if (IS_ERR(cs42l42->regmap)) {
+ ret = PTR_ERR(cs42l42->regmap);
+ dev_err(&i2c_client->dev, "regmap_init() failed: %d\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs42l42->supplies); i++)
+ cs42l42->supplies[i].supply = cs42l42_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c_client->dev,
+ ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(&i2c_client->dev,
+ "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* Reset the Device */
+ cs42l42->reset_gpio = devm_gpiod_get_optional(&i2c_client->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(cs42l42->reset_gpio))
+ return PTR_ERR(cs42l42->reset_gpio);
+
+ if (cs42l42->reset_gpio) {
+ dev_dbg(&i2c_client->dev, "Found reset GPIO\n");
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+ }
+ mdelay(3);
+
+ /* Request IRQ */
+ ret = devm_request_threaded_irq(&i2c_client->dev,
+ i2c_client->irq,
+ NULL, cs42l42_irq_thread,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW,
+ "cs42l42", cs42l42);
+
+ if (ret != 0)
+ dev_err(&i2c_client->dev,
+ "Failed to request IRQ: %d\n", ret);
+
+ /* initialize codec */
+ ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_AB, &reg);
+ devid = (reg & 0xFF) << 12;
+
+ ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_CD, &reg);
+ devid |= (reg & 0xFF) << 4;
+
+ ret = regmap_read(cs42l42->regmap, CS42L42_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS42L42_CHIP_ID) {
+ ret = -ENODEV;
+ dev_err(&i2c_client->dev,
+ "CS42L42 Device ID (%X). Expected %X\n",
+ devid, CS42L42_CHIP_ID);
+ return ret;
+ }
+
+ ret = regmap_read(cs42l42->regmap, CS42L42_REVID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "Get Revision ID failed\n");
+ return ret;
+ }
+
+ dev_info(&i2c_client->dev,
+ "Cirrus Logic CS42L42, Revision: %02X\n", reg & 0xFF);
+
+ /* Power up the codec */
+ regmap_update_bits(cs42l42->regmap, CS42L42_PWR_CTL1,
+ CS42L42_ASP_DAO_PDN_MASK |
+ CS42L42_ASP_DAI_PDN_MASK |
+ CS42L42_MIXER_PDN_MASK |
+ CS42L42_EQ_PDN_MASK |
+ CS42L42_HP_PDN_MASK |
+ CS42L42_ADC_PDN_MASK |
+ CS42L42_PDN_ALL_MASK,
+ (1 << CS42L42_ASP_DAO_PDN_SHIFT) |
+ (1 << CS42L42_ASP_DAI_PDN_SHIFT) |
+ (1 << CS42L42_MIXER_PDN_SHIFT) |
+ (1 << CS42L42_EQ_PDN_SHIFT) |
+ (1 << CS42L42_HP_PDN_SHIFT) |
+ (1 << CS42L42_ADC_PDN_SHIFT) |
+ (0 << CS42L42_PDN_ALL_SHIFT));
+
+ if (i2c_client->dev.of_node) {
+ ret = cs42l42_handle_device_data(i2c_client, cs42l42);
+ if (ret != 0)
+ return ret;
+ }
+
+ /* Setup headset detection */
+ cs42l42_setup_hs_type_detect(cs42l42);
+
+ /* Mask/Unmask Interrupts */
+ cs42l42_set_interrupt_masks(cs42l42);
+
+ /* Register codec for machine driver */
+ ret = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs42l42, &cs42l42_dai, 1);
+ if (ret < 0)
+ goto err_disable;
+ return 0;
+
+err_disable:
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ return ret;
+}
+
+static int cs42l42_i2c_remove(struct i2c_client *i2c_client)
+{
+ struct cs42l42_private *cs42l42 = i2c_get_clientdata(i2c_client);
+
+ snd_soc_unregister_codec(&i2c_client->dev);
+
+ /* Hold down reset */
+ if (cs42l42->reset_gpio)
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs42l42_runtime_suspend(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs42l42->regmap, true);
+ regcache_mark_dirty(cs42l42->regmap);
+
+ /* Hold down reset */
+ if (cs42l42->reset_gpio)
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 0);
+
+ /* remove power */
+ regulator_bulk_disable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+
+ return 0;
+}
+
+static int cs42l42_runtime_resume(struct device *dev)
+{
+ struct cs42l42_private *cs42l42 = dev_get_drvdata(dev);
+ int ret;
+
+ /* Enable power */
+ ret = regulator_bulk_enable(ARRAY_SIZE(cs42l42->supplies),
+ cs42l42->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (cs42l42->reset_gpio)
+ gpiod_set_value_cansleep(cs42l42->reset_gpio, 1);
+
+ regcache_cache_only(cs42l42->regmap, false);
+ regcache_sync(cs42l42->regmap);
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs42l42_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs42l42_runtime_suspend, cs42l42_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs42l42_of_match[] = {
+ { .compatible = "cirrus,cs42l42", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs42l42_of_match);
+
+
+static const struct i2c_device_id cs42l42_id[] = {
+ {"cs42l42", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs42l42_id);
+
+static struct i2c_driver cs42l42_i2c_driver = {
+ .driver = {
+ .name = "cs42l42",
+ .pm = &cs42l42_runtime_pm,
+ .of_match_table = cs42l42_of_match,
+ },
+ .id_table = cs42l42_id,
+ .probe = cs42l42_i2c_probe,
+ .remove = cs42l42_i2c_remove,
+};
+
+module_i2c_driver(cs42l42_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS42L42 driver");
+MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_AUTHOR("Michael White, Cirrus Logic Inc, <michael.white@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs42l42.h b/sound/soc/codecs/cs42l42.h
new file mode 100644
index 000000000000..d87a0a5322d5
--- /dev/null
+++ b/sound/soc/codecs/cs42l42.h
@@ -0,0 +1,776 @@
+/*
+ * cs42l42.h -- CS42L42 ALSA SoC audio driver header
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ * Author: Michael White <michael.white@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS42L42_H__
+#define __CS42L42_H__
+
+#define CS42L42_PAGE_REGISTER 0x00 /* Page Select Register */
+#define CS42L42_WIN_START 0x00
+#define CS42L42_WIN_LEN 0x100
+#define CS42L42_RANGE_MIN 0x00
+#define CS42L42_RANGE_MAX 0x7F
+
+#define CS42L42_PAGE_10 0x1000
+#define CS42L42_PAGE_11 0x1100
+#define CS42L42_PAGE_12 0x1200
+#define CS42L42_PAGE_13 0x1300
+#define CS42L42_PAGE_15 0x1500
+#define CS42L42_PAGE_19 0x1900
+#define CS42L42_PAGE_1B 0x1B00
+#define CS42L42_PAGE_1C 0x1C00
+#define CS42L42_PAGE_1D 0x1D00
+#define CS42L42_PAGE_1F 0x1F00
+#define CS42L42_PAGE_20 0x2000
+#define CS42L42_PAGE_21 0x2100
+#define CS42L42_PAGE_23 0x2300
+#define CS42L42_PAGE_24 0x2400
+#define CS42L42_PAGE_25 0x2500
+#define CS42L42_PAGE_26 0x2600
+#define CS42L42_PAGE_28 0x2800
+#define CS42L42_PAGE_29 0x2900
+#define CS42L42_PAGE_2A 0x2A00
+#define CS42L42_PAGE_30 0x3000
+
+#define CS42L42_CHIP_ID 0x42A42
+
+/* Page 0x10 Global Registers */
+#define CS42L42_DEVID_AB (CS42L42_PAGE_10 + 0x01)
+#define CS42L42_DEVID_CD (CS42L42_PAGE_10 + 0x02)
+#define CS42L42_DEVID_E (CS42L42_PAGE_10 + 0x03)
+#define CS42L42_FABID (CS42L42_PAGE_10 + 0x04)
+#define CS42L42_REVID (CS42L42_PAGE_10 + 0x05)
+#define CS42L42_FRZ_CTL (CS42L42_PAGE_10 + 0x06)
+
+#define CS42L42_SRC_CTL (CS42L42_PAGE_10 + 0x07)
+#define CS42L42_SRC_BYPASS_DAC_SHIFT 1
+#define CS42L42_SRC_BYPASS_DAC_MASK (1 << CS42L42_SRC_BYPASS_DAC_SHIFT)
+
+#define CS42L42_MCLK_STATUS (CS42L42_PAGE_10 + 0x08)
+
+#define CS42L42_MCLK_CTL (CS42L42_PAGE_10 + 0x09)
+#define CS42L42_INTERNAL_FS_SHIFT 1
+#define CS42L42_INTERNAL_FS_MASK (1 << CS42L42_INTERNAL_FS_SHIFT)
+
+#define CS42L42_SFTRAMP_RATE (CS42L42_PAGE_10 + 0x0A)
+#define CS42L42_I2C_DEBOUNCE (CS42L42_PAGE_10 + 0x0E)
+#define CS42L42_I2C_STRETCH (CS42L42_PAGE_10 + 0x0F)
+#define CS42L42_I2C_TIMEOUT (CS42L42_PAGE_10 + 0x10)
+
+/* Page 0x11 Power and Headset Detect Registers */
+#define CS42L42_PWR_CTL1 (CS42L42_PAGE_11 + 0x01)
+#define CS42L42_ASP_DAO_PDN_SHIFT 7
+#define CS42L42_ASP_DAO_PDN_MASK (1 << CS42L42_ASP_DAO_PDN_SHIFT)
+#define CS42L42_ASP_DAI_PDN_SHIFT 6
+#define CS42L42_ASP_DAI_PDN_MASK (1 << CS42L42_ASP_DAI_PDN_SHIFT)
+#define CS42L42_MIXER_PDN_SHIFT 5
+#define CS42L42_MIXER_PDN_MASK (1 << CS42L42_MIXER_PDN_SHIFT)
+#define CS42L42_EQ_PDN_SHIFT 4
+#define CS42L42_EQ_PDN_MASK (1 << CS42L42_EQ_PDN_SHIFT)
+#define CS42L42_HP_PDN_SHIFT 3
+#define CS42L42_HP_PDN_MASK (1 << CS42L42_HP_PDN_SHIFT)
+#define CS42L42_ADC_PDN_SHIFT 2
+#define CS42L42_ADC_PDN_MASK (1 << CS42L42_HP_PDN_SHIFT)
+#define CS42L42_PDN_ALL_SHIFT 0
+#define CS42L42_PDN_ALL_MASK (1 << CS42L42_PDN_ALL_SHIFT)
+
+#define CS42L42_PWR_CTL2 (CS42L42_PAGE_11 + 0x02)
+#define CS42L42_ADC_SRC_PDNB_SHIFT 0
+#define CS42L42_ADC_SRC_PDNB_MASK (1 << CS42L42_ADC_SRC_PDNB_SHIFT)
+#define CS42L42_DAC_SRC_PDNB_SHIFT 1
+#define CS42L42_DAC_SRC_PDNB_MASK (1 << CS42L42_DAC_SRC_PDNB_SHIFT)
+#define CS42L42_ASP_DAI1_PDN_SHIFT 2
+#define CS42L42_ASP_DAI1_PDN_MASK (1 << CS42L42_ASP_DAI1_PDN_SHIFT)
+#define CS42L42_SRC_PDN_OVERRIDE_SHIFT 3
+#define CS42L42_SRC_PDN_OVERRIDE_MASK (1 << CS42L42_SRC_PDN_OVERRIDE_SHIFT)
+#define CS42L42_DISCHARGE_FILT_SHIFT 4
+#define CS42L42_DISCHARGE_FILT_MASK (1 << CS42L42_DISCHARGE_FILT_SHIFT)
+
+#define CS42L42_PWR_CTL3 (CS42L42_PAGE_11 + 0x03)
+#define CS42L42_RING_SENSE_PDNB_SHIFT 1
+#define CS42L42_RING_SENSE_PDNB_MASK (1 << \
+ CS42L42_RING_SENSE_PDNB_SHIFT)
+#define CS42L42_VPMON_PDNB_SHIFT 2
+#define CS42L42_VPMON_PDNB_MASK (1 << \
+ CS42L42_VPMON_PDNB_SHIFT)
+#define CS42L42_SW_CLK_STP_STAT_SEL_SHIFT 5
+#define CS42L42_SW_CLK_STP_STAT_SEL_MASK (3 << \
+ CS42L42_SW_CLK_STP_STAT_SEL_SHIFT)
+
+#define CS42L42_RSENSE_CTL1 (CS42L42_PAGE_11 + 0x04)
+#define CS42L42_RS_TRIM_R_SHIFT 0
+#define CS42L42_RS_TRIM_R_MASK (1 << \
+ CS42L42_RS_TRIM_R_SHIFT)
+#define CS42L42_RS_TRIM_T_SHIFT 1
+#define CS42L42_RS_TRIM_T_MASK (1 << \
+ CS42L42_RS_TRIM_T_SHIFT)
+#define CS42L42_HPREF_RS_SHIFT 2
+#define CS42L42_HPREF_RS_MASK (1 << \
+ CS42L42_HPREF_RS_SHIFT)
+#define CS42L42_HSBIAS_FILT_REF_RS_SHIFT 3
+#define CS42L42_HSBIAS_FILT_REF_RS_MASK (1 << \
+ CS42L42_HSBIAS_FILT_REF_RS_SHIFT)
+#define CS42L42_RING_SENSE_PU_HIZ_SHIFT 6
+#define CS42L42_RING_SENSE_PU_HIZ_MASK (1 << \
+ CS42L42_RING_SENSE_PU_HIZ_SHIFT)
+
+#define CS42L42_RSENSE_CTL2 (CS42L42_PAGE_11 + 0x05)
+#define CS42L42_TS_RS_GATE_SHIFT 7
+#define CS42L42_TS_RS_GATE_MAS (1 << CS42L42_TS_RS_GATE_SHIFT)
+
+#define CS42L42_OSC_SWITCH (CS42L42_PAGE_11 + 0x07)
+#define CS42L42_SCLK_PRESENT_SHIFT 0
+#define CS42L42_SCLK_PRESENT_MASK (1 << CS42L42_SCLK_PRESENT_SHIFT)
+
+#define CS42L42_OSC_SWITCH_STATUS (CS42L42_PAGE_11 + 0x09)
+#define CS42L42_OSC_SW_SEL_STAT_SHIFT 0
+#define CS42L42_OSC_SW_SEL_STAT_MASK (3 << CS42L42_OSC_SW_SEL_STAT_SHIFT)
+#define CS42L42_OSC_PDNB_STAT_SHIFT 2
+#define CS42L42_OSC_PDNB_STAT_MASK (1 << CS42L42_OSC_SW_SEL_STAT_SHIFT)
+
+#define CS42L42_RSENSE_CTL3 (CS42L42_PAGE_11 + 0x12)
+#define CS42L42_RS_RISE_DBNCE_TIME_SHIFT 0
+#define CS42L42_RS_RISE_DBNCE_TIME_MASK (7 << \
+ CS42L42_RS_RISE_DBNCE_TIME_SHIFT)
+#define CS42L42_RS_FALL_DBNCE_TIME_SHIFT 3
+#define CS42L42_RS_FALL_DBNCE_TIME_MASK (7 << \
+ CS42L42_RS_FALL_DBNCE_TIME_SHIFT)
+#define CS42L42_RS_PU_EN_SHIFT 6
+#define CS42L42_RS_PU_EN_MASK (1 << \
+ CS42L42_RS_PU_EN_SHIFT)
+#define CS42L42_RS_INV_SHIFT 7
+#define CS42L42_RS_INV_MASK (1 << \
+ CS42L42_RS_INV_SHIFT)
+
+#define CS42L42_TSENSE_CTL (CS42L42_PAGE_11 + 0x13)
+#define CS42L42_TS_RISE_DBNCE_TIME_SHIFT 0
+#define CS42L42_TS_RISE_DBNCE_TIME_MASK (7 << \
+ CS42L42_TS_RISE_DBNCE_TIME_SHIFT)
+#define CS42L42_TS_FALL_DBNCE_TIME_SHIFT 3
+#define CS42L42_TS_FALL_DBNCE_TIME_MASK (7 << \
+ CS42L42_TS_FALL_DBNCE_TIME_SHIFT)
+#define CS42L42_TS_INV_SHIFT 7
+#define CS42L42_TS_INV_MASK (1 << \
+ CS42L42_TS_INV_SHIFT)
+
+#define CS42L42_TSRS_INT_DISABLE (CS42L42_PAGE_11 + 0x14)
+#define CS42L42_D_RS_PLUG_DBNC_SHIFT 0
+#define CS42L42_D_RS_PLUG_DBNC_MASK (1 << CS42L42_D_RS_PLUG_DBNC_SHIFT)
+#define CS42L42_D_RS_UNPLUG_DBNC_SHIFT 1
+#define CS42L42_D_RS_UNPLUG_DBNC_MASK (1 << CS42L42_D_RS_UNPLUG_DBNC_SHIFT)
+#define CS42L42_D_TS_PLUG_DBNC_SHIFT 2
+#define CS42L42_D_TS_PLUG_DBNC_MASK (1 << CS42L42_D_TS_PLUG_DBNC_SHIFT)
+#define CS42L42_D_TS_UNPLUG_DBNC_SHIFT 3
+#define CS42L42_D_TS_UNPLUG_DBNC_MASK (1 << CS42L42_D_TS_UNPLUG_DBNC_SHIFT)
+
+#define CS42L42_TRSENSE_STATUS (CS42L42_PAGE_11 + 0x15)
+#define CS42L42_RS_PLUG_DBNC_SHIFT 0
+#define CS42L42_RS_PLUG_DBNC_MASK (1 << CS42L42_RS_PLUG_DBNC_SHIFT)
+#define CS42L42_RS_UNPLUG_DBNC_SHIFT 1
+#define CS42L42_RS_UNPLUG_DBNC_MASK (1 << CS42L42_RS_UNPLUG_DBNC_SHIFT)
+#define CS42L42_TS_PLUG_DBNC_SHIFT 2
+#define CS42L42_TS_PLUG_DBNC_MASK (1 << CS42L42_TS_PLUG_DBNC_SHIFT)
+#define CS42L42_TS_UNPLUG_DBNC_SHIFT 3
+#define CS42L42_TS_UNPLUG_DBNC_MASK (1 << CS42L42_TS_UNPLUG_DBNC_SHIFT)
+
+#define CS42L42_HSDET_CTL1 (CS42L42_PAGE_11 + 0x1F)
+#define CS42L42_HSDET_COMP1_LVL_SHIFT 0
+#define CS42L42_HSDET_COMP1_LVL_MASK (15 << CS42L42_HSDET_COMP1_LVL_SHIFT)
+#define CS42L42_HSDET_COMP2_LVL_SHIFT 4
+#define CS42L42_HSDET_COMP2_LVL_MASK (15 << CS42L42_HSDET_COMP2_LVL_SHIFT)
+
+#define CS42L42_HSDET_CTL2 (CS42L42_PAGE_11 + 0x20)
+#define CS42L42_HSDET_AUTO_TIME_SHIFT 0
+#define CS42L42_HSDET_AUTO_TIME_MASK (3 << CS42L42_HSDET_AUTO_TIME_SHIFT)
+#define CS42L42_HSBIAS_REF_SHIFT 3
+#define CS42L42_HSBIAS_REF_MASK (1 << CS42L42_HSBIAS_REF_SHIFT)
+#define CS42L42_HSDET_SET_SHIFT 4
+#define CS42L42_HSDET_SET_MASK (3 << CS42L42_HSDET_SET_SHIFT)
+#define CS42L42_HSDET_CTRL_SHIFT 6
+#define CS42L42_HSDET_CTRL_MASK (3 << CS42L42_HSDET_CTRL_SHIFT)
+
+#define CS42L42_HS_SWITCH_CTL (CS42L42_PAGE_11 + 0x21)
+#define CS42L42_SW_GNDHS_HS4_SHIFT 0
+#define CS42L42_SW_GNDHS_HS4_MASK (1 << CS42L42_SW_GNDHS_HS4_SHIFT)
+#define CS42L42_SW_GNDHS_HS3_SHIFT 1
+#define CS42L42_SW_GNDHS_HS3_MASK (1 << CS42L42_SW_GNDHS_HS3_SHIFT)
+#define CS42L42_SW_HSB_HS4_SHIFT 2
+#define CS42L42_SW_HSB_HS4_MASK (1 << CS42L42_SW_HSB_HS4_SHIFT)
+#define CS42L42_SW_HSB_HS3_SHIFT 3
+#define CS42L42_SW_HSB_HS3_MASK (1 << CS42L42_SW_HSB_HS3_SHIFT)
+#define CS42L42_SW_HSB_FILT_HS4_SHIFT 4
+#define CS42L42_SW_HSB_FILT_HS4_MASK (1 << CS42L42_SW_HSB_FILT_HS4_SHIFT)
+#define CS42L42_SW_HSB_FILT_HS3_SHIFT 5
+#define CS42L42_SW_HSB_FILT_HS3_MASK (1 << CS42L42_SW_HSB_FILT_HS3_SHIFT)
+#define CS42L42_SW_REF_HS4_SHIFT 6
+#define CS42L42_SW_REF_HS4_MASK (1 << CS42L42_SW_REF_HS4_SHIFT)
+#define CS42L42_SW_REF_HS3_SHIFT 7
+#define CS42L42_SW_REF_HS3_MASK (1 << CS42L42_SW_REF_HS3_SHIFT)
+
+#define CS42L42_HS_DET_STATUS (CS42L42_PAGE_11 + 0x24)
+#define CS42L42_HSDET_TYPE_SHIFT 0
+#define CS42L42_HSDET_TYPE_MASK (3 << CS42L42_HSDET_TYPE_SHIFT)
+#define CS42L42_HSDET_COMP1_OUT_SHIFT 6
+#define CS42L42_HSDET_COMP1_OUT_MASK (1 << CS42L42_HSDET_COMP1_OUT_SHIFT)
+#define CS42L42_HSDET_COMP2_OUT_SHIFT 7
+#define CS42L42_HSDET_COMP2_OUT_MASK (1 << CS42L42_HSDET_COMP2_OUT_SHIFT)
+#define CS42L42_PLUG_CTIA 0
+#define CS42L42_PLUG_OMTP 1
+#define CS42L42_PLUG_HEADPHONE 2
+#define CS42L42_PLUG_INVALID 3
+
+#define CS42L42_HS_CLAMP_DISABLE (CS42L42_PAGE_11 + 0x29)
+#define CS42L42_HS_CLAMP_DISABLE_SHIFT 0
+#define CS42L42_HS_CLAMP_DISABLE_MASK (1 << CS42L42_HS_CLAMP_DISABLE_SHIFT)
+
+/* Page 0x12 Clocking Registers */
+#define CS42L42_MCLK_SRC_SEL (CS42L42_PAGE_12 + 0x01)
+#define CS42L42_MCLKDIV_SHIFT 1
+#define CS42L42_MCLKDIV_MASK (1 << CS42L42_MCLKDIV_SHIFT)
+#define CS42L42_MCLK_SRC_SEL_SHIFT 0
+#define CS42L42_MCLK_SRC_SEL_MASK (1 << CS42L42_MCLK_SRC_SEL_SHIFT)
+
+#define CS42L42_SPDIF_CLK_CFG (CS42L42_PAGE_12 + 0x02)
+#define CS42L42_FSYNC_PW_LOWER (CS42L42_PAGE_12 + 0x03)
+
+#define CS42L42_FSYNC_PW_UPPER (CS42L42_PAGE_12 + 0x04)
+#define CS42L42_FSYNC_PULSE_WIDTH_SHIFT 0
+#define CS42L42_FSYNC_PULSE_WIDTH_MASK (0xff << \
+ CS42L42_FSYNC_PULSE_WIDTH_SHIFT)
+
+#define CS42L42_FSYNC_P_LOWER (CS42L42_PAGE_12 + 0x05)
+
+#define CS42L42_FSYNC_P_UPPER (CS42L42_PAGE_12 + 0x06)
+#define CS42L42_FSYNC_PERIOD_SHIFT 0
+#define CS42L42_FSYNC_PERIOD_MASK (0xff << CS42L42_FSYNC_PERIOD_SHIFT)
+
+#define CS42L42_ASP_CLK_CFG (CS42L42_PAGE_12 + 0x07)
+#define CS42L42_ASP_SCLK_EN_SHIFT 5
+#define CS42L42_ASP_SCLK_EN_MASK (1 << CS42L42_ASP_SCLK_EN_SHIFT)
+#define CS42L42_ASP_MASTER_MODE 0x01
+#define CS42L42_ASP_SLAVE_MODE 0x00
+#define CS42L42_ASP_MODE_SHIFT 4
+#define CS42L42_ASP_MODE_MASK (1 << CS42L42_ASP_MODE_SHIFT)
+#define CS42L42_ASP_SCPOL_IN_DAC_SHIFT 2
+#define CS42L42_ASP_SCPOL_IN_DAC_MASK (1 << CS42L42_ASP_SCPOL_IN_DAC_SHIFT)
+#define CS42L42_ASP_LCPOL_IN_SHIFT 0
+#define CS42L42_ASP_LCPOL_IN_MASK (1 << CS42L42_ASP_LCPOL_IN_SHIFT)
+#define CS42L42_ASP_POL_INV 1
+
+#define CS42L42_ASP_FRM_CFG (CS42L42_PAGE_12 + 0x08)
+#define CS42L42_ASP_STP_SHIFT 4
+#define CS42L42_ASP_STP_MASK (1 << CS42L42_ASP_STP_SHIFT)
+#define CS42L42_ASP_5050_SHIFT 3
+#define CS42L42_ASP_5050_MASK (1 << CS42L42_ASP_5050_SHIFT)
+#define CS42L42_ASP_FSD_SHIFT 0
+#define CS42L42_ASP_FSD_MASK (7 << CS42L42_ASP_FSD_SHIFT)
+#define CS42L42_ASP_FSD_0_5 1
+#define CS42L42_ASP_FSD_1_0 2
+#define CS42L42_ASP_FSD_1_5 3
+#define CS42L42_ASP_FSD_2_0 4
+
+#define CS42L42_FS_RATE_EN (CS42L42_PAGE_12 + 0x09)
+#define CS42L42_FS_EN_SHIFT 0
+#define CS42L42_FS_EN_MASK (0xf << CS42L42_FS_EN_SHIFT)
+#define CS42L42_FS_EN_IASRC_96K 0x1
+#define CS42L42_FS_EN_OASRC_96K 0x2
+
+#define CS42L42_IN_ASRC_CLK (CS42L42_PAGE_12 + 0x0A)
+#define CS42L42_CLK_IASRC_SEL_SHIFT 0
+#define CS42L42_CLK_IASRC_SEL_MASK (1 << CS42L42_CLK_IASRC_SEL_SHIFT)
+#define CS42L42_CLK_IASRC_SEL_12 1
+
+#define CS42L42_OUT_ASRC_CLK (CS42L42_PAGE_12 + 0x0B)
+#define CS42L42_CLK_OASRC_SEL_SHIFT 0
+#define CS42L42_CLK_OASRC_SEL_MASK (1 << CS42L42_CLK_OASRC_SEL_SHIFT)
+#define CS42L42_CLK_OASRC_SEL_12 1
+
+#define CS42L42_PLL_DIV_CFG1 (CS42L42_PAGE_12 + 0x0C)
+#define CS42L42_SCLK_PREDIV_SHIFT 0
+#define CS42L42_SCLK_PREDIV_MASK (3 << CS42L42_SCLK_PREDIV_SHIFT)
+
+/* Page 0x13 Interrupt Registers */
+/* Interrupts */
+#define CS42L42_ADC_OVFL_STATUS (CS42L42_PAGE_13 + 0x01)
+#define CS42L42_MIXER_STATUS (CS42L42_PAGE_13 + 0x02)
+#define CS42L42_SRC_STATUS (CS42L42_PAGE_13 + 0x03)
+#define CS42L42_ASP_RX_STATUS (CS42L42_PAGE_13 + 0x04)
+#define CS42L42_ASP_TX_STATUS (CS42L42_PAGE_13 + 0x05)
+#define CS42L42_CODEC_STATUS (CS42L42_PAGE_13 + 0x08)
+#define CS42L42_DET_INT_STATUS1 (CS42L42_PAGE_13 + 0x09)
+#define CS42L42_DET_INT_STATUS2 (CS42L42_PAGE_13 + 0x0A)
+#define CS42L42_SRCPL_INT_STATUS (CS42L42_PAGE_13 + 0x0B)
+#define CS42L42_VPMON_STATUS (CS42L42_PAGE_13 + 0x0D)
+#define CS42L42_PLL_LOCK_STATUS (CS42L42_PAGE_13 + 0x0E)
+#define CS42L42_TSRS_PLUG_STATUS (CS42L42_PAGE_13 + 0x0F)
+/* Masks */
+#define CS42L42_ADC_OVFL_INT_MASK (CS42L42_PAGE_13 + 0x16)
+#define CS42L42_ADC_OVFL_SHIFT 0
+#define CS42L42_ADC_OVFL_MASK (1 << CS42L42_ADC_OVFL_SHIFT)
+#define CS42L42_ADC_OVFL_VAL_MASK CS42L42_ADC_OVFL_MASK
+
+#define CS42L42_MIXER_INT_MASK (CS42L42_PAGE_13 + 0x17)
+#define CS42L42_MIX_CHB_OVFL_SHIFT 0
+#define CS42L42_MIX_CHB_OVFL_MASK (1 << CS42L42_MIX_CHB_OVFL_SHIFT)
+#define CS42L42_MIX_CHA_OVFL_SHIFT 1
+#define CS42L42_MIX_CHA_OVFL_MASK (1 << CS42L42_MIX_CHA_OVFL_SHIFT)
+#define CS42L42_EQ_OVFL_SHIFT 2
+#define CS42L42_EQ_OVFL_MASK (1 << CS42L42_EQ_OVFL_SHIFT)
+#define CS42L42_EQ_BIQUAD_OVFL_SHIFT 3
+#define CS42L42_EQ_BIQUAD_OVFL_MASK (1 << CS42L42_EQ_BIQUAD_OVFL_SHIFT)
+#define CS42L42_MIXER_VAL_MASK (CS42L42_MIX_CHB_OVFL_MASK | \
+ CS42L42_MIX_CHA_OVFL_MASK | \
+ CS42L42_EQ_OVFL_MASK | \
+ CS42L42_EQ_BIQUAD_OVFL_MASK)
+
+#define CS42L42_SRC_INT_MASK (CS42L42_PAGE_13 + 0x18)
+#define CS42L42_SRC_ILK_SHIFT 0
+#define CS42L42_SRC_ILK_MASK (1 << CS42L42_SRC_ILK_SHIFT)
+#define CS42L42_SRC_OLK_SHIFT 1
+#define CS42L42_SRC_OLK_MASK (1 << CS42L42_SRC_OLK_SHIFT)
+#define CS42L42_SRC_IUNLK_SHIFT 2
+#define CS42L42_SRC_IUNLK_MASK (1 << CS42L42_SRC_IUNLK_SHIFT)
+#define CS42L42_SRC_OUNLK_SHIFT 3
+#define CS42L42_SRC_OUNLK_MASK (1 << CS42L42_SRC_OUNLK_SHIFT)
+#define CS42L42_SRC_VAL_MASK (CS42L42_SRC_ILK_MASK | \
+ CS42L42_SRC_OLK_MASK | \
+ CS42L42_SRC_IUNLK_MASK | \
+ CS42L42_SRC_OUNLK_MASK)
+
+#define CS42L42_ASP_RX_INT_MASK (CS42L42_PAGE_13 + 0x19)
+#define CS42L42_ASPRX_NOLRCK_SHIFT 0
+#define CS42L42_ASPRX_NOLRCK_MASK (1 << CS42L42_ASPRX_NOLRCK_SHIFT)
+#define CS42L42_ASPRX_EARLY_SHIFT 1
+#define CS42L42_ASPRX_EARLY_MASK (1 << CS42L42_ASPRX_EARLY_SHIFT)
+#define CS42L42_ASPRX_LATE_SHIFT 2
+#define CS42L42_ASPRX_LATE_MASK (1 << CS42L42_ASPRX_LATE_SHIFT)
+#define CS42L42_ASPRX_ERROR_SHIFT 3
+#define CS42L42_ASPRX_ERROR_MASK (1 << CS42L42_ASPRX_ERROR_SHIFT)
+#define CS42L42_ASPRX_OVLD_SHIFT 4
+#define CS42L42_ASPRX_OVLD_MASK (1 << CS42L42_ASPRX_OVLD_SHIFT)
+#define CS42L42_ASP_RX_VAL_MASK (CS42L42_ASPRX_NOLRCK_MASK | \
+ CS42L42_ASPRX_EARLY_MASK | \
+ CS42L42_ASPRX_LATE_MASK | \
+ CS42L42_ASPRX_ERROR_MASK | \
+ CS42L42_ASPRX_OVLD_MASK)
+
+#define CS42L42_ASP_TX_INT_MASK (CS42L42_PAGE_13 + 0x1A)
+#define CS42L42_ASPTX_NOLRCK_SHIFT 0
+#define CS42L42_ASPTX_NOLRCK_MASK (1 << CS42L42_ASPTX_NOLRCK_SHIFT)
+#define CS42L42_ASPTX_EARLY_SHIFT 1
+#define CS42L42_ASPTX_EARLY_MASK (1 << CS42L42_ASPTX_EARLY_SHIFT)
+#define CS42L42_ASPTX_LATE_SHIFT 2
+#define CS42L42_ASPTX_LATE_MASK (1 << CS42L42_ASPTX_LATE_SHIFT)
+#define CS42L42_ASPTX_SMERROR_SHIFT 3
+#define CS42L42_ASPTX_SMERROR_MASK (1 << CS42L42_ASPTX_SMERROR_SHIFT)
+#define CS42L42_ASP_TX_VAL_MASK (CS42L42_ASPTX_NOLRCK_MASK | \
+ CS42L42_ASPTX_EARLY_MASK | \
+ CS42L42_ASPTX_LATE_MASK | \
+ CS42L42_ASPTX_SMERROR_MASK)
+
+#define CS42L42_CODEC_INT_MASK (CS42L42_PAGE_13 + 0x1B)
+#define CS42L42_PDN_DONE_SHIFT 0
+#define CS42L42_PDN_DONE_MASK (1 << CS42L42_PDN_DONE_SHIFT)
+#define CS42L42_HSDET_AUTO_DONE_SHIFT 1
+#define CS42L42_HSDET_AUTO_DONE_MASK (1 << CS42L42_HSDET_AUTO_DONE_SHIFT)
+#define CS42L42_CODEC_VAL_MASK (CS42L42_PDN_DONE_MASK | \
+ CS42L42_HSDET_AUTO_DONE_MASK)
+
+#define CS42L42_SRCPL_INT_MASK (CS42L42_PAGE_13 + 0x1C)
+#define CS42L42_SRCPL_ADC_LK_SHIFT 0
+#define CS42L42_SRCPL_ADC_LK_MASK (1 << CS42L42_SRCPL_ADC_LK_SHIFT)
+#define CS42L42_SRCPL_DAC_LK_SHIFT 2
+#define CS42L42_SRCPL_DAC_LK_MASK (1 << CS42L42_SRCPL_DAC_LK_SHIFT)
+#define CS42L42_SRCPL_ADC_UNLK_SHIFT 5
+#define CS42L42_SRCPL_ADC_UNLK_MASK (1 << CS42L42_SRCPL_ADC_UNLK_SHIFT)
+#define CS42L42_SRCPL_DAC_UNLK_SHIFT 6
+#define CS42L42_SRCPL_DAC_UNLK_MASK (1 << CS42L42_SRCPL_DAC_UNLK_SHIFT)
+#define CS42L42_SRCPL_VAL_MASK (CS42L42_SRCPL_ADC_LK_MASK | \
+ CS42L42_SRCPL_DAC_LK_MASK | \
+ CS42L42_SRCPL_ADC_UNLK_MASK | \
+ CS42L42_SRCPL_DAC_UNLK_MASK)
+
+#define CS42L42_VPMON_INT_MASK (CS42L42_PAGE_13 + 0x1E)
+#define CS42L42_VPMON_SHIFT 0
+#define CS42L42_VPMON_MASK (1 << CS42L42_VPMON_SHIFT)
+#define CS42L42_VPMON_VAL_MASK CS42L42_VPMON_MASK
+
+#define CS42L42_PLL_LOCK_INT_MASK (CS42L42_PAGE_13 + 0x1F)
+#define CS42L42_PLL_LOCK_SHIFT 0
+#define CS42L42_PLL_LOCK_MASK (1 << CS42L42_PLL_LOCK_SHIFT)
+#define CS42L42_PLL_LOCK_VAL_MASK CS42L42_PLL_LOCK_MASK
+
+#define CS42L42_TSRS_PLUG_INT_MASK (CS42L42_PAGE_13 + 0x20)
+#define CS42L42_RS_PLUG_SHIFT 0
+#define CS42L42_RS_PLUG_MASK (1 << CS42L42_RS_PLUG_SHIFT)
+#define CS42L42_RS_UNPLUG_SHIFT 1
+#define CS42L42_RS_UNPLUG_MASK (1 << CS42L42_RS_UNPLUG_SHIFT)
+#define CS42L42_TS_PLUG_SHIFT 2
+#define CS42L42_TS_PLUG_MASK (1 << CS42L42_TS_PLUG_SHIFT)
+#define CS42L42_TS_UNPLUG_SHIFT 3
+#define CS42L42_TS_UNPLUG_MASK (1 << CS42L42_TS_UNPLUG_SHIFT)
+#define CS42L42_TSRS_PLUG_VAL_MASK (CS42L42_RS_PLUG_MASK | \
+ CS42L42_RS_UNPLUG_MASK | \
+ CS42L42_TS_PLUG_MASK | \
+ CS42L42_TS_UNPLUG_MASK)
+#define CS42L42_TS_PLUG 3
+#define CS42L42_TS_UNPLUG 0
+#define CS42L42_TS_TRANS 1
+
+/* Page 0x15 Fractional-N PLL Registers */
+#define CS42L42_PLL_CTL1 (CS42L42_PAGE_15 + 0x01)
+#define CS42L42_PLL_START_SHIFT 0
+#define CS42L42_PLL_START_MASK (1 << CS42L42_PLL_START_SHIFT)
+
+#define CS42L42_PLL_DIV_FRAC0 (CS42L42_PAGE_15 + 0x02)
+#define CS42L42_PLL_DIV_FRAC_SHIFT 0
+#define CS42L42_PLL_DIV_FRAC_MASK (0xff << CS42L42_PLL_DIV_FRAC_SHIFT)
+
+#define CS42L42_PLL_DIV_FRAC1 (CS42L42_PAGE_15 + 0x03)
+#define CS42L42_PLL_DIV_FRAC2 (CS42L42_PAGE_15 + 0x04)
+
+#define CS42L42_PLL_DIV_INT (CS42L42_PAGE_15 + 0x05)
+#define CS42L42_PLL_DIV_INT_SHIFT 0
+#define CS42L42_PLL_DIV_INT_MASK (0xff << CS42L42_PLL_DIV_INT_SHIFT)
+
+#define CS42L42_PLL_CTL3 (CS42L42_PAGE_15 + 0x08)
+#define CS42L42_PLL_DIVOUT_SHIFT 0
+#define CS42L42_PLL_DIVOUT_MASK (0xff << CS42L42_PLL_DIVOUT_SHIFT)
+
+#define CS42L42_PLL_CAL_RATIO (CS42L42_PAGE_15 + 0x0A)
+#define CS42L42_PLL_CAL_RATIO_SHIFT 0
+#define CS42L42_PLL_CAL_RATIO_MASK (0xff << CS42L42_PLL_CAL_RATIO_SHIFT)
+
+#define CS42L42_PLL_CTL4 (CS42L42_PAGE_15 + 0x1B)
+#define CS42L42_PLL_MODE_SHIFT 0
+#define CS42L42_PLL_MODE_MASK (3 << CS42L42_PLL_MODE_SHIFT)
+
+/* Page 0x19 HP Load Detect Registers */
+#define CS42L42_LOAD_DET_RCSTAT (CS42L42_PAGE_19 + 0x25)
+#define CS42L42_RLA_STAT_SHIFT 0
+#define CS42L42_RLA_STAT_MASK (3 << CS42L42_RLA_STAT_SHIFT)
+#define CS42L42_RLA_STAT_15_OHM 0
+
+#define CS42L42_LOAD_DET_DONE (CS42L42_PAGE_19 + 0x26)
+#define CS42L42_HPLOAD_DET_DONE_SHIFT 0
+#define CS42L42_HPLOAD_DET_DONE_MASK (1 << CS42L42_HPLOAD_DET_DONE_SHIFT)
+
+#define CS42L42_LOAD_DET_EN (CS42L42_PAGE_19 + 0x27)
+#define CS42L42_HP_LD_EN_SHIFT 0
+#define CS42L42_HP_LD_EN_MASK (1 << CS42L42_HP_LD_EN_SHIFT)
+
+/* Page 0x1B Headset Interface Registers */
+#define CS42L42_HSBIAS_SC_AUTOCTL (CS42L42_PAGE_1B + 0x70)
+#define CS42L42_HSBIAS_SENSE_TRIP_SHIFT 0
+#define CS42L42_HSBIAS_SENSE_TRIP_MASK (7 << \
+ CS42L42_HSBIAS_SENSE_TRIP_SHIFT)
+#define CS42L42_TIP_SENSE_EN_SHIFT 5
+#define CS42L42_TIP_SENSE_EN_MASK (1 << \
+ CS42L42_TIP_SENSE_EN_SHIFT)
+#define CS42L42_AUTO_HSBIAS_HIZ_SHIFT 6
+#define CS42L42_AUTO_HSBIAS_HIZ_MASK (1 << \
+ CS42L42_AUTO_HSBIAS_HIZ_SHIFT)
+#define CS42L42_HSBIAS_SENSE_EN_SHIFT 7
+#define CS42L42_HSBIAS_SENSE_EN_MASK (1 << \
+ CS42L42_HSBIAS_SENSE_EN_SHIFT)
+
+#define CS42L42_WAKE_CTL (CS42L42_PAGE_1B + 0x71)
+#define CS42L42_WAKEB_CLEAR_SHIFT 0
+#define CS42L42_WAKEB_CLEAR_MASK (1 << CS42L42_WAKEB_CLEAR_SHIFT)
+#define CS42L42_WAKEB_MODE_SHIFT 5
+#define CS42L42_WAKEB_MODE_MASK (1 << CS42L42_WAKEB_MODE_SHIFT)
+#define CS42L42_M_HP_WAKE_SHIFT 6
+#define CS42L42_M_HP_WAKE_MASK (1 << CS42L42_M_HP_WAKE_SHIFT)
+#define CS42L42_M_MIC_WAKE_SHIFT 7
+#define CS42L42_M_MIC_WAKE_MASK (1 << CS42L42_M_MIC_WAKE_SHIFT)
+
+#define CS42L42_ADC_DISABLE_MUTE (CS42L42_PAGE_1B + 0x72)
+#define CS42L42_ADC_DISABLE_S0_MUTE_SHIFT 7
+#define CS42L42_ADC_DISABLE_S0_MUTE_MASK (1 << \
+ CS42L42_ADC_DISABLE_S0_MUTE_SHIFT)
+
+#define CS42L42_TIPSENSE_CTL (CS42L42_PAGE_1B + 0x73)
+#define CS42L42_TIP_SENSE_DEBOUNCE_SHIFT 0
+#define CS42L42_TIP_SENSE_DEBOUNCE_MASK (3 << \
+ CS42L42_TIP_SENSE_DEBOUNCE_SHIFT)
+#define CS42L42_TIP_SENSE_INV_SHIFT 5
+#define CS42L42_TIP_SENSE_INV_MASK (1 << \
+ CS42L42_TIP_SENSE_INV_SHIFT)
+#define CS42L42_TIP_SENSE_CTRL_SHIFT 6
+#define CS42L42_TIP_SENSE_CTRL_MASK (3 << \
+ CS42L42_TIP_SENSE_CTRL_SHIFT)
+
+#define CS42L42_MISC_DET_CTL (CS42L42_PAGE_1B + 0x74)
+#define CS42L42_PDN_MIC_LVL_DET_SHIFT 0
+#define CS42L42_PDN_MIC_LVL_DET_MASK (1 << CS42L42_PDN_MIC_LVL_DET_SHIFT)
+#define CS42L42_HSBIAS_CTL_SHIFT 1
+#define CS42L42_HSBIAS_CTL_MASK (3 << CS42L42_HSBIAS_CTL_SHIFT)
+#define CS42L42_DETECT_MODE_SHIFT 3
+#define CS42L42_DETECT_MODE_MASK (3 << CS42L42_DETECT_MODE_SHIFT)
+
+#define CS42L42_MIC_DET_CTL1 (CS42L42_PAGE_1B + 0x75)
+#define CS42L42_HS_DET_LEVEL_SHIFT 0
+#define CS42L42_HS_DET_LEVEL_MASK (0x3F << CS42L42_HS_DET_LEVEL_SHIFT)
+#define CS42L42_EVENT_STAT_SEL_SHIFT 6
+#define CS42L42_EVENT_STAT_SEL_MASK (1 << CS42L42_EVENT_STAT_SEL_SHIFT)
+#define CS42L42_LATCH_TO_VP_SHIFT 7
+#define CS42L42_LATCH_TO_VP_MASK (1 << CS42L42_LATCH_TO_VP_SHIFT)
+
+#define CS42L42_MIC_DET_CTL2 (CS42L42_PAGE_1B + 0x76)
+#define CS42L42_DEBOUNCE_TIME_SHIFT 5
+#define CS42L42_DEBOUNCE_TIME_MASK (0x07 << CS42L42_DEBOUNCE_TIME_SHIFT)
+
+#define CS42L42_DET_STATUS1 (CS42L42_PAGE_1B + 0x77)
+#define CS42L42_HSBIAS_HIZ_MODE_SHIFT 6
+#define CS42L42_HSBIAS_HIZ_MODE_MASK (1 << CS42L42_HSBIAS_HIZ_MODE_SHIFT)
+#define CS42L42_TIP_SENSE_SHIFT 7
+#define CS42L42_TIP_SENSE_MASK (1 << CS42L42_TIP_SENSE_SHIFT)
+
+#define CS42L42_DET_STATUS2 (CS42L42_PAGE_1B + 0x78)
+#define CS42L42_SHORT_TRUE_SHIFT 0
+#define CS42L42_SHORT_TRUE_MASK (1 << CS42L42_SHORT_TRUE_SHIFT)
+#define CS42L42_HS_TRUE_SHIFT 1
+#define CS42L42_HS_TRUE_MASK (1 << CS42L42_HS_TRUE_SHIFT)
+
+#define CS42L42_DET_INT1_MASK (CS42L42_PAGE_1B + 0x79)
+#define CS42L42_TIP_SENSE_UNPLUG_SHIFT 5
+#define CS42L42_TIP_SENSE_UNPLUG_MASK (1 << CS42L42_TIP_SENSE_UNPLUG_SHIFT)
+#define CS42L42_TIP_SENSE_PLUG_SHIFT 6
+#define CS42L42_TIP_SENSE_PLUG_MASK (1 << CS42L42_TIP_SENSE_PLUG_SHIFT)
+#define CS42L42_HSBIAS_SENSE_SHIFT 7
+#define CS42L42_HSBIAS_SENSE_MASK (1 << CS42L42_HSBIAS_SENSE_SHIFT)
+#define CS42L42_DET_INT_VAL1_MASK (CS42L42_TIP_SENSE_UNPLUG_MASK | \
+ CS42L42_TIP_SENSE_PLUG_MASK | \
+ CS42L42_HSBIAS_SENSE_MASK)
+
+#define CS42L42_DET_INT2_MASK (CS42L42_PAGE_1B + 0x7A)
+#define CS42L42_M_SHORT_DET_SHIFT 0
+#define CS42L42_M_SHORT_DET_MASK (1 << \
+ CS42L42_M_SHORT_DET_SHIFT)
+#define CS42L42_M_SHORT_RLS_SHIFT 1
+#define CS42L42_M_SHORT_RLS_MASK (1 << \
+ CS42L42_M_SHORT_RLS_SHIFT)
+#define CS42L42_M_HSBIAS_HIZ_SHIFT 2
+#define CS42L42_M_HSBIAS_HIZ_MASK (1 << \
+ CS42L42_M_HSBIAS_HIZ_SHIFT)
+#define CS42L42_M_DETECT_FT_SHIFT 6
+#define CS42L42_M_DETECT_FT_MASK (1 << \
+ CS42L42_M_DETECT_FT_SHIFT)
+#define CS42L42_M_DETECT_TF_SHIFT 7
+#define CS42L42_M_DETECT_TF_MASK (1 << \
+ CS42L42_M_DETECT_TF_SHIFT)
+#define CS42L42_DET_INT_VAL2_MASK (CS42L42_M_SHORT_DET_MASK | \
+ CS42L42_M_SHORT_RLS_MASK | \
+ CS42L42_M_HSBIAS_HIZ_MASK | \
+ CS42L42_M_DETECT_FT_MASK | \
+ CS42L42_M_DETECT_TF_MASK)
+
+/* Page 0x1C Headset Bias Registers */
+#define CS42L42_HS_BIAS_CTL (CS42L42_PAGE_1C + 0x03)
+#define CS42L42_HSBIAS_RAMP_SHIFT 0
+#define CS42L42_HSBIAS_RAMP_MASK (3 << CS42L42_HSBIAS_RAMP_SHIFT)
+#define CS42L42_HSBIAS_PD_SHIFT 4
+#define CS42L42_HSBIAS_PD_MASK (1 << CS42L42_HSBIAS_PD_SHIFT)
+#define CS42L42_HSBIAS_CAPLESS_SHIFT 7
+#define CS42L42_HSBIAS_CAPLESS_MASK (1 << CS42L42_HSBIAS_CAPLESS_SHIFT)
+
+/* Page 0x1D ADC Registers */
+#define CS42L42_ADC_CTL (CS42L42_PAGE_1D + 0x01)
+#define CS42L42_ADC_NOTCH_DIS_SHIFT 5
+#define CS42L42_ADC_FORCE_WEAK_VCM_SHIFT 4
+#define CS42L42_ADC_INV_SHIFT 2
+#define CS42L42_ADC_DIG_BOOST_SHIFT 0
+
+#define CS42L42_ADC_VOLUME (CS42L42_PAGE_1D + 0x03)
+#define CS42L42_ADC_VOL_SHIFT 0
+
+#define CS42L42_ADC_WNF_HPF_CTL (CS42L42_PAGE_1D + 0x04)
+#define CS42L42_ADC_WNF_CF_SHIFT 4
+#define CS42L42_ADC_WNF_EN_SHIFT 3
+#define CS42L42_ADC_HPF_CF_SHIFT 1
+#define CS42L42_ADC_HPF_EN_SHIFT 0
+
+/* Page 0x1F DAC Registers */
+#define CS42L42_DAC_CTL1 (CS42L42_PAGE_1F + 0x01)
+#define CS42L42_DACB_INV_SHIFT 1
+#define CS42L42_DACA_INV_SHIFT 0
+
+#define CS42L42_DAC_CTL2 (CS42L42_PAGE_1F + 0x06)
+#define CS42L42_HPOUT_PULLDOWN_SHIFT 4
+#define CS42L42_HPOUT_PULLDOWN_MASK (15 << CS42L42_HPOUT_PULLDOWN_SHIFT)
+#define CS42L42_HPOUT_LOAD_SHIFT 3
+#define CS42L42_HPOUT_LOAD_MASK (1 << CS42L42_HPOUT_LOAD_SHIFT)
+#define CS42L42_HPOUT_CLAMP_SHIFT 2
+#define CS42L42_HPOUT_CLAMP_MASK (1 << CS42L42_HPOUT_CLAMP_SHIFT)
+#define CS42L42_DAC_HPF_EN_SHIFT 1
+#define CS42L42_DAC_HPF_EN_MASK (1 << CS42L42_DAC_HPF_EN_SHIFT)
+#define CS42L42_DAC_MON_EN_SHIFT 0
+#define CS42L42_DAC_MON_EN_MASK (1 << CS42L42_DAC_MON_EN_SHIFT)
+
+/* Page 0x20 HP CTL Registers */
+#define CS42L42_HP_CTL (CS42L42_PAGE_20 + 0x01)
+#define CS42L42_HP_ANA_BMUTE_SHIFT 3
+#define CS42L42_HP_ANA_BMUTE_MASK (1 << CS42L42_HP_ANA_BMUTE_SHIFT)
+#define CS42L42_HP_ANA_AMUTE_SHIFT 2
+#define CS42L42_HP_ANA_AMUTE_MASK (1 << CS42L42_HP_ANA_AMUTE_SHIFT)
+#define CS42L42_HP_FULL_SCALE_VOL_SHIFT 1
+#define CS42L42_HP_FULL_SCALE_VOL_MASK (1 << CS42L42_HP_FULL_SCALE_VOL_SHIFT)
+
+/* Page 0x21 Class H Registers */
+#define CS42L42_CLASSH_CTL (CS42L42_PAGE_21 + 0x01)
+
+/* Page 0x23 Mixer Volume Registers */
+#define CS42L42_MIXER_CHA_VOL (CS42L42_PAGE_23 + 0x01)
+#define CS42L42_MIXER_ADC_VOL (CS42L42_PAGE_23 + 0x02)
+
+#define CS42L42_MIXER_CHB_VOL (CS42L42_PAGE_23 + 0x03)
+#define CS42L42_MIXER_CH_VOL_SHIFT 0
+#define CS42L42_MIXER_CH_VOL_MASK (0x3f << CS42L42_MIXER_CH_VOL_SHIFT)
+
+/* Page 0x24 EQ Registers */
+#define CS42L42_EQ_COEF_IN0 (CS42L42_PAGE_24 + 0x01)
+#define CS42L42_EQ_COEF_IN1 (CS42L42_PAGE_24 + 0x02)
+#define CS42L42_EQ_COEF_IN2 (CS42L42_PAGE_24 + 0x03)
+#define CS42L42_EQ_COEF_IN3 (CS42L42_PAGE_24 + 0x04)
+#define CS42L42_EQ_COEF_RW (CS42L42_PAGE_24 + 0x06)
+#define CS42L42_EQ_COEF_OUT0 (CS42L42_PAGE_24 + 0x07)
+#define CS42L42_EQ_COEF_OUT1 (CS42L42_PAGE_24 + 0x08)
+#define CS42L42_EQ_COEF_OUT2 (CS42L42_PAGE_24 + 0x09)
+#define CS42L42_EQ_COEF_OUT3 (CS42L42_PAGE_24 + 0x0A)
+#define CS42L42_EQ_INIT_STAT (CS42L42_PAGE_24 + 0x0B)
+#define CS42L42_EQ_START_FILT (CS42L42_PAGE_24 + 0x0C)
+#define CS42L42_EQ_MUTE_CTL (CS42L42_PAGE_24 + 0x0E)
+
+/* Page 0x25 Audio Port Registers */
+#define CS42L42_SP_RX_CH_SEL (CS42L42_PAGE_25 + 0x01)
+
+#define CS42L42_SP_RX_ISOC_CTL (CS42L42_PAGE_25 + 0x02)
+#define CS42L42_SP_RX_RSYNC_SHIFT 6
+#define CS42L42_SP_RX_RSYNC_MASK (1 << CS42L42_SP_RX_RSYNC_SHIFT)
+#define CS42L42_SP_RX_NSB_POS_SHIFT 3
+#define CS42L42_SP_RX_NSB_POS_MASK (7 << CS42L42_SP_RX_NSB_POS_SHIFT)
+#define CS42L42_SP_RX_NFS_NSBB_SHIFT 2
+#define CS42L42_SP_RX_NFS_NSBB_MASK (1 << CS42L42_SP_RX_NFS_NSBB_SHIFT)
+#define CS42L42_SP_RX_ISOC_MODE_SHIFT 0
+#define CS42L42_SP_RX_ISOC_MODE_MASK (3 << CS42L42_SP_RX_ISOC_MODE_SHIFT)
+
+#define CS42L42_SP_RX_FS (CS42L42_PAGE_25 + 0x03)
+#define CS42l42_SPDIF_CH_SEL (CS42L42_PAGE_25 + 0x04)
+#define CS42L42_SP_TX_ISOC_CTL (CS42L42_PAGE_25 + 0x05)
+#define CS42L42_SP_TX_FS (CS42L42_PAGE_25 + 0x06)
+#define CS42L42_SPDIF_SW_CTL1 (CS42L42_PAGE_25 + 0x07)
+
+/* Page 0x26 SRC Registers */
+#define CS42L42_SRC_SDIN_FS (CS42L42_PAGE_26 + 0x01)
+#define CS42L42_SRC_SDIN_FS_SHIFT 0
+#define CS42L42_SRC_SDIN_FS_MASK (0x1f << CS42L42_SRC_SDIN_FS_SHIFT)
+
+#define CS42L42_SRC_SDOUT_FS (CS42L42_PAGE_26 + 0x09)
+
+/* Page 0x28 S/PDIF Registers */
+#define CS42L42_SPDIF_CTL1 (CS42L42_PAGE_28 + 0x01)
+#define CS42L42_SPDIF_CTL2 (CS42L42_PAGE_28 + 0x02)
+#define CS42L42_SPDIF_CTL3 (CS42L42_PAGE_28 + 0x03)
+#define CS42L42_SPDIF_CTL4 (CS42L42_PAGE_28 + 0x04)
+
+/* Page 0x29 Serial Port TX Registers */
+#define CS42L42_ASP_TX_SZ_EN (CS42L42_PAGE_29 + 0x01)
+#define CS42L42_ASP_TX_CH_EN (CS42L42_PAGE_29 + 0x02)
+#define CS42L42_ASP_TX_CH_AP_RES (CS42L42_PAGE_29 + 0x03)
+#define CS42L42_ASP_TX_CH1_BIT_MSB (CS42L42_PAGE_29 + 0x04)
+#define CS42L42_ASP_TX_CH1_BIT_LSB (CS42L42_PAGE_29 + 0x05)
+#define CS42L42_ASP_TX_HIZ_DLY_CFG (CS42L42_PAGE_29 + 0x06)
+#define CS42L42_ASP_TX_CH2_BIT_MSB (CS42L42_PAGE_29 + 0x0A)
+#define CS42L42_ASP_TX_CH2_BIT_LSB (CS42L42_PAGE_29 + 0x0B)
+
+/* Page 0x2A Serial Port RX Registers */
+#define CS42L42_ASP_RX_DAI0_EN (CS42L42_PAGE_2A + 0x01)
+#define CS42L42_ASP_RX0_CH_EN_SHIFT 2
+#define CS42L42_ASP_RX0_CH_EN_MASK (0xf << CS42L42_ASP_RX0_CH_EN_SHIFT)
+#define CS42L42_ASP_RX0_CH1_EN 1
+#define CS42L42_ASP_RX0_CH2_EN 2
+#define CS42L42_ASP_RX0_CH3_EN 4
+#define CS42L42_ASP_RX0_CH4_EN 8
+
+#define CS42L42_ASP_RX_DAI0_CH1_AP_RES (CS42L42_PAGE_2A + 0x02)
+#define CS42L42_ASP_RX_DAI0_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x03)
+#define CS42L42_ASP_RX_DAI0_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x04)
+#define CS42L42_ASP_RX_DAI0_CH2_AP_RES (CS42L42_PAGE_2A + 0x05)
+#define CS42L42_ASP_RX_DAI0_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x06)
+#define CS42L42_ASP_RX_DAI0_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x07)
+#define CS42L42_ASP_RX_DAI0_CH3_AP_RES (CS42L42_PAGE_2A + 0x08)
+#define CS42L42_ASP_RX_DAI0_CH3_BIT_MSB (CS42L42_PAGE_2A + 0x09)
+#define CS42L42_ASP_RX_DAI0_CH3_BIT_LSB (CS42L42_PAGE_2A + 0x0A)
+#define CS42L42_ASP_RX_DAI0_CH4_AP_RES (CS42L42_PAGE_2A + 0x0B)
+#define CS42L42_ASP_RX_DAI0_CH4_BIT_MSB (CS42L42_PAGE_2A + 0x0C)
+#define CS42L42_ASP_RX_DAI0_CH4_BIT_LSB (CS42L42_PAGE_2A + 0x0D)
+#define CS42L42_ASP_RX_DAI1_CH1_AP_RES (CS42L42_PAGE_2A + 0x0E)
+#define CS42L42_ASP_RX_DAI1_CH1_BIT_MSB (CS42L42_PAGE_2A + 0x0F)
+#define CS42L42_ASP_RX_DAI1_CH1_BIT_LSB (CS42L42_PAGE_2A + 0x10)
+#define CS42L42_ASP_RX_DAI1_CH2_AP_RES (CS42L42_PAGE_2A + 0x11)
+#define CS42L42_ASP_RX_DAI1_CH2_BIT_MSB (CS42L42_PAGE_2A + 0x12)
+#define CS42L42_ASP_RX_DAI1_CH2_BIT_LSB (CS42L42_PAGE_2A + 0x13)
+
+#define CS42L42_ASP_RX_CH_AP_SHIFT 6
+#define CS42L42_ASP_RX_CH_AP_MASK (1 << CS42L42_ASP_RX_CH_AP_SHIFT)
+#define CS42L42_ASP_RX_CH_AP_LOW 0
+#define CS42L42_ASP_RX_CH_AP_HI 1
+#define CS42L42_ASP_RX_CH_RES_SHIFT 0
+#define CS42L42_ASP_RX_CH_RES_MASK (3 << CS42L42_ASP_RX_CH_RES_SHIFT)
+#define CS42L42_ASP_RX_CH_RES_32 3
+#define CS42L42_ASP_RX_CH_RES_16 1
+#define CS42L42_ASP_RX_CH_BIT_ST_SHIFT 0
+#define CS42L42_ASP_RX_CH_BIT_ST_MASK (0xff << CS42L42_ASP_RX_CH_BIT_ST_SHIFT)
+
+/* Page 0x30 ID Registers */
+#define CS42L42_SUB_REVID (CS42L42_PAGE_30 + 0x14)
+#define CS42L42_MAX_REGISTER (CS42L42_PAGE_30 + 0x14)
+
+/* Defines for fracturing values spread across multiple registers */
+#define CS42L42_FRAC0_VAL(val) ((val) & 0x0000ff)
+#define CS42L42_FRAC1_VAL(val) (((val) & 0x00ff00) >> 8)
+#define CS42L42_FRAC2_VAL(val) (((val) & 0xff0000) >> 16)
+
+#define CS42L42_NUM_SUPPLIES 5
+
+static const char *const cs42l42_supply_names[CS42L42_NUM_SUPPLIES] = {
+ "VA",
+ "VP",
+ "VCP",
+ "VD_FILT",
+ "VL",
+};
+
+struct cs42l42_private {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct regulator_bulk_data supplies[CS42L42_NUM_SUPPLIES];
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+ u32 sclk;
+ u32 srate;
+ u32 swidth;
+ u8 plug_state;
+ u8 hs_type;
+ u8 ts_inv;
+ u8 ts_dbnc_rise;
+ u8 ts_dbnc_fall;
+ u8 btn_det_init_dbnce;
+ u8 btn_det_event_dbnce;
+ u8 bias_thresholds[CS42L42_NUM_BIASES];
+ u8 hs_bias_ramp_rate;
+ u8 hs_bias_ramp_time;
+};
+
+#endif /* __CS42L42_H__ */
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 71ba5605495f..3df2c473ab88 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -1337,8 +1337,6 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
gpio_set_value_cansleep(cs42l73->pdata.reset_gpio, 1);
}
- regcache_cache_bypass(cs42l73->regmap, true);
-
/* initialize codec */
ret = regmap_read(cs42l73->regmap, CS42L73_DEVID_AB, &reg);
devid = (reg & 0xFF) << 12;
@@ -1366,8 +1364,6 @@ static int cs42l73_i2c_probe(struct i2c_client *i2c_client,
dev_info(&i2c_client->dev,
"Cirrus Logic CS42L73, Revision: %02X\n", reg & 0xFF);
- regcache_cache_bypass(cs42l73->regmap, false);
-
ret = snd_soc_register_codec(&i2c_client->dev,
&soc_codec_dev_cs42l73, cs42l73_dai,
ARRAY_SIZE(cs42l73_dai));
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index b4d87379d2bc..c1785bd4ff19 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -321,7 +321,6 @@ static struct snd_soc_dai_driver cs42xx8_dai = {
};
static const struct reg_default cs42xx8_reg[] = {
- { 0x01, 0x01 }, /* Chip I.D. and Revision Register */
{ 0x02, 0x00 }, /* Power Control */
{ 0x03, 0xF0 }, /* Functional Mode */
{ 0x04, 0x46 }, /* Interface Formats */
@@ -498,13 +497,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
/* Make sure hardware reset done */
msleep(5);
- /*
- * We haven't marked the chip revision as volatile due to
- * sharing a register with the right input volume; explicitly
- * bypass the cache to read it.
- */
- regcache_cache_bypass(cs42xx8->regmap, true);
-
/* Validate the chip ID */
ret = regmap_read(cs42xx8->regmap, CS42XX8_CHIPID, &val);
if (ret < 0) {
@@ -523,8 +515,6 @@ int cs42xx8_probe(struct device *dev, struct regmap *regmap)
dev_info(dev, "found device, revision %X\n",
val & CS42XX8_CHIPID_REV_ID_MASK);
- regcache_cache_bypass(cs42xx8->regmap, false);
-
cs42xx8_dai.name = cs42xx8->drvdata->name;
/* Each adc supports stereo input */
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index 5b22564f037c..73559ae864b6 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -335,9 +335,11 @@ static const struct snd_kcontrol_new cs47l24_aec_loopback_mux =
static const struct snd_soc_dapm_widget cs47l24_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
- ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1064,7 +1066,7 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
static int cs47l24_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
- struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct cs47l24_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
@@ -1113,8 +1115,8 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
static int cs47l24_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int ret;
priv->core.arizona->dapm = dapm;
@@ -1124,14 +1126,6 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_mono(codec);
arizona_init_notifiers(codec);
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
- "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
- priv);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
- return ret;
- }
-
ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
if (ret)
goto err_adsp2_codec_probe;
@@ -1145,7 +1139,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
if (ret)
goto err_adsp2_codec_probe;
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
return 0;
@@ -1159,17 +1153,12 @@ err_adsp2_codec_probe:
static int cs47l24_codec_remove(struct snd_soc_codec *codec)
{
struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
priv->core.arizona->dapm = NULL;
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
- arizona_free_spk(codec);
-
return 0;
}
@@ -1285,25 +1274,47 @@ static int cs47l24_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
+ cs47l24);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- return ret;
+ goto err_spk_irqs;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- snd_soc_unregister_platform(&pdev->dev);
+ goto err_platform;
}
return ret;
+
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
+
+ return ret;
}
static int cs47l24_remove(struct platform_device *pdev)
{
struct cs47l24_priv *cs47l24 = platform_get_drvdata(pdev);
+ struct arizona *arizona = cs47l24->core.arizona;
snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
@@ -1312,6 +1323,10 @@ static int cs47l24_remove(struct platform_device *pdev)
wm_adsp2_remove(&cs47l24->core.adsp[1]);
wm_adsp2_remove(&cs47l24->core.adsp[2]);
+ arizona_free_spk_irqs(arizona);
+
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, cs47l24);
+
return 0;
}
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index 2b8914dd5990..6274d79c1353 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -204,10 +204,19 @@ static void da7219_aad_hptest_work(struct work_struct *work)
snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
DA7219_MIXOUT_R_AMP_EN_MASK,
DA7219_MIXOUT_R_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
- snd_soc_write(codec, DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK,
+ DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK,
+ DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ msleep(DA7219_SETTLING_DELAY);
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MUTE_EN_MASK |
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK, 0);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MUTE_EN_MASK |
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK, 0);
/*
* If we're running from the internal oscillator then give audio paths
@@ -244,6 +253,7 @@ static void da7219_aad_hptest_work(struct work_struct *work)
regcache_mark_dirty(da7219->regmap);
regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
DA7219_HP_R_CTRL);
+ msleep(DA7219_SETTLING_DELAY);
regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
DA7219_MIXOUT_R_CTRL);
regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 1152aa5e7c39..99601627f83c 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -823,6 +823,85 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
}
}
+static int da7219_settling_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ msleep(DA7219_SETTLING_DELAY);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_mixout_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ u8 hp_ctrl, min_gain_mask;
+
+ switch (w->reg) {
+ case DA7219_MIXOUT_L_CTRL:
+ hp_ctrl = DA7219_HP_L_CTRL;
+ min_gain_mask = DA7219_HP_L_AMP_MIN_GAIN_EN_MASK;
+ break;
+ case DA7219_MIXOUT_R_CTRL:
+ hp_ctrl = DA7219_HP_R_CTRL;
+ min_gain_mask = DA7219_HP_R_AMP_MIN_GAIN_EN_MASK;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Enable minimum gain on HP to avoid pops */
+ snd_soc_update_bits(codec, hp_ctrl, min_gain_mask,
+ min_gain_mask);
+
+ msleep(DA7219_MIN_GAIN_DELAY);
+
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ /* Remove minimum gain on HP */
+ snd_soc_update_bits(codec, hp_ctrl, min_gain_mask, 0);
+
+ break;
+ }
+
+ return 0;
+}
+
+static int da7219_gain_ramp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Ensure nominal gain ramping for DAPM sequence */
+ da7219->gain_ramp_ctrl =
+ snd_soc_read(codec, DA7219_GAIN_RAMP_CTRL);
+ snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL,
+ DA7219_GAIN_RAMP_RATE_NOMINAL);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ /* Restore previous gain ramp settings */
+ snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL,
+ da7219->gain_ramp_ctrl);
+ break;
+ }
+
+ return 0;
+}
+
/*
* DAPM Widgets
@@ -880,7 +959,8 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
/* DAI */
- SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, DA7219_DAI_TDM_CTRL,
+ DA7219_DAI_OE_SHIFT, DA7219_NO_INVERT),
SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
/* Output Muxes */
@@ -906,30 +986,46 @@ static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
/* DACs */
- SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT,
- DA7219_NO_INVERT),
- SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT,
- DA7219_NO_INVERT),
+ SND_SOC_DAPM_DAC_E("DACL", NULL, DA7219_DAC_L_CTRL,
+ DA7219_DAC_L_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_DAC_E("DACR", NULL, DA7219_DAC_R_CTRL,
+ DA7219_DAC_R_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output PGAs */
- SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
- DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
- DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
- NULL, 0),
- SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL,
- DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
- SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL,
- DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_PGA_E("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+ DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA_E("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+ DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ NULL, 0, da7219_mixout_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Left PGA", 1, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_SUPPLY_S("Headphone Right PGA", 1, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
/* Output Supplies */
- SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT,
- DA7219_NO_INVERT, NULL, 0),
+ SND_SOC_DAPM_SUPPLY_S("Charge Pump", 0, DA7219_CP_CTRL,
+ DA7219_CP_EN_SHIFT, DA7219_NO_INVERT,
+ da7219_settling_event,
+ SND_SOC_DAPM_POST_PMU),
/* Outputs */
SND_SOC_DAPM_OUTPUT("HPL"),
SND_SOC_DAPM_OUTPUT("HPR"),
+
+ /* Pre/Post Power */
+ SND_SOC_DAPM_PRE("Pre Power Gain Ramp", da7219_gain_ramp_event),
+ SND_SOC_DAPM_POST("Post Power Gain Ramp", da7219_gain_ramp_event),
};
@@ -1002,8 +1098,8 @@ static const struct snd_soc_dapm_route da7219_audio_map[] = {
{"Mixout Left PGA", NULL, "DACL"},
{"Mixout Right PGA", NULL, "DACR"},
- {"Headphone Left PGA", NULL, "Mixout Left PGA"},
- {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+ {"HPL", NULL, "Mixout Left PGA"},
+ {"HPR", NULL, "Mixout Right PGA"},
{"HPL", NULL, "Headphone Left PGA"},
{"HPR", NULL, "Headphone Right PGA"},
@@ -1711,6 +1807,14 @@ static int da7219_probe(struct snd_soc_codec *codec)
DA7219_HP_R_AMP_RAMP_EN_MASK,
DA7219_HP_R_AMP_RAMP_EN_MASK);
+ /* Default minimum gain on HP to avoid pops during DAPM sequencing */
+ snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_L_AMP_MIN_GAIN_EN_MASK);
+ snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK,
+ DA7219_HP_R_AMP_MIN_GAIN_EN_MASK);
+
/* Default infinite tone gen, start/stop by Kcontrol */
snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
index 66d3bad86739..6baba7455fa1 100644
--- a/sound/soc/codecs/da7219.h
+++ b/sound/soc/codecs/da7219.h
@@ -777,6 +777,10 @@
#define DA7219_SYS_STAT_CHECK_RETRIES 6
#define DA7219_SYS_STAT_CHECK_DELAY 50
+/* Power up/down Delays */
+#define DA7219_SETTLING_DELAY 40
+#define DA7219_MIN_GAIN_DELAY 30
+
enum da7219_clk_src {
DA7219_CLKSRC_MCLK = 0,
DA7219_CLKSRC_MCLK_SQR,
@@ -814,6 +818,7 @@ struct da7219_priv {
bool master;
bool alc_en;
+ u8 gain_ramp_ctrl;
};
#endif /* __DA7219_H */
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index b904492d7744..90b5948e0ff3 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -364,7 +364,12 @@ static int hdmi_of_xlate_dai_name(struct snd_soc_component *component,
struct of_phandle_args *args,
const char **dai_name)
{
- int id = args->args[0];
+ int id;
+
+ if (args->args_count)
+ id = args->args[0];
+ else
+ id = 0;
if (id < ARRAY_SIZE(hdmi_dai_name)) {
*dai_name = hdmi_dai_name[id];
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index 55558643166f..2db8179047ae 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -249,6 +249,11 @@ static int rt298_jack_detect(struct rt298_priv *rt298, bool *hp, bool *mic)
snd_soc_dapm_force_enable_pin(dapm, "LDO1");
snd_soc_dapm_sync(dapm);
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL1, 0x1001, 0);
+ regmap_update_bits(rt298->regmap,
+ RT298_POWER_CTRL2, 0x4, 0x4);
+
regmap_write(rt298->regmap, RT298_SET_MIC1, 0x24);
msleep(50);
diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c
index 01a18d88f1eb..00ff2788879e 100644
--- a/sound/soc/codecs/rt5663.c
+++ b/sound/soc/codecs/rt5663.c
@@ -1547,11 +1547,11 @@ static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
msleep(sleep_time[i]);
val = snd_soc_read(codec, RT5663_EM_JACK_TYPE_2) &
0x0003;
+ dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n",
+ __func__, val, sleep_time[i]);
i++;
if (val == 0x1 || val == 0x2 || val == 0x3)
break;
- dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n",
- __func__, val, sleep_time[i]);
}
dev_dbg(codec->dev, "%s val = %d\n", __func__, val);
switch (val) {
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
index 7b31ee9b82bc..d6e00c77edcd 100644
--- a/sound/soc/codecs/sti-sas.c
+++ b/sound/soc/codecs/sti-sas.c
@@ -424,7 +424,7 @@ static const struct snd_soc_dai_ops stih407_dac_ops = {
static const struct regmap_config stih407_sas_regmap = {
.reg_bits = 32,
.val_bits = 32,
-
+ .fast_io = true,
.max_register = STIH407_AUDIO_DAC_CTRL,
.reg_defaults = stih407_sas_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(stih407_sas_reg_defaults),
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index df5e5cb33baa..810369f687d7 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -341,20 +341,9 @@ static int tas571x_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
-
- gpiod_set_value(priv->pdn_gpio, 0);
- usleep_range(5000, 6000);
-
- regcache_cache_only(priv->regmap, false);
- ret = regcache_sync(priv->regmap);
- if (ret)
- return ret;
}
break;
case SND_SOC_BIAS_OFF:
- regcache_cache_only(priv->regmap, true);
- gpiod_set_value(priv->pdn_gpio, 1);
-
if (!IS_ERR(priv->mclk))
clk_disable_unprepare(priv->mclk);
break;
@@ -401,16 +390,6 @@ static const struct snd_kcontrol_new tas5711_controls[] = {
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
-
- SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
- TAS5717_CH1_LEFT_CH_MIX_REG,
- TAS5717_CH1_RIGHT_CH_MIX_REG,
- 16, 0, 0x80, 0),
-
- SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
- TAS5717_CH2_LEFT_CH_MIX_REG,
- TAS5717_CH2_RIGHT_CH_MIX_REG,
- 16, 0, 0x80, 0),
};
static const struct regmap_range tas571x_readonly_regs_range[] = {
@@ -488,6 +467,16 @@ static const struct snd_kcontrol_new tas5717_controls[] = {
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
+ SOC_DOUBLE_R_RANGE("CH1 Mixer Volume",
+ TAS5717_CH1_LEFT_CH_MIX_REG,
+ TAS5717_CH1_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
+ SOC_DOUBLE_R_RANGE("CH2 Mixer Volume",
+ TAS5717_CH2_LEFT_CH_MIX_REG,
+ TAS5717_CH2_RIGHT_CH_MIX_REG,
+ 16, 0, 0x80, 0),
+
/*
* The biquads are named according to the register names.
* Please note that TI's TAS57xx Graphical Development Environment
@@ -747,13 +736,14 @@ static int tas571x_i2c_probe(struct i2c_client *client,
/* pulse the active low reset line for ~100us */
usleep_range(100, 200);
gpiod_set_value(priv->reset_gpio, 0);
- usleep_range(12000, 20000);
+ usleep_range(13500, 20000);
}
ret = regmap_write(priv->regmap, TAS571X_OSC_TRIM_REG, 0);
if (ret)
return ret;
+ usleep_range(50000, 60000);
memcpy(&priv->codec_driver, &tas571x_codec, sizeof(priv->codec_driver));
priv->codec_driver.component_driver.controls = priv->chip->controls;
@@ -770,9 +760,6 @@ static int tas571x_i2c_probe(struct i2c_client *client,
return ret;
}
- regcache_cache_only(priv->regmap, true);
- gpiod_set_value(priv->pdn_gpio, 1);
-
return snd_soc_register_codec(&client->dev, &priv->codec_driver,
&tas571x_dai, 1);
}
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 5a8d96ec058c..8877b74b0510 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -157,7 +157,7 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned short val;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
int connect, change;
val = (ucontrol->value.integer.value[0] & mask);
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 93876c6d48ee..e7ab37d0dd32 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -607,6 +607,9 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
return 0;
}
@@ -1077,9 +1080,11 @@ static const struct snd_kcontrol_new wm5102_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm5102_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm5102_sysclk_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1903,7 +1908,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
static int wm5102_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
- struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct wm5102_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
return wm_adsp_compr_open(&priv->core.adsp[0], stream);
}
@@ -1926,18 +1931,10 @@ static irqreturn_t wm5102_adsp2_irq(int irq, void *data)
static int wm5102_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int ret;
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
- "ADSP2 Compressed IRQ", wm5102_adsp2_irq,
- priv);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
- return ret;
- }
-
ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
if (ret)
return ret;
@@ -1949,8 +1946,9 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
+ arizona_init_notifiers(codec);
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
priv->core.arizona->dapm = dapm;
@@ -1965,16 +1963,11 @@ err_adsp2_codec_probe:
static int wm5102_codec_remove(struct snd_soc_codec *codec)
{
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
wm_adsp2_codec_remove(&priv->core.adsp[0], codec);
priv->core.arizona->dapm = NULL;
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
- arizona_free_spk(codec);
-
return 0;
}
@@ -2092,25 +2085,47 @@ static int wm5102_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5102_adsp2_irq,
+ wm5102);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- return ret;
+ goto err_spk_irqs;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
wm5102_dai, ARRAY_SIZE(wm5102_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- snd_soc_unregister_platform(&pdev->dev);
+ goto err_platform;
}
return ret;
+
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
+
+ return ret;
}
static int wm5102_remove(struct platform_device *pdev)
{
struct wm5102_priv *wm5102 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm5102->core.arizona;
snd_soc_unregister_platform(&pdev->dev);
snd_soc_unregister_codec(&pdev->dev);
@@ -2118,6 +2133,10 @@ static int wm5102_remove(struct platform_device *pdev)
wm_adsp2_remove(&wm5102->core.adsp[0]);
+ arizona_free_spk_irqs(arizona);
+
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5102);
+
return 0;
}
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 06bae3b23fce..585fc706c1b0 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -183,7 +183,9 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
regmap_write_async(regmap, patch[i].reg,
patch[i].def);
break;
-
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
break;
}
@@ -1073,9 +1075,11 @@ static const struct snd_kcontrol_new wm5110_output_anc_src[] = {
static const struct snd_soc_dapm_widget wm5110_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
- 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+ 0, wm5110_sysclk_ev, SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -2220,7 +2224,7 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
static int wm5110_open(struct snd_compr_stream *stream)
{
struct snd_soc_pcm_runtime *rtd = stream->private_data;
- struct wm5110_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+ struct wm5110_priv *priv = snd_soc_platform_get_drvdata(rtd->platform);
struct arizona *arizona = priv->core.arizona;
int n_adsp;
@@ -2269,8 +2273,8 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int i, ret;
priv->core.arizona->dapm = dapm;
@@ -2280,14 +2284,6 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_mono(codec);
arizona_init_notifiers(codec);
- ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
- "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
- priv);
- if (ret != 0) {
- dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
- return ret;
- }
-
for (i = 0; i < WM5110_NUM_ADSP; ++i) {
ret = wm_adsp2_codec_probe(&priv->core.adsp[i], codec);
if (ret)
@@ -2300,7 +2296,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
if (ret)
goto err_adsp2_codec_probe;
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
return 0;
@@ -2308,15 +2304,12 @@ err_adsp2_codec_probe:
for (--i; i >= 0; --i)
wm_adsp2_codec_remove(&priv->core.adsp[i], codec);
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
return ret;
}
static int wm5110_codec_remove(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona *arizona = priv->core.arizona;
int i;
for (i = 0; i < WM5110_NUM_ADSP; ++i)
@@ -2324,10 +2317,6 @@ static int wm5110_codec_remove(struct snd_soc_codec *codec)
priv->core.arizona->dapm = NULL;
- arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
-
- arizona_free_spk(codec);
-
return 0;
}
@@ -2449,25 +2438,47 @@ static int wm5110_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
+ ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+ "ADSP2 Compressed IRQ", wm5110_adsp2_irq,
+ wm5110);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "Failed to request DSP IRQ: %d\n", ret);
+ return ret;
+ }
+
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ goto err_dsp_irq;
+
ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
- return ret;
+ goto err_spk_irqs;
}
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
wm5110_dai, ARRAY_SIZE(wm5110_dai));
if (ret < 0) {
dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
- snd_soc_unregister_platform(&pdev->dev);
+ goto err_platform;
}
return ret;
+
+err_platform:
+ snd_soc_unregister_platform(&pdev->dev);
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+err_dsp_irq:
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
+
+ return ret;
}
static int wm5110_remove(struct platform_device *pdev)
{
struct wm5110_priv *wm5110 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm5110->core.arizona;
int i;
snd_soc_unregister_platform(&pdev->dev);
@@ -2477,6 +2488,10 @@ static int wm5110_remove(struct platform_device *pdev)
for (i = 0; i < WM5110_NUM_ADSP; i++)
wm_adsp2_remove(&wm5110->core.adsp[i]);
+ arizona_free_spk_irqs(arizona);
+
+ arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, wm5110);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index 2f2821b3382f..ee0c8639c743 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -108,6 +108,9 @@ static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_PRE_PMD:
break;
+ case SND_SOC_DAPM_PRE_PMU:
+ case SND_SOC_DAPM_POST_PMD:
+ return arizona_clk_ev(w, kcontrol, event);
default:
return 0;
}
@@ -408,9 +411,11 @@ static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
0, wm8997_sysclk_ev,
- SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1055,11 +1060,13 @@ static struct snd_soc_dai_driver wm8997_dai[] = {
static int wm8997_codec_probe(struct snd_soc_codec *codec)
{
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
arizona_init_spk(codec);
+ arizona_init_notifiers(codec);
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
priv->core.arizona->dapm = dapm;
@@ -1072,8 +1079,6 @@ static int wm8997_codec_remove(struct snd_soc_codec *codec)
priv->core.arizona->dapm = NULL;
- arizona_free_spk(codec);
-
return 0;
}
@@ -1119,7 +1124,7 @@ static int wm8997_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm8997_priv *wm8997;
- int i;
+ int i, ret;
wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
GFP_KERNEL);
@@ -1159,15 +1164,33 @@ static int wm8997_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
- wm8997_dai, ARRAY_SIZE(wm8997_dai));
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
+ wm8997_dai, ARRAY_SIZE(wm8997_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+
+ return ret;
}
static int wm8997_remove(struct platform_device *pdev)
{
+ struct wm8997_priv *wm8997 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm8997->core.arizona;
+
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ arizona_free_spk_irqs(arizona);
+
return 0;
}
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index bcc2e1060a6c..3694f5958d86 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -541,9 +541,11 @@ static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
- ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_SYSCLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
- ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, arizona_clk_ev,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
@@ -1318,13 +1320,15 @@ static int wm8998_codec_probe(struct snd_soc_codec *codec)
{
struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct snd_soc_component *component = snd_soc_dapm_to_component(dapm);
priv->core.arizona->dapm = dapm;
arizona_init_spk(codec);
arizona_init_gpio(codec);
+ arizona_init_notifiers(codec);
- snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+ snd_soc_component_disable_pin(component, "HAPTICS");
return 0;
}
@@ -1335,8 +1339,6 @@ static int wm8998_codec_remove(struct snd_soc_codec *codec)
priv->core.arizona->dapm = NULL;
- arizona_free_spk(codec);
-
return 0;
}
@@ -1385,7 +1387,7 @@ static int wm8998_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm8998_priv *wm8998;
- int i;
+ int i, ret;
wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
GFP_KERNEL);
@@ -1417,15 +1419,35 @@ static int wm8998_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
- return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
- wm8998_dai, ARRAY_SIZE(wm8998_dai));
+ ret = arizona_init_spk_irqs(arizona);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
+ wm8998_dai, ARRAY_SIZE(wm8998_dai));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+ goto err_spk_irqs;
+ }
+
+ return ret;
+
+err_spk_irqs:
+ arizona_free_spk_irqs(arizona);
+
+ return ret;
}
static int wm8998_remove(struct platform_device *pdev)
{
+ struct wm8998_priv *wm8998 = platform_get_drvdata(pdev);
+ struct arizona *arizona = wm8998->core.arizona;
+
snd_soc_unregister_codec(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ arizona_free_spk_irqs(arizona);
+
return 0;
}
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 557709eac698..85f7c5bb8b82 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -187,7 +187,7 @@ static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
bool change;
mixer = mc->shift >> 8;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index e4301ddb1b84..7e4822185feb 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -231,7 +231,7 @@ static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
struct soc_mixer_control *mc =
(struct soc_mixer_control *)kcontrol->private_value;
unsigned int mixer, mask, shift, old;
- struct snd_soc_dapm_update update;
+ struct snd_soc_dapm_update update = { 0 };
bool change;
mixer = mc->shift >> 8;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index b943dde8dbe5..593b7d1aed46 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -162,6 +162,16 @@
#define ADSP_MAX_STD_CTRL_SIZE 512
+#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
+#define WM_ADSP_ACKED_CTL_N_QUICKPOLLS 10
+#define WM_ADSP_ACKED_CTL_MIN_VALUE 0
+#define WM_ADSP_ACKED_CTL_MAX_VALUE 0xFFFFFF
+
+/*
+ * Event control messages
+ */
+#define WM_ADSP_FW_EVENT_SHUTDOWN 0x000001
+
struct wm_adsp_buf {
struct list_head list;
void *buf;
@@ -177,7 +187,7 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
buf->buf = vmalloc(len);
if (!buf->buf) {
- vfree(buf);
+ kfree(buf);
return NULL;
}
memcpy(buf->buf, src, len);
@@ -441,11 +451,29 @@ struct wm_coeff_ctl {
unsigned int offset;
size_t len;
unsigned int set:1;
- struct snd_kcontrol *kcontrol;
struct soc_bytes_ext bytes_ext;
unsigned int flags;
+ unsigned int type;
};
+static const char *wm_adsp_mem_region_name(unsigned int type)
+{
+ switch (type) {
+ case WMFW_ADSP1_PM:
+ return "PM";
+ case WMFW_ADSP1_DM:
+ return "DM";
+ case WMFW_ADSP2_XM:
+ return "XM";
+ case WMFW_ADSP2_YM:
+ return "YM";
+ case WMFW_ADSP1_ZM:
+ return "ZM";
+ default:
+ return NULL;
+ }
+}
+
#ifdef CONFIG_DEBUG_FS
static void wm_adsp_debugfs_save_wmfwname(struct wm_adsp *dsp, const char *s)
{
@@ -727,6 +755,24 @@ static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
return container_of(ext, struct wm_coeff_ctl, bytes_ext);
}
+static int wm_coeff_base_reg(struct wm_coeff_ctl *ctl, unsigned int *reg)
+{
+ const struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
+ struct wm_adsp *dsp = ctl->dsp;
+ const struct wm_adsp_region *mem;
+
+ mem = wm_adsp_find_region(dsp, alg_region->type);
+ if (!mem) {
+ adsp_err(dsp, "No base for region %x\n",
+ alg_region->type);
+ return -EINVAL;
+ }
+
+ *reg = wm_adsp_region_to_reg(mem, ctl->alg_region.base + ctl->offset);
+
+ return 0;
+}
+
static int wm_coeff_info(struct snd_kcontrol *kctl,
struct snd_ctl_elem_info *uinfo)
{
@@ -734,30 +780,94 @@ static int wm_coeff_info(struct snd_kcontrol *kctl,
(struct soc_bytes_ext *)kctl->private_value;
struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
- uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
- uinfo->count = ctl->len;
+ switch (ctl->type) {
+ case WMFW_CTL_TYPE_ACKED:
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+ uinfo->value.integer.min = WM_ADSP_ACKED_CTL_MIN_VALUE;
+ uinfo->value.integer.max = WM_ADSP_ACKED_CTL_MAX_VALUE;
+ uinfo->value.integer.step = 1;
+ uinfo->count = 1;
+ break;
+ default:
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = ctl->len;
+ break;
+ }
+
return 0;
}
+static int wm_coeff_write_acked_control(struct wm_coeff_ctl *ctl,
+ unsigned int event_id)
+{
+ struct wm_adsp *dsp = ctl->dsp;
+ u32 val = cpu_to_be32(event_id);
+ unsigned int reg;
+ int i, ret;
+
+ ret = wm_coeff_base_reg(ctl, &reg);
+ if (ret)
+ return ret;
+
+ adsp_dbg(dsp, "Sending 0x%x to acked control alg 0x%x %s:0x%x\n",
+ event_id, ctl->alg_region.alg,
+ wm_adsp_mem_region_name(ctl->alg_region.type), ctl->offset);
+
+ ret = regmap_raw_write(dsp->regmap, reg, &val, sizeof(val));
+ if (ret) {
+ adsp_err(dsp, "Failed to write %x: %d\n", reg, ret);
+ return ret;
+ }
+
+ /*
+ * Poll for ack, we initially poll at ~1ms intervals for firmwares
+ * that respond quickly, then go to ~10ms polls. A firmware is unlikely
+ * to ack instantly so we do the first 1ms delay before reading the
+ * control to avoid a pointless bus transaction
+ */
+ for (i = 0; i < WM_ADSP_ACKED_CTL_TIMEOUT_MS;) {
+ switch (i) {
+ case 0 ... WM_ADSP_ACKED_CTL_N_QUICKPOLLS - 1:
+ usleep_range(1000, 2000);
+ i++;
+ break;
+ default:
+ usleep_range(10000, 20000);
+ i += 10;
+ break;
+ }
+
+ ret = regmap_raw_read(dsp->regmap, reg, &val, sizeof(val));
+ if (ret) {
+ adsp_err(dsp, "Failed to read %x: %d\n", reg, ret);
+ return ret;
+ }
+
+ if (val == 0) {
+ adsp_dbg(dsp, "Acked control ACKED at poll %u\n", i);
+ return 0;
+ }
+ }
+
+ adsp_warn(dsp, "Acked control @0x%x alg:0x%x %s:0x%x timed out\n",
+ reg, ctl->alg_region.alg,
+ wm_adsp_mem_region_name(ctl->alg_region.type),
+ ctl->offset);
+
+ return -ETIMEDOUT;
+}
+
static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
const void *buf, size_t len)
{
- struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
- const struct wm_adsp_region *mem;
struct wm_adsp *dsp = ctl->dsp;
void *scratch;
int ret;
unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n",
- alg_region->type);
- return -EINVAL;
- }
-
- reg = ctl->alg_region.base + ctl->offset;
- reg = wm_adsp_region_to_reg(mem, reg);
+ ret = wm_coeff_base_reg(ctl, &reg);
+ if (ret)
+ return ret;
scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
if (!scratch)
@@ -823,25 +933,41 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
return ret;
}
+static int wm_coeff_put_acked(struct snd_kcontrol *kctl,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct soc_bytes_ext *bytes_ext =
+ (struct soc_bytes_ext *)kctl->private_value;
+ struct wm_coeff_ctl *ctl = bytes_ext_to_ctl(bytes_ext);
+ unsigned int val = ucontrol->value.integer.value[0];
+ int ret;
+
+ if (val == 0)
+ return 0; /* 0 means no event */
+
+ mutex_lock(&ctl->dsp->pwr_lock);
+
+ if (ctl->enabled)
+ ret = wm_coeff_write_acked_control(ctl, val);
+ else
+ ret = -EPERM;
+
+ mutex_unlock(&ctl->dsp->pwr_lock);
+
+ return ret;
+}
+
static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
void *buf, size_t len)
{
- struct wm_adsp_alg_region *alg_region = &ctl->alg_region;
- const struct wm_adsp_region *mem;
struct wm_adsp *dsp = ctl->dsp;
void *scratch;
int ret;
unsigned int reg;
- mem = wm_adsp_find_region(dsp, alg_region->type);
- if (!mem) {
- adsp_err(dsp, "No base for region %x\n",
- alg_region->type);
- return -EINVAL;
- }
-
- reg = ctl->alg_region.base + ctl->offset;
- reg = wm_adsp_region_to_reg(mem, reg);
+ ret = wm_coeff_base_reg(ctl, &reg);
+ if (ret)
+ return ret;
scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
if (!scratch)
@@ -918,6 +1044,21 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
return ret;
}
+static int wm_coeff_get_acked(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ /*
+ * Although it's not useful to read an acked control, we must satisfy
+ * user-side assumptions that all controls are readable and that a
+ * write of the same value should be filtered out (it's valid to send
+ * the same event number again to the firmware). We therefore return 0,
+ * meaning "no event" so valid event numbers will always be a change
+ */
+ ucontrol->value.integer.value[0] = 0;
+
+ return 0;
+}
+
struct wmfw_ctl_work {
struct wm_adsp *dsp;
struct wm_coeff_ctl *ctl;
@@ -967,30 +1108,35 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
if (!kcontrol)
return -ENOMEM;
- kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kcontrol->name = ctl->name;
kcontrol->info = wm_coeff_info;
- kcontrol->get = wm_coeff_get;
- kcontrol->put = wm_coeff_put;
kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
kcontrol->tlv.c = snd_soc_bytes_tlv_callback;
kcontrol->private_value = (unsigned long)&ctl->bytes_ext;
+ kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
- ctl->bytes_ext.max = ctl->len;
- ctl->bytes_ext.get = wm_coeff_tlv_get;
- ctl->bytes_ext.put = wm_coeff_tlv_put;
+ switch (ctl->type) {
+ case WMFW_CTL_TYPE_ACKED:
+ kcontrol->get = wm_coeff_get_acked;
+ kcontrol->put = wm_coeff_put_acked;
+ break;
+ default:
+ kcontrol->get = wm_coeff_get;
+ kcontrol->put = wm_coeff_put;
- kcontrol->access = wmfw_convert_flags(ctl->flags, ctl->len);
+ ctl->bytes_ext.max = ctl->len;
+ ctl->bytes_ext.get = wm_coeff_tlv_get;
+ ctl->bytes_ext.put = wm_coeff_tlv_put;
+ break;
+ }
- ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
+ ret = snd_soc_add_codec_controls(dsp->codec, kcontrol, 1);
if (ret < 0)
goto err_kcontrol;
kfree(kcontrol);
- ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
-
return 0;
err_kcontrol:
@@ -1035,6 +1181,27 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp)
return 0;
}
+static void wm_adsp_signal_event_controls(struct wm_adsp *dsp,
+ unsigned int event)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list) {
+ if (ctl->type != WMFW_CTL_TYPE_HOSTEVENT)
+ continue;
+
+ if (!ctl->enabled)
+ continue;
+
+ ret = wm_coeff_write_acked_control(ctl, event);
+ if (ret)
+ adsp_warn(dsp,
+ "Failed to send 0x%x event to alg 0x%x (%d)\n",
+ event, ctl->alg_region.alg, ret);
+ }
+}
+
static void wm_adsp_ctl_work(struct work_struct *work)
{
struct wmfw_ctl_work *ctl_work = container_of(work,
@@ -1056,34 +1223,16 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
const struct wm_adsp_alg_region *alg_region,
unsigned int offset, unsigned int len,
const char *subname, unsigned int subname_len,
- unsigned int flags)
+ unsigned int flags, unsigned int type)
{
struct wm_coeff_ctl *ctl;
struct wmfw_ctl_work *ctl_work;
char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
- char *region_name;
+ const char *region_name;
int ret;
- if (flags & WMFW_CTL_FLAG_SYS)
- return 0;
-
- switch (alg_region->type) {
- case WMFW_ADSP1_PM:
- region_name = "PM";
- break;
- case WMFW_ADSP1_DM:
- region_name = "DM";
- break;
- case WMFW_ADSP2_XM:
- region_name = "XM";
- break;
- case WMFW_ADSP2_YM:
- region_name = "YM";
- break;
- case WMFW_ADSP1_ZM:
- region_name = "ZM";
- break;
- default:
+ region_name = wm_adsp_mem_region_name(alg_region->type);
+ if (!region_name) {
adsp_err(dsp, "Unknown region type: %d\n", alg_region->type);
return -EINVAL;
}
@@ -1139,6 +1288,7 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
ctl->dsp = dsp;
ctl->flags = flags;
+ ctl->type = type;
ctl->offset = offset;
ctl->len = len;
ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
@@ -1149,6 +1299,9 @@ static int wm_adsp_create_control(struct wm_adsp *dsp,
list_add(&ctl->list, &dsp->ctl_list);
+ if (flags & WMFW_CTL_FLAG_SYS)
+ return 0;
+
ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
if (!ctl_work) {
ret = -ENOMEM;
@@ -1308,6 +1461,21 @@ static inline void wm_coeff_parse_coeff(struct wm_adsp *dsp, const u8 **data,
adsp_dbg(dsp, "\tALSA control len: %#x\n", blk->len);
}
+static int wm_adsp_check_coeff_flags(struct wm_adsp *dsp,
+ const struct wm_coeff_parsed_coeff *coeff_blk,
+ unsigned int f_required,
+ unsigned int f_illegal)
+{
+ if ((coeff_blk->flags & f_illegal) ||
+ ((coeff_blk->flags & f_required) != f_required)) {
+ adsp_err(dsp, "Illegal flags 0x%x for control type 0x%x\n",
+ coeff_blk->flags, coeff_blk->ctl_type);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
const struct wmfw_region *region)
{
@@ -1324,6 +1492,28 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
switch (coeff_blk.ctl_type) {
case SNDRV_CTL_ELEM_TYPE_BYTES:
break;
+ case WMFW_CTL_TYPE_ACKED:
+ if (coeff_blk.flags & WMFW_CTL_FLAG_SYS)
+ continue; /* ignore */
+
+ ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
+ WMFW_CTL_FLAG_VOLATILE |
+ WMFW_CTL_FLAG_WRITEABLE |
+ WMFW_CTL_FLAG_READABLE,
+ 0);
+ if (ret)
+ return -EINVAL;
+ break;
+ case WMFW_CTL_TYPE_HOSTEVENT:
+ ret = wm_adsp_check_coeff_flags(dsp, &coeff_blk,
+ WMFW_CTL_FLAG_SYS |
+ WMFW_CTL_FLAG_VOLATILE |
+ WMFW_CTL_FLAG_WRITEABLE |
+ WMFW_CTL_FLAG_READABLE,
+ 0);
+ if (ret)
+ return -EINVAL;
+ break;
default:
adsp_err(dsp, "Unknown control type: %d\n",
coeff_blk.ctl_type);
@@ -1338,7 +1528,8 @@ static int wm_adsp_parse_coeff(struct wm_adsp *dsp,
coeff_blk.len,
coeff_blk.name,
coeff_blk.name_len,
- coeff_blk.flags);
+ coeff_blk.flags,
+ coeff_blk.ctl_type);
if (ret < 0)
adsp_err(dsp, "Failed to create control: %.*s, %d\n",
coeff_blk.name_len, coeff_blk.name, ret);
@@ -1491,23 +1682,11 @@ static int wm_adsp_load(struct wm_adsp *dsp)
reg = offset;
break;
case WMFW_ADSP1_PM:
- region_name = "PM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP1_DM:
- region_name = "DM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP2_XM:
- region_name = "XM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP2_YM:
- region_name = "YM";
- reg = wm_adsp_region_to_reg(mem, offset);
- break;
case WMFW_ADSP1_ZM:
- region_name = "ZM";
+ region_name = wm_adsp_mem_region_name(type);
reg = wm_adsp_region_to_reg(mem, offset);
break;
default:
@@ -1750,7 +1929,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp1_alg[i].dm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1770,7 +1950,8 @@ static int wm_adsp1_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp1_alg[i].zm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1861,7 +2042,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp2_alg[i].xm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1881,7 +2063,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp2_alg[i].ym);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1901,7 +2084,8 @@ static int wm_adsp2_setup_algs(struct wm_adsp *dsp)
len -= be32_to_cpu(adsp2_alg[i].zm);
len *= 4;
wm_adsp_create_control(dsp, alg_region, 0,
- len, NULL, 0, 0);
+ len, NULL, 0, 0,
+ SNDRV_CTL_ELEM_TYPE_BYTES);
} else {
adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
be32_to_cpu(adsp2_alg[i].alg.id));
@@ -2114,7 +2298,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
int ret;
unsigned int val;
- dsp->card = codec->component.card;
+ dsp->codec = codec;
mutex_lock(&dsp->pwr_lock);
@@ -2325,8 +2509,6 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsp = &dsps[w->shift];
struct wm_coeff_ctl *ctl;
- dsp->card = codec->component.card;
-
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
wm_adsp2_set_dspclk(dsp, freq);
@@ -2393,14 +2575,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
mutex_lock(&dsp->pwr_lock);
- if (wm_adsp_fw[dsp->fw].num_caps != 0)
+ if (wm_adsp_fw[dsp->fw].num_caps != 0) {
ret = wm_adsp_buffer_init(dsp);
+ if (ret < 0) {
+ mutex_unlock(&dsp->pwr_lock);
+ goto err;
+ }
+ }
mutex_unlock(&dsp->pwr_lock);
break;
case SND_SOC_DAPM_PRE_PMD:
+ /* Tell the firmware to cleanup */
+ wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
+
/* Log firmware state, it can be useful for analysis */
wm_adsp2_show_fw_status(dsp);
@@ -2441,6 +2631,8 @@ EXPORT_SYMBOL_GPL(wm_adsp2_event);
int wm_adsp2_codec_probe(struct wm_adsp *dsp, struct snd_soc_codec *codec)
{
+ dsp->codec = codec;
+
wm_adsp2_init_debugfs(dsp, codec);
return snd_soc_add_codec_controls(codec,
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 362dd7ce60d8..411d062c13f2 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -44,7 +44,7 @@ struct wm_adsp {
int type;
struct device *dev;
struct regmap *regmap;
- struct snd_soc_card *card;
+ struct snd_soc_codec *codec;
int base;
int sysclk_reg;
@@ -110,18 +110,17 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
-extern int wm_adsp_compr_open(struct wm_adsp *dsp,
- struct snd_compr_stream *stream);
-extern int wm_adsp_compr_free(struct snd_compr_stream *stream);
-extern int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
- struct snd_compr_params *params);
-extern int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
- struct snd_compr_caps *caps);
-extern int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
-extern int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
-extern int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
- struct snd_compr_tstamp *tstamp);
-extern int wm_adsp_compr_copy(struct snd_compr_stream *stream,
- char __user *buf, size_t count);
+int wm_adsp_compr_open(struct wm_adsp *dsp, struct snd_compr_stream *stream);
+int wm_adsp_compr_free(struct snd_compr_stream *stream);
+int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
+ struct snd_compr_params *params);
+int wm_adsp_compr_get_caps(struct snd_compr_stream *stream,
+ struct snd_compr_caps *caps);
+int wm_adsp_compr_trigger(struct snd_compr_stream *stream, int cmd);
+int wm_adsp_compr_handle_irq(struct wm_adsp *dsp);
+int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
+ struct snd_compr_tstamp *tstamp);
+int wm_adsp_compr_copy(struct snd_compr_stream *stream,
+ char __user *buf, size_t count);
#endif
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index 7613d60d62ea..ec78b9da020f 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -26,6 +26,10 @@
#define WMFW_CTL_FLAG_WRITEABLE 0x0002
#define WMFW_CTL_FLAG_READABLE 0x0001
+/* Non-ALSA coefficient types start at 0x1000 */
+#define WMFW_CTL_TYPE_ACKED 0x1000 /* acked control */
+#define WMFW_CTL_TYPE_HOSTEVENT 0x1001 /* event control */
+
struct wmfw_header {
char magic[4];
__le32 len;