summaryrefslogtreecommitdiff
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/Kconfig8
-rw-r--r--sound/soc/codecs/Makefile2
-rw-r--r--sound/soc/codecs/ab8500-codec.c2
-rw-r--r--sound/soc/codecs/ak4104.c65
-rw-r--r--sound/soc/codecs/ak4535.c7
-rw-r--r--sound/soc/codecs/ak4642.c23
-rw-r--r--sound/soc/codecs/arizona.c25
-rw-r--r--sound/soc/codecs/arizona.h65
-rw-r--r--sound/soc/codecs/cs4271.c32
-rw-r--r--sound/soc/codecs/da7210.c13
-rw-r--r--sound/soc/codecs/da9055.c43
-rw-r--r--sound/soc/codecs/jz4740.c142
-rw-r--r--sound/soc/codecs/lm49453.c10
-rw-r--r--sound/soc/codecs/max9768.c7
-rw-r--r--sound/soc/codecs/max98088.c14
-rw-r--r--sound/soc/codecs/wm2200.c634
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm5102.c120
-rw-r--r--sound/soc/codecs/wm5110.c49
-rw-r--r--sound/soc/codecs/wm_adsp.c666
-rw-r--r--sound/soc/codecs/wm_adsp.h59
-rw-r--r--sound/soc/codecs/wmfw.h128
22 files changed, 1839 insertions, 277 deletions
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 1801a6517ddf..7f15a82d1b21 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -147,6 +147,13 @@ config SND_SOC_WM_HUBS
default y if SND_SOC_WM8993=y || SND_SOC_WM8994=y
default m if SND_SOC_WM8993=m || SND_SOC_WM8994=m
+config SND_SOC_WM_ADSP
+ tristate
+ default y if SND_SOC_WM5102=y
+ default y if SND_SOC_WM2200=y
+ default m if SND_SOC_WM5102=m
+ default m if SND_SOC_WM2200=m
+
config SND_SOC_AB8500_CODEC
tristate
@@ -230,6 +237,7 @@ config SND_SOC_CX20442
tristate
config SND_SOC_JZ4740_CODEC
+ select REGMAP_MMIO
tristate
config SND_SOC_L3
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index fff64db4bf29..998f5c528af1 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -63,6 +63,7 @@ snd-soc-twl6040-objs := twl6040.o
snd-soc-uda134x-objs := uda134x.o
snd-soc-uda1380-objs := uda1380.o
snd-soc-wl1273-objs := wl1273.o
+snd-soc-wm-adsp-objs := wm_adsp.o
snd-soc-wm0010-objs := wm0010.o
snd-soc-wm1250-ev1-objs := wm1250-ev1.o
snd-soc-wm2000-objs := wm2000.o
@@ -231,6 +232,7 @@ obj-$(CONFIG_SND_SOC_WM9090) += snd-soc-wm9090.o
obj-$(CONFIG_SND_SOC_WM9705) += snd-soc-wm9705.o
obj-$(CONFIG_SND_SOC_WM9712) += snd-soc-wm9712.o
obj-$(CONFIG_SND_SOC_WM9713) += snd-soc-wm9713.o
+obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index af547490b4f7..4d96090db662 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2356,7 +2356,7 @@ static int ab8500_codec_set_dai_tdm_slot(struct snd_soc_dai *dai,
return 0;
}
-struct snd_soc_dai_driver ab8500_codec_dai[] = {
+static struct snd_soc_dai_driver ab8500_codec_dai[] = {
{
.name = "ab8500-codec-dai.0",
.id = 0,
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 31d4483245d0..4b11b82b2273 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -15,6 +15,8 @@
#include <sound/soc.h>
#include <sound/initval.h>
#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <sound/asoundef.h>
/* AK4104 registers addresses */
@@ -98,14 +100,32 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
val = 0;
switch (params_rate(params)) {
+ case 22050:
+ val |= IEC958_AES3_CON_FS_22050;
+ break;
+ case 24000:
+ val |= IEC958_AES3_CON_FS_24000;
+ break;
+ case 32000:
+ val |= IEC958_AES3_CON_FS_32000;
+ break;
case 44100:
val |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
val |= IEC958_AES3_CON_FS_48000;
break;
- case 32000:
- val |= IEC958_AES3_CON_FS_32000;
+ case 88200:
+ val |= IEC958_AES3_CON_FS_88200;
+ break;
+ case 96000:
+ val |= IEC958_AES3_CON_FS_96000;
+ break;
+ case 176400:
+ val |= IEC958_AES3_CON_FS_176400;
+ break;
+ case 192000:
+ val |= IEC958_AES3_CON_FS_192000;
break;
default:
dev_err(codec->dev, "unsupported sampling rate\n");
@@ -186,6 +206,7 @@ static const struct regmap_config ak4104_regmap = {
static int ak4104_spi_probe(struct spi_device *spi)
{
+ struct device_node *np = spi->dev.of_node;
struct ak4104_private *ak4104;
unsigned int val;
int ret;
@@ -201,49 +222,59 @@ static int ak4104_spi_probe(struct spi_device *spi)
if (ak4104 == NULL)
return -ENOMEM;
- ak4104->regmap = regmap_init_spi(spi, &ak4104_regmap);
+ ak4104->regmap = devm_regmap_init_spi(spi, &ak4104_regmap);
if (IS_ERR(ak4104->regmap)) {
ret = PTR_ERR(ak4104->regmap);
return ret;
}
+ if (np) {
+ enum of_gpio_flags flags;
+ int gpio = of_get_named_gpio_flags(np, "reset-gpio", 0, &flags);
+
+ if (gpio_is_valid(gpio)) {
+ ret = devm_gpio_request_one(&spi->dev, gpio,
+ flags & OF_GPIO_ACTIVE_LOW ?
+ GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
+ "ak4104 reset");
+ if (ret < 0)
+ return ret;
+ }
+ }
+
/* read the 'reserved' register - according to the datasheet, it
* should contain 0x5b. Not a good way to verify the presence of
* the device, but there is no hardware ID register. */
ret = regmap_read(ak4104->regmap, AK4104_REG_RESERVED, &val);
if (ret != 0)
- goto err;
- if (val != AK4104_RESERVED_VAL) {
- ret = -ENODEV;
- goto err;
- }
+ return ret;
+ if (val != AK4104_RESERVED_VAL)
+ return -ENODEV;
spi_set_drvdata(spi, ak4104);
ret = snd_soc_register_codec(&spi->dev,
&soc_codec_device_ak4104, &ak4104_dai, 1);
- if (ret != 0)
- goto err;
-
- return 0;
-
-err:
- regmap_exit(ak4104->regmap);
return ret;
}
static int __devexit ak4104_spi_remove(struct spi_device *spi)
{
- struct ak4104_private *ak4101 = spi_get_drvdata(spi);
- regmap_exit(ak4101->regmap);
snd_soc_unregister_codec(&spi->dev);
return 0;
}
+static const struct of_device_id ak4104_of_match[] = {
+ { .compatible = "asahi-kasei,ak4104", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ak4104_of_match);
+
static struct spi_driver ak4104_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
+ .of_match_table = ak4104_of_match,
},
.probe = ak4104_spi_probe,
.remove = __devexit_p(ak4104_spi_remove),
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 618fdc30f73e..fc5581063b2d 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -447,7 +447,7 @@ static __devinit int ak4535_i2c_probe(struct i2c_client *i2c,
if (ak4535 == NULL)
return -ENOMEM;
- ak4535->regmap = regmap_init_i2c(i2c, &ak4535_regmap);
+ ak4535->regmap = devm_regmap_init_i2c(i2c, &ak4535_regmap);
if (IS_ERR(ak4535->regmap)) {
ret = PTR_ERR(ak4535->regmap);
dev_err(&i2c->dev, "Failed to init regmap: %d\n", ret);
@@ -458,18 +458,13 @@ static __devinit int ak4535_i2c_probe(struct i2c_client *i2c,
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_ak4535, &ak4535_dai, 1);
- if (ret != 0)
- regmap_exit(ak4535->regmap);
return ret;
}
static __devexit int ak4535_i2c_remove(struct i2c_client *client)
{
- struct ak4535_priv *ak4535 = i2c_get_clientdata(client);
-
snd_soc_unregister_codec(&client->dev);
- regmap_exit(ak4535->regmap);
return 0;
}
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index b3e24f289421..546466abb77f 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -194,12 +194,6 @@ static const struct snd_soc_dapm_route ak4642_intercon[] = {
{"LINEOUT Mixer", "DACL", "DAC"},
};
-/* codec private data */
-struct ak4642_priv {
- unsigned int sysclk;
- enum snd_soc_control_type control_type;
-};
-
/*
* ak4642 register cache
*/
@@ -468,10 +462,9 @@ static int ak4642_resume(struct snd_soc_codec *codec)
static int ak4642_probe(struct snd_soc_codec *codec)
{
- struct ak4642_priv *ak4642 = snd_soc_codec_get_drvdata(codec);
int ret;
- ret = snd_soc_codec_set_cache_io(codec, 8, 8, ak4642->control_type);
+ ret = snd_soc_codec_set_cache_io(codec, 8, 8, SND_SOC_I2C);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cache I/O: %d\n", ret);
return ret;
@@ -523,21 +516,9 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = {
static __devinit int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
- struct ak4642_priv *ak4642;
- int ret;
-
- ak4642 = devm_kzalloc(&i2c->dev, sizeof(struct ak4642_priv),
- GFP_KERNEL);
- if (!ak4642)
- return -ENOMEM;
-
- i2c_set_clientdata(i2c, ak4642);
- ak4642->control_type = SND_SOC_I2C;
-
- ret = snd_soc_register_codec(&i2c->dev,
+ return snd_soc_register_codec(&i2c->dev,
(struct snd_soc_codec_driver *)id->driver_data,
&ak4642_dai, 1);
- return ret;
}
static __devexit int ak4642_i2c_remove(struct i2c_client *client)
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index c03b65af3059..87cfaa3a6f0e 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -268,7 +268,7 @@ EXPORT_SYMBOL_GPL(arizona_out_ev);
static unsigned int arizona_sysclk_48k_rates[] = {
6144000,
12288000,
- 22579200,
+ 24576000,
49152000,
73728000,
98304000,
@@ -278,7 +278,7 @@ static unsigned int arizona_sysclk_48k_rates[] = {
static unsigned int arizona_sysclk_44k1_rates[] = {
5644800,
11289600,
- 24576000,
+ 22579200,
45158400,
67737600,
90316800,
@@ -380,6 +380,18 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
case 49152000:
val |= 3 << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
+ case 67737600:
+ case 73728000:
+ val |= 4 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 90316800:
+ case 98304000:
+ val |= 5 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
+ case 135475200:
+ case 147456000:
+ val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
+ break;
default:
return -EINVAL;
}
@@ -925,6 +937,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
bool ena;
int ret;
+ if (fll->fref == Fref && fll->fout == Fout)
+ return 0;
+
ret = regmap_read(arizona->regmap, fll->base + 1, &reg);
if (ret != 0) {
arizona_fll_err(fll, "Failed to read current state: %d\n",
@@ -970,6 +985,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
if (ena)
pm_runtime_put_autosuspend(arizona->dev);
+ fll->fref = Fref;
+ fll->fout = Fout;
+
return 0;
}
@@ -1002,6 +1020,9 @@ int arizona_set_fll(struct arizona_fll *fll, int source,
if (ret == 0)
arizona_fll_warn(fll, "Timed out waiting for lock\n");
+ fll->fref = Fref;
+ fll->fout = Fout;
+
return 0;
}
EXPORT_SYMBOL_GPL(arizona_set_fll);
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 36ec64946120..84c415d335bd 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -17,6 +17,8 @@
#include <sound/soc.h>
+#include "wm_adsp.h"
+
#define ARIZONA_CLK_SYSCLK 1
#define ARIZONA_CLK_ASYNCCLK 2
#define ARIZONA_CLK_OPCLK 3
@@ -46,15 +48,18 @@
#define ARIZONA_MIXER_VOL_SHIFT 1
#define ARIZONA_MIXER_VOL_WIDTH 7
-#define ARIZONA_MAX_DAI 3
+#define ARIZONA_MAX_DAI 4
+#define ARIZONA_MAX_ADSP 4
struct arizona;
+struct wm_adsp;
struct arizona_dai_priv {
int clk;
};
struct arizona_priv {
+ struct wm_adsp adsp[ARIZONA_MAX_ADSP];
struct arizona *arizona;
int sysclk;
int asyncclk;
@@ -89,19 +94,30 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
const struct snd_kcontrol_new name##_mux = \
SOC_DAPM_VALUE_ENUM("Route", name##_enum)
+#define ARIZONA_MUX_ENUMS(name, base_reg) \
+ static ARIZONA_MUX_ENUM_DECL(name##_enum, base_reg); \
+ static ARIZONA_MUX_CTL_DECL(name)
+
#define ARIZONA_MIXER_ENUMS(name, base_reg) \
- static ARIZONA_MUX_ENUM_DECL(name##_in1_enum, base_reg); \
- static ARIZONA_MUX_ENUM_DECL(name##_in2_enum, base_reg + 2); \
- static ARIZONA_MUX_ENUM_DECL(name##_in3_enum, base_reg + 4); \
- static ARIZONA_MUX_ENUM_DECL(name##_in4_enum, base_reg + 6); \
- static ARIZONA_MUX_CTL_DECL(name##_in1); \
- static ARIZONA_MUX_CTL_DECL(name##_in2); \
- static ARIZONA_MUX_CTL_DECL(name##_in3); \
- static ARIZONA_MUX_CTL_DECL(name##_in4)
+ ARIZONA_MUX_ENUMS(name##_in1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_in2, base_reg + 2); \
+ ARIZONA_MUX_ENUMS(name##_in3, base_reg + 4); \
+ ARIZONA_MUX_ENUMS(name##_in4, base_reg + 6)
+
+#define ARIZONA_DSP_AUX_ENUMS(name, base_reg) \
+ ARIZONA_MUX_ENUMS(name##_aux1, base_reg); \
+ ARIZONA_MUX_ENUMS(name##_aux2, base_reg + 8); \
+ ARIZONA_MUX_ENUMS(name##_aux3, base_reg + 16); \
+ ARIZONA_MUX_ENUMS(name##_aux4, base_reg + 24); \
+ ARIZONA_MUX_ENUMS(name##_aux5, base_reg + 32); \
+ ARIZONA_MUX_ENUMS(name##_aux6, base_reg + 40)
#define ARIZONA_MUX(name, ctrl) \
SND_SOC_DAPM_VALUE_MUX(name, SND_SOC_NOPM, 0, 0, ctrl)
+#define ARIZONA_MUX_WIDGETS(name, name_str) \
+ ARIZONA_MUX(name_str " Input", &name##_mux)
+
#define ARIZONA_MIXER_WIDGETS(name, name_str) \
ARIZONA_MUX(name_str " Input 1", &name##_in1_mux), \
ARIZONA_MUX(name_str " Input 2", &name##_in2_mux), \
@@ -109,6 +125,19 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
ARIZONA_MUX(name_str " Input 4", &name##_in4_mux), \
SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+#define ARIZONA_DSP_WIDGETS(name, name_str) \
+ ARIZONA_MIXER_WIDGETS(name##L, name_str "L"), \
+ ARIZONA_MIXER_WIDGETS(name##R, name_str "R"), \
+ ARIZONA_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ ARIZONA_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ ARIZONA_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ ARIZONA_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
+
+#define ARIZONA_MUX_ROUTES(name) \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Input")
+
#define ARIZONA_MIXER_ROUTES(widget, name) \
{ widget, NULL, name " Mixer" }, \
{ name " Mixer", NULL, name " Input 1" }, \
@@ -120,6 +149,22 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
ARIZONA_MIXER_INPUT_ROUTES(name " Input 3"), \
ARIZONA_MIXER_INPUT_ROUTES(name " Input 4")
+#define ARIZONA_DSP_ROUTES(name) \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ ARIZONA_MIXER_INPUT_ROUTES(name " Aux 6"), \
+ ARIZONA_MIXER_ROUTES(name, name "L"), \
+ ARIZONA_MIXER_ROUTES(name, name "R")
+
extern const struct soc_enum arizona_lhpf1_mode;
extern const struct soc_enum arizona_lhpf2_mode;
extern const struct soc_enum arizona_lhpf3_mode;
@@ -146,6 +191,8 @@ struct arizona_fll {
unsigned int vco_mult;
struct completion lock;
struct completion ok;
+ unsigned int fref;
+ unsigned int fout;
char lock_name[ARIZONA_FLL_NAME_LEN];
char clock_ok_name[ARIZONA_FLL_NAME_LEN];
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index f994af34f552..6ad3878db8fc 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -474,18 +474,28 @@ static int cs4271_probe(struct snd_soc_codec *codec)
struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
int ret;
int gpio_nreset = -EINVAL;
+ int amutec_eq_bmutec = 0;
#ifdef CONFIG_OF
- if (of_match_device(cs4271_dt_ids, codec->dev))
+ if (of_match_device(cs4271_dt_ids, codec->dev)) {
gpio_nreset = of_get_named_gpio(codec->dev->of_node,
"reset-gpio", 0);
+
+ if (!of_get_property(codec->dev->of_node,
+ "cirrus,amutec-eq-bmutec", NULL))
+ amutec_eq_bmutec = 1;
+ }
#endif
- if (cs4271plat && gpio_is_valid(cs4271plat->gpio_nreset))
- gpio_nreset = cs4271plat->gpio_nreset;
+ if (cs4271plat) {
+ if (gpio_is_valid(cs4271plat->gpio_nreset))
+ gpio_nreset = cs4271plat->gpio_nreset;
+
+ amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
+ }
if (gpio_nreset >= 0)
- if (gpio_request(gpio_nreset, "CS4271 Reset"))
+ if (devm_gpio_request(codec->dev, gpio_nreset, "CS4271 Reset"))
gpio_nreset = -EINVAL;
if (gpio_nreset >= 0) {
/* Reset codec */
@@ -528,6 +538,11 @@ static int cs4271_probe(struct snd_soc_codec *codec)
/* Power-up sequence requires 85 uS */
udelay(85);
+ if (amutec_eq_bmutec)
+ snd_soc_update_bits(codec, CS4271_MODE2,
+ CS4271_MODE2_MUTECAEQUB,
+ CS4271_MODE2_MUTECAEQUB);
+
return snd_soc_add_codec_controls(codec, cs4271_snd_controls,
ARRAY_SIZE(cs4271_snd_controls));
}
@@ -535,15 +550,10 @@ static int cs4271_probe(struct snd_soc_codec *codec)
static int cs4271_remove(struct snd_soc_codec *codec)
{
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
- int gpio_nreset;
-
- gpio_nreset = cs4271->gpio_nreset;
- if (gpio_is_valid(gpio_nreset)) {
+ if (gpio_is_valid(cs4271->gpio_nreset))
/* Set codec to the reset state */
- gpio_set_value(gpio_nreset, 0);
- gpio_free(gpio_nreset);
- }
+ gpio_set_value(cs4271->gpio_nreset, 0);
return 0;
};
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index af5db7080519..ab1ee5b734db 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1231,7 +1231,7 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, da7210);
- da7210->regmap = regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
+ da7210->regmap = devm_regmap_init_i2c(i2c, &da7210_regmap_config_i2c);
if (IS_ERR(da7210->regmap)) {
ret = PTR_ERR(da7210->regmap);
dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
@@ -1245,24 +1245,15 @@ static int __devinit da7210_i2c_probe(struct i2c_client *i2c,
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_da7210, &da7210_dai, 1);
- if (ret < 0) {
+ if (ret < 0)
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
- goto err_regmap;
- }
- return ret;
-
-err_regmap:
- regmap_exit(da7210->regmap);
return ret;
}
static int __devexit da7210_i2c_remove(struct i2c_client *client)
{
- struct da7210_priv *da7210 = i2c_get_clientdata(client);
-
snd_soc_unregister_codec(&client->dev);
- regmap_exit(da7210->regmap);
return 0;
}
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index f379b085c392..d3a6de2e757b 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -173,6 +173,7 @@
#define DA9055_AIF_FORMAT_I2S_MODE (0 << 0)
#define DA9055_AIF_FORMAT_LEFT_J (1 << 0)
#define DA9055_AIF_FORMAT_RIGHT_J (2 << 0)
+#define DA9055_AIF_FORMAT_DSP (3 << 0)
#define DA9055_AIF_WORD_S16_LE (0 << 2)
#define DA9055_AIF_WORD_S20_3LE (1 << 2)
#define DA9055_AIF_WORD_S24_LE (2 << 2)
@@ -752,6 +753,17 @@ static const struct snd_kcontrol_new da9055_dapm_mixoutr_controls[] = {
6, 1, 0),
};
+/* Headphone Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_hp_l_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_L_CTRL, 3, 1, 0);
+
+static const struct snd_kcontrol_new da9055_dapm_hp_r_control =
+SOC_DAPM_SINGLE("Switch", DA9055_HP_R_CTRL, 3, 1, 0);
+
+/* Lineout Output Enable */
+static const struct snd_kcontrol_new da9055_dapm_lineout_control =
+SOC_DAPM_SINGLE("Switch", DA9055_LINE_CTRL, 3, 1, 0);
+
/* DAPM widgets */
static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = {
/* Input Side */
@@ -816,6 +828,14 @@ static const struct snd_soc_dapm_widget da9055_dapm_widgets[] = {
&da9055_dapm_mixoutr_controls[0],
ARRAY_SIZE(da9055_dapm_mixoutr_controls)),
+ /* Output Enable Switches */
+ SND_SOC_DAPM_SWITCH("Headphone Left Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_hp_l_control),
+ SND_SOC_DAPM_SWITCH("Headphone Right Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_hp_r_control),
+ SND_SOC_DAPM_SWITCH("Lineout Enable", SND_SOC_NOPM, 0, 0,
+ &da9055_dapm_lineout_control),
+
/* Output PGAs */
SND_SOC_DAPM_PGA("MIXOUT Left", DA9055_MIXOUT_L_CTRL, 7, 0, NULL, 0),
SND_SOC_DAPM_PGA("MIXOUT Right", DA9055_MIXOUT_R_CTRL, 7, 0, NULL, 0),
@@ -901,17 +921,20 @@ static const struct snd_soc_dapm_route da9055_audio_map[] = {
{"Out Mixer Right", "DAC Right Switch", "DAC Right"},
{"MIXOUT Left", NULL, "Out Mixer Left"},
- {"Headphone Left", NULL, "MIXOUT Left"},
+ {"Headphone Left Enable", "Switch", "MIXOUT Left"},
+ {"Headphone Left", NULL, "Headphone Left Enable"},
{"Headphone Left", NULL, "Charge Pump"},
{"HPL", NULL, "Headphone Left"},
{"MIXOUT Right", NULL, "Out Mixer Right"},
- {"Headphone Right", NULL, "MIXOUT Right"},
+ {"Headphone Right Enable", "Switch", "MIXOUT Right"},
+ {"Headphone Right", NULL, "Headphone Right Enable"},
{"Headphone Right", NULL, "Charge Pump"},
{"HPR", NULL, "Headphone Right"},
{"MIXOUT Right", NULL, "Out Mixer Right"},
- {"Lineout", NULL, "MIXOUT Right"},
+ {"Lineout Enable", "Switch", "MIXOUT Right"},
+ {"Lineout", NULL, "Lineout Enable"},
{"LINE", NULL, "Lineout"},
};
@@ -1175,6 +1198,9 @@ static int da9055_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
case SND_SOC_DAIFMT_RIGHT_J:
aif_ctrl = DA9055_AIF_FORMAT_RIGHT_J;
break;
+ case SND_SOC_DAIFMT_DSP_A:
+ aif_ctrl = DA9055_AIF_FORMAT_DSP;
+ break;
default:
return -EINVAL;
}
@@ -1390,8 +1416,7 @@ static int da9055_probe(struct snd_soc_codec *codec)
DA9055_GAIN_RAMPING_EN, DA9055_GAIN_RAMPING_EN);
/*
- * There are two separate control bits for input and output mixers as
- * well as headphone and line outs.
+ * There are two separate control bits for input and output mixers.
* One to enable corresponding amplifier and other to enable its
* output. As amplifier bits are related to power control, they are
* being managed by DAPM while other (non power related) bits are
@@ -1407,14 +1432,6 @@ static int da9055_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, DA9055_MIXOUT_R_CTRL,
DA9055_MIXOUT_R_MIX_EN, DA9055_MIXOUT_R_MIX_EN);
- snd_soc_update_bits(codec, DA9055_HP_L_CTRL,
- DA9055_HP_L_AMP_OE, DA9055_HP_L_AMP_OE);
- snd_soc_update_bits(codec, DA9055_HP_R_CTRL,
- DA9055_HP_R_AMP_OE, DA9055_HP_R_AMP_OE);
-
- snd_soc_update_bits(codec, DA9055_LINE_CTRL,
- DA9055_LINE_AMP_OE, DA9055_LINE_AMP_OE);
-
/* Set this as per your system configuration */
snd_soc_write(codec, DA9055_PLL_CTRL, DA9055_PLL_INDIV_10_20_MHZ);
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 85d9cabe6d55..9ad1da36887e 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -16,6 +16,7 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/io.h>
+#include <linux/regmap.h>
#include <linux/delay.h>
@@ -24,9 +25,10 @@
#include <sound/pcm_params.h>
#include <sound/initval.h>
#include <sound/soc.h>
+#include <sound/tlv.h>
#define JZ4740_REG_CODEC_1 0x0
-#define JZ4740_REG_CODEC_2 0x1
+#define JZ4740_REG_CODEC_2 0x4
#define JZ4740_CODEC_1_LINE_ENABLE BIT(29)
#define JZ4740_CODEC_1_MIC_ENABLE BIT(28)
@@ -67,43 +69,36 @@
#define JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET 4
#define JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET 0
-static const uint32_t jz4740_codec_regs[] = {
- 0x021b2302, 0x00170803,
+static const struct reg_default jz4740_codec_reg_defaults[] = {
+ { JZ4740_REG_CODEC_1, 0x021b2302 },
+ { JZ4740_REG_CODEC_2, 0x00170803 },
};
struct jz4740_codec {
- void __iomem *base;
- struct resource *mem;
+ struct regmap *regmap;
};
-static unsigned int jz4740_codec_read(struct snd_soc_codec *codec,
- unsigned int reg)
-{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
- return readl(jz4740_codec->base + (reg << 2));
-}
-
-static int jz4740_codec_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int val)
-{
- struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
- u32 *cache = codec->reg_cache;
-
- cache[reg] = val;
- writel(val, jz4740_codec->base + (reg << 2));
+static const unsigned int jz4740_mic_tlv[] = {
+ TLV_DB_RANGE_HEAD(2),
+ 0, 2, TLV_DB_SCALE_ITEM(0, 600, 0),
+ 3, 3, TLV_DB_SCALE_ITEM(2000, 0, 0),
+};
- return 0;
-}
+static const DECLARE_TLV_DB_SCALE(jz4740_out_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(jz4740_in_tlv, -3450, 150, 0);
static const struct snd_kcontrol_new jz4740_codec_controls[] = {
- SOC_SINGLE("Master Playback Volume", JZ4740_REG_CODEC_2,
- JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0),
- SOC_SINGLE("Master Capture Volume", JZ4740_REG_CODEC_2,
- JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0),
+ SOC_SINGLE_TLV("Master Playback Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_HEADPHONE_VOLUME_OFFSET, 3, 0,
+ jz4740_out_tlv),
+ SOC_SINGLE_TLV("Master Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_INPUT_VOLUME_OFFSET, 31, 0,
+ jz4740_in_tlv),
SOC_SINGLE("Master Playback Switch", JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_HEADPHONE_DISABLE_OFFSET, 1, 1),
- SOC_SINGLE("Mic Capture Volume", JZ4740_REG_CODEC_2,
- JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0),
+ SOC_SINGLE_TLV("Mic Capture Volume", JZ4740_REG_CODEC_2,
+ JZ4740_CODEC_2_MIC_BOOST_GAIN_OFFSET, 3, 0,
+ jz4740_mic_tlv),
};
static const struct snd_kcontrol_new jz4740_codec_output_controls[] = {
@@ -163,8 +158,8 @@ static const struct snd_soc_dapm_route jz4740_codec_dapm_routes[] = {
static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
+ struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(dai->codec);
uint32_t val;
- struct snd_soc_codec *codec = dai->codec;
switch (params_rate(params)) {
case 8000:
@@ -200,7 +195,7 @@ static int jz4740_codec_hw_params(struct snd_pcm_substream *substream,
val <<= JZ4740_CODEC_2_SAMPLE_RATE_OFFSET;
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_2,
+ regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_2,
JZ4740_CODEC_2_SAMPLE_RATE_MASK, val);
return 0;
@@ -230,25 +225,23 @@ static struct snd_soc_dai_driver jz4740_codec_dai = {
.symmetric_rates = 1,
};
-static void jz4740_codec_wakeup(struct snd_soc_codec *codec)
+static void jz4740_codec_wakeup(struct regmap *regmap)
{
- int i;
- uint32_t *cache = codec->reg_cache;
-
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_RESET, JZ4740_CODEC_1_RESET);
udelay(2);
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SUSPEND | JZ4740_CODEC_1_RESET, 0);
- for (i = 0; i < ARRAY_SIZE(jz4740_codec_regs); ++i)
- jz4740_codec_write(codec, i, cache[i]);
+ regcache_sync(regmap);
}
static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+ struct regmap *regmap = jz4740_codec->regmap;
unsigned int mask;
unsigned int value;
@@ -261,12 +254,12 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
value = 0;
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
break;
case SND_SOC_BIAS_STANDBY:
/* The only way to clear the suspend flag is to reset the codec */
if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
- jz4740_codec_wakeup(codec);
+ jz4740_codec_wakeup(regmap);
mask = JZ4740_CODEC_1_VREF_DISABLE |
JZ4740_CODEC_1_VREF_AMP_DISABLE |
@@ -275,13 +268,14 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
JZ4740_CODEC_1_VREF_AMP_DISABLE |
JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M;
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
break;
case SND_SOC_BIAS_OFF:
mask = JZ4740_CODEC_1_SUSPEND;
value = JZ4740_CODEC_1_SUSPEND;
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1, mask, value);
+ regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value);
+ regcache_mark_dirty(regmap);
break;
default:
break;
@@ -294,7 +288,9 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec,
static int jz4740_codec_dev_probe(struct snd_soc_codec *codec)
{
- snd_soc_update_bits(codec, JZ4740_REG_CODEC_1,
+ struct jz4740_codec *jz4740_codec = snd_soc_codec_get_drvdata(codec);
+
+ regmap_update_bits(jz4740_codec->regmap, JZ4740_REG_CODEC_1,
JZ4740_CODEC_1_SW2_ENABLE, JZ4740_CODEC_1_SW2_ENABLE);
jz4740_codec_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
@@ -331,12 +327,7 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
.remove = jz4740_codec_dev_remove,
.suspend = jz4740_codec_suspend,
.resume = jz4740_codec_resume,
- .read = jz4740_codec_read,
- .write = jz4740_codec_write,
.set_bias_level = jz4740_codec_set_bias_level,
- .reg_cache_default = jz4740_codec_regs,
- .reg_word_size = sizeof(u32),
- .reg_cache_size = 2,
.controls = jz4740_codec_controls,
.num_controls = ARRAY_SIZE(jz4740_codec_controls),
@@ -346,11 +337,23 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
.num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
};
+static const struct regmap_config jz4740_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = JZ4740_REG_CODEC_2,
+
+ .reg_defaults = jz4740_codec_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(jz4740_codec_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+};
+
static int __devinit jz4740_codec_probe(struct platform_device *pdev)
{
int ret;
struct jz4740_codec *jz4740_codec;
struct resource *mem;
+ void __iomem *base;
jz4740_codec = devm_kzalloc(&pdev->dev, sizeof(*jz4740_codec),
GFP_KERNEL);
@@ -358,56 +361,29 @@ static int __devinit jz4740_codec_probe(struct platform_device *pdev)
return -ENOMEM;
mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!mem) {
- dev_err(&pdev->dev, "Failed to get mmio memory resource\n");
- ret = -ENOENT;
- goto err_out;
- }
-
- mem = request_mem_region(mem->start, resource_size(mem), pdev->name);
- if (!mem) {
- dev_err(&pdev->dev, "Failed to request mmio memory region\n");
- ret = -EBUSY;
- goto err_out;
- }
+ base = devm_request_and_ioremap(&pdev->dev, mem);
+ if (!base)
+ return -EBUSY;
- jz4740_codec->base = ioremap(mem->start, resource_size(mem));
- if (!jz4740_codec->base) {
- dev_err(&pdev->dev, "Failed to ioremap mmio memory\n");
- ret = -EBUSY;
- goto err_release_mem_region;
- }
- jz4740_codec->mem = mem;
+ jz4740_codec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &jz4740_codec_regmap_config);
+ if (IS_ERR(jz4740_codec->regmap))
+ return PTR_ERR(jz4740_codec->regmap);
platform_set_drvdata(pdev, jz4740_codec);
ret = snd_soc_register_codec(&pdev->dev,
&soc_codec_dev_jz4740_codec, &jz4740_codec_dai, 1);
- if (ret) {
+ if (ret)
dev_err(&pdev->dev, "Failed to register codec\n");
- goto err_iounmap;
- }
- return 0;
-
-err_iounmap:
- iounmap(jz4740_codec->base);
-err_release_mem_region:
- release_mem_region(mem->start, resource_size(mem));
-err_out:
return ret;
}
static int __devexit jz4740_codec_remove(struct platform_device *pdev)
{
- struct jz4740_codec *jz4740_codec = platform_get_drvdata(pdev);
- struct resource *mem = jz4740_codec->mem;
-
snd_soc_unregister_codec(&pdev->dev);
- iounmap(jz4740_codec->base);
- release_mem_region(mem->start, resource_size(mem));
-
platform_set_drvdata(pdev, NULL);
return 0;
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index 99b0a9dcff34..096b6aa87f0f 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1497,7 +1497,7 @@ static __devinit int lm49453_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, lm49453);
- lm49453->regmap = regmap_init_i2c(i2c, &lm49453_regmap_config);
+ lm49453->regmap = devm_regmap_init_i2c(i2c, &lm49453_regmap_config);
if (IS_ERR(lm49453->regmap)) {
ret = PTR_ERR(lm49453->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -1508,21 +1508,15 @@ static __devinit int lm49453_i2c_probe(struct i2c_client *i2c,
ret = snd_soc_register_codec(&i2c->dev,
&soc_codec_dev_lm49453,
lm49453_dai, ARRAY_SIZE(lm49453_dai));
- if (ret < 0) {
+ if (ret < 0)
dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
- regmap_exit(lm49453->regmap);
- return ret;
- }
return ret;
}
static int __devexit lm49453_i2c_remove(struct i2c_client *client)
{
- struct lm49453_priv *lm49453 = i2c_get_clientdata(client);
-
snd_soc_unregister_codec(&client->dev);
- regmap_exit(lm49453->regmap);
return 0;
}
diff --git a/sound/soc/codecs/max9768.c b/sound/soc/codecs/max9768.c
index 17b3ec2d05cb..a777de6a1b23 100644
--- a/sound/soc/codecs/max9768.c
+++ b/sound/soc/codecs/max9768.c
@@ -187,7 +187,7 @@ static int __devinit max9768_i2c_probe(struct i2c_client *client,
i2c_set_clientdata(client, max9768);
- max9768->regmap = regmap_init_i2c(client, &max9768_i2c_regmap_config);
+ max9768->regmap = devm_regmap_init_i2c(client, &max9768_i2c_regmap_config);
if (IS_ERR(max9768->regmap)) {
err = PTR_ERR(max9768->regmap);
goto err_gpio_free;
@@ -195,12 +195,10 @@ static int __devinit max9768_i2c_probe(struct i2c_client *client,
err = snd_soc_register_codec(&client->dev, &max9768_codec_driver, NULL, 0);
if (err)
- goto err_regmap_free;
+ goto err_gpio_free;
return 0;
- err_regmap_free:
- regmap_exit(max9768->regmap);
err_gpio_free:
if (gpio_is_valid(max9768->shdn_gpio))
gpio_free(max9768->shdn_gpio);
@@ -215,7 +213,6 @@ static int __devexit max9768_i2c_remove(struct i2c_client *client)
struct max9768 *max9768 = i2c_get_clientdata(client);
snd_soc_unregister_codec(&client->dev);
- regmap_exit(max9768->regmap);
if (gpio_is_valid(max9768->shdn_gpio))
gpio_free(max9768->shdn_gpio);
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index 3264a5169306..b858264235c4 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -2098,13 +2098,13 @@ static const struct i2c_device_id max98088_i2c_id[] = {
MODULE_DEVICE_TABLE(i2c, max98088_i2c_id);
static struct i2c_driver max98088_i2c_driver = {
- .driver = {
- .name = "max98088",
- .owner = THIS_MODULE,
- },
- .probe = max98088_i2c_probe,
- .remove = __devexit_p(max98088_i2c_remove),
- .id_table = max98088_i2c_id,
+ .driver = {
+ .name = "max98088",
+ .owner = THIS_MODULE,
+ },
+ .probe = max98088_i2c_probe,
+ .remove = max98088_i2c_remove,
+ .id_table = max98088_i2c_id,
};
module_i2c_driver(max98088_i2c_driver);
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index eab64a193989..06d4e612a164 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
+#include <linux/firmware.h>
#include <linux/gcd.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
@@ -32,6 +33,39 @@
#include <sound/wm2200.h>
#include "wm2200.h"
+#include "wmfw.h"
+
+#define WM2200_DSP_CONTROL_1 0x00
+#define WM2200_DSP_CONTROL_2 0x02
+#define WM2200_DSP_CONTROL_3 0x03
+#define WM2200_DSP_CONTROL_4 0x04
+#define WM2200_DSP_CONTROL_5 0x06
+#define WM2200_DSP_CONTROL_6 0x07
+#define WM2200_DSP_CONTROL_7 0x08
+#define WM2200_DSP_CONTROL_8 0x09
+#define WM2200_DSP_CONTROL_9 0x0A
+#define WM2200_DSP_CONTROL_10 0x0B
+#define WM2200_DSP_CONTROL_11 0x0C
+#define WM2200_DSP_CONTROL_12 0x0D
+#define WM2200_DSP_CONTROL_13 0x0F
+#define WM2200_DSP_CONTROL_14 0x10
+#define WM2200_DSP_CONTROL_15 0x11
+#define WM2200_DSP_CONTROL_16 0x12
+#define WM2200_DSP_CONTROL_17 0x13
+#define WM2200_DSP_CONTROL_18 0x14
+#define WM2200_DSP_CONTROL_19 0x16
+#define WM2200_DSP_CONTROL_20 0x17
+#define WM2200_DSP_CONTROL_21 0x18
+#define WM2200_DSP_CONTROL_22 0x1A
+#define WM2200_DSP_CONTROL_23 0x1B
+#define WM2200_DSP_CONTROL_24 0x1C
+#define WM2200_DSP_CONTROL_25 0x1E
+#define WM2200_DSP_CONTROL_26 0x20
+#define WM2200_DSP_CONTROL_27 0x21
+#define WM2200_DSP_CONTROL_28 0x22
+#define WM2200_DSP_CONTROL_29 0x23
+#define WM2200_DSP_CONTROL_30 0x24
+#define WM2200_DSP_CONTROL_31 0x26
/* The code assumes DCVDD is generated internally */
#define WM2200_NUM_CORE_SUPPLIES 2
@@ -64,6 +98,66 @@ struct wm2200_priv {
int sysclk;
};
+#define WM2200_DSP_RANGE_BASE (WM2200_MAX_REGISTER + 1)
+#define WM2200_DSP_SPACING 12288
+
+#define WM2200_DSP1_DM_BASE (WM2200_DSP_RANGE_BASE + (0 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_PM_BASE (WM2200_DSP_RANGE_BASE + (1 * WM2200_DSP_SPACING))
+#define WM2200_DSP1_ZM_BASE (WM2200_DSP_RANGE_BASE + (2 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_DM_BASE (WM2200_DSP_RANGE_BASE + (3 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_PM_BASE (WM2200_DSP_RANGE_BASE + (4 * WM2200_DSP_SPACING))
+#define WM2200_DSP2_ZM_BASE (WM2200_DSP_RANGE_BASE + (5 * WM2200_DSP_SPACING))
+
+static const struct regmap_range_cfg wm2200_ranges[] = {
+ /* DSP1 DM */
+ { .range_min = WM2200_DSP1_DM_BASE,
+ .range_max = WM2200_DSP1_DM_BASE + 12287,
+ .selector_reg = WM2200_DSP1_CONTROL_3,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_DM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_DM_0_SHIFT,
+ .window_start = WM2200_DSP1_DM_0, .window_len = 2048, },
+
+ /* DSP1 PM */
+ { .range_min = WM2200_DSP1_PM_BASE,
+ .range_max = WM2200_DSP1_PM_BASE + 12287,
+ .selector_reg = WM2200_DSP1_CONTROL_2,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_PM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_PM_0_SHIFT,
+ .window_start = WM2200_DSP1_PM_0, .window_len = 768, },
+
+ /* DSP1 ZM */
+ { .range_min = WM2200_DSP1_ZM_BASE,
+ .range_max = WM2200_DSP1_ZM_BASE + 2047,
+ .selector_reg = WM2200_DSP1_CONTROL_4,
+ .selector_mask = WM2200_DSP1_PAGE_BASE_ZM_0_MASK,
+ .selector_shift = WM2200_DSP1_PAGE_BASE_ZM_0_SHIFT,
+ .window_start = WM2200_DSP1_ZM_0, .window_len = 1024, },
+
+ /* DSP2 DM */
+ { .range_min = WM2200_DSP2_DM_BASE,
+ .range_max = WM2200_DSP2_DM_BASE + 4095,
+ .selector_reg = WM2200_DSP2_CONTROL_3,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_DM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_DM_0_SHIFT,
+ .window_start = WM2200_DSP2_DM_0, .window_len = 2048, },
+
+ /* DSP2 PM */
+ { .range_min = WM2200_DSP2_PM_BASE,
+ .range_max = WM2200_DSP2_PM_BASE + 11287,
+ .selector_reg = WM2200_DSP2_CONTROL_2,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_PM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_PM_0_SHIFT,
+ .window_start = WM2200_DSP2_PM_0, .window_len = 768, },
+
+ /* DSP2 ZM */
+ { .range_min = WM2200_DSP2_ZM_BASE,
+ .range_max = WM2200_DSP2_ZM_BASE + 2047,
+ .selector_reg = WM2200_DSP2_CONTROL_4,
+ .selector_mask = WM2200_DSP2_PAGE_BASE_ZM_0_MASK,
+ .selector_shift = WM2200_DSP2_PAGE_BASE_ZM_0_SHIFT,
+ .window_start = WM2200_DSP2_ZM_0, .window_len = 1024, },
+};
+
static struct reg_default wm2200_reg_defaults[] = {
{ 0x000B, 0x0000 }, /* R11 - Tone Generator 1 */
{ 0x0102, 0x0000 }, /* R258 - Clocking 3 */
@@ -407,6 +501,16 @@ static struct reg_default wm2200_reg_defaults[] = {
static bool wm2200_volatile_register(struct device *dev, unsigned int reg)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+ if ((reg >= wm2200_ranges[i].window_start &&
+ reg <= wm2200_ranges[i].window_start +
+ wm2200_ranges[i].window_len) ||
+ (reg >= wm2200_ranges[i].range_min &&
+ reg <= wm2200_ranges[i].range_max))
+ return true;
+
switch (reg) {
case WM2200_SOFTWARE_RESET:
case WM2200_DEVICE_REVISION:
@@ -423,6 +527,16 @@ static bool wm2200_volatile_register(struct device *dev, unsigned int reg)
static bool wm2200_readable_register(struct device *dev, unsigned int reg)
{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(wm2200_ranges); i++)
+ if ((reg >= wm2200_ranges[i].window_start &&
+ reg <= wm2200_ranges[i].window_start +
+ wm2200_ranges[i].window_len) ||
+ (reg >= wm2200_ranges[i].range_min &&
+ reg <= wm2200_ranges[i].range_max))
+ return true;
+
switch (reg) {
case WM2200_SOFTWARE_RESET:
case WM2200_DEVICE_REVISION:
@@ -873,6 +987,400 @@ static int wm2200_reset(struct wm2200_priv *wm2200)
}
}
+static int wm2200_dsp_load(struct snd_soc_codec *codec, int base)
+{
+ const struct firmware *firmware;
+ struct regmap *regmap = codec->control_data;
+ unsigned int pos = 0;
+ const struct wmfw_header *header;
+ const struct wmfw_adsp1_sizes *adsp1_sizes;
+ const struct wmfw_footer *footer;
+ const struct wmfw_region *region;
+ const char *file, *region_name;
+ char *text;
+ unsigned int dm, pm, zm, reg;
+ int regions = 0;
+ int ret, offset, type;
+
+ switch (base) {
+ case WM2200_DSP1_CONTROL_1:
+ file = "wm2200-dsp1.wmfw";
+ dm = WM2200_DSP1_DM_BASE;
+ pm = WM2200_DSP1_PM_BASE;
+ zm = WM2200_DSP1_ZM_BASE;
+ break;
+ case WM2200_DSP2_CONTROL_1:
+ file = "wm2200-dsp2.wmfw";
+ dm = WM2200_DSP2_DM_BASE;
+ pm = WM2200_DSP2_PM_BASE;
+ zm = WM2200_DSP2_ZM_BASE;
+ break;
+ default:
+ dev_err(codec->dev, "BASE %x\n", base);
+ BUG_ON(1);
+ return -EINVAL;
+ }
+
+ ret = request_firmware(&firmware, file, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request '%s'\n", file);
+ return ret;
+ }
+
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ if (pos >= firmware->size) {
+ dev_err(codec->dev, "%s: file too short, %d bytes\n",
+ file, firmware->size);
+ return -EINVAL;
+ }
+
+ header = (void*)&firmware->data[0];
+
+ if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+ dev_err(codec->dev, "%s: invalid magic\n", file);
+ return -EINVAL;
+ }
+
+ if (header->ver != 0) {
+ dev_err(codec->dev, "%s: unknown file format %d\n",
+ file, header->ver);
+ return -EINVAL;
+ }
+
+ if (le32_to_cpu(header->len) != sizeof(*header) +
+ sizeof(*adsp1_sizes) + sizeof(*footer)) {
+ dev_err(codec->dev, "%s: unexpected header length %d\n",
+ file, le32_to_cpu(header->len));
+ return -EINVAL;
+ }
+
+ if (header->core != WMFW_ADSP1) {
+ dev_err(codec->dev, "%s: invalid core %d\n",
+ file, header->core);
+ return -EINVAL;
+ }
+
+ adsp1_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp1_sizes[1]);
+
+ dev_dbg(codec->dev, "%s: %d DM, %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp1_sizes->dm),
+ le32_to_cpu(adsp1_sizes->pm), le32_to_cpu(adsp1_sizes->zm));
+
+ dev_dbg(codec->dev, "%s: timestamp %llu\n", file,
+ le64_to_cpu(footer->timestamp));
+
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*region)) {
+ region = (void *)&(firmware->data[pos]);
+ region_name = "Unknown";
+ reg = 0;
+ text = NULL;
+ offset = le32_to_cpu(region->offset) & 0xffffff;
+ type = be32_to_cpu(region->type) & 0xff;
+
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ region_name = "Firmware name";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_INFO_TEXT:
+ region_name = "Information";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "Absolute";
+ reg = offset;
+ break;
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ reg = pm + (offset * 3);
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ reg = dm + (offset * 2);
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ reg = zm + (offset * 2);
+ break;
+ default:
+ dev_warn(codec->dev,
+ "%s.%d: Unknown region type %x at %d(%x)\n",
+ file, regions, type, pos, pos);
+ break;
+ }
+
+ dev_dbg(codec->dev, "%s.%d: %d bytes at %d in %s\n", file,
+ regions, le32_to_cpu(region->len), offset,
+ region_name);
+
+ if (text) {
+ memcpy(text, region->data, le32_to_cpu(region->len));
+ dev_info(codec->dev, "%s: %s\n", file, text);
+ kfree(text);
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, region->data,
+ le32_to_cpu(region->len));
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+ file, regions,
+ le32_to_cpu(region->len), offset,
+ region_name, ret);
+ goto out;
+ }
+ }
+
+ pos += le32_to_cpu(region->len) + sizeof(*region);
+ regions++;
+ }
+
+ if (pos > firmware->size)
+ dev_warn(codec->dev, "%s.%d: %d bytes at end of file\n",
+ file, regions, pos - firmware->size);
+
+out:
+ release_firmware(firmware);
+
+ return ret;
+}
+
+static int wm2200_setup_algs(struct snd_soc_codec *codec, int base)
+{
+ struct regmap *regmap = codec->control_data;
+ struct wmfw_adsp1_id_hdr id;
+ struct wmfw_adsp1_alg_hdr *alg;
+ size_t algs;
+ int zm, dm, pm, ret, i;
+ __be32 val;
+
+ switch (base) {
+ case WM2200_DSP1_CONTROL_1:
+ dm = WM2200_DSP1_DM_BASE;
+ pm = WM2200_DSP1_PM_BASE;
+ zm = WM2200_DSP1_ZM_BASE;
+ break;
+ case WM2200_DSP2_CONTROL_1:
+ dm = WM2200_DSP2_DM_BASE;
+ pm = WM2200_DSP2_PM_BASE;
+ zm = WM2200_DSP2_ZM_BASE;
+ break;
+ default:
+ dev_err(codec->dev, "BASE %x\n", base);
+ BUG_ON(1);
+ return -EINVAL;
+ }
+
+ ret = regmap_raw_read(regmap, dm, &id, sizeof(id));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ algs = be32_to_cpu(id.algs);
+ dev_info(codec->dev, "Firmware: %x v%d.%d.%d, %d algorithms\n",
+ be32_to_cpu(id.fw.id),
+ (be32_to_cpu(id.fw.ver) & 0xff000) >> 16,
+ (be32_to_cpu(id.fw.ver) & 0xff00) >> 8,
+ be32_to_cpu(id.fw.ver) & 0xff,
+ algs);
+
+ /* Read the terminator first to validate the length */
+ ret = regmap_raw_read(regmap, dm +
+ (sizeof(id) + (algs * sizeof(*alg))) / 2,
+ &val, sizeof(val));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read algorithm list end: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (be32_to_cpu(val) != 0xbedead)
+ dev_warn(codec->dev, "Algorithm list end %x 0x%x != 0xbeadead\n",
+ (sizeof(id) + (algs * sizeof(*alg))) / 2,
+ be32_to_cpu(val));
+
+ alg = kzalloc(sizeof(*alg) * algs, GFP_KERNEL);
+ if (!alg)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(regmap, dm + (sizeof(id) / 2),
+ alg, algs * sizeof(*alg));
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to read algorithm list: %d\n",
+ ret);
+ goto out;
+ }
+
+ for (i = 0; i < algs; i++) {
+ dev_info(codec->dev, "%d: ID %x v%d.%d.%d\n",
+ i, be32_to_cpu(alg[i].alg.id),
+ (be32_to_cpu(alg[i].alg.ver) & 0xff000) >> 16,
+ (be32_to_cpu(alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(alg[i].alg.ver) & 0xff);
+ }
+
+out:
+ kfree(alg);
+ return ret;
+}
+
+static int wm2200_load_coeff(struct snd_soc_codec *codec, int base)
+{
+ struct regmap *regmap = codec->control_data;
+ struct wmfw_coeff_hdr *hdr;
+ struct wmfw_coeff_item *blk;
+ const struct firmware *firmware;
+ const char *file, *region_name;
+ int ret, dm, pm, zm, pos, blocks, type, offset, reg;
+
+ switch (base) {
+ case WM2200_DSP1_CONTROL_1:
+ file = "wm2200-dsp1.bin";
+ dm = WM2200_DSP1_DM_BASE;
+ pm = WM2200_DSP1_PM_BASE;
+ zm = WM2200_DSP1_ZM_BASE;
+ break;
+ case WM2200_DSP2_CONTROL_1:
+ file = "wm2200-dsp2.bin";
+ dm = WM2200_DSP2_DM_BASE;
+ pm = WM2200_DSP2_PM_BASE;
+ zm = WM2200_DSP2_ZM_BASE;
+ break;
+ default:
+ dev_err(codec->dev, "BASE %x\n", base);
+ BUG_ON(1);
+ return -EINVAL;
+ }
+
+ ret = request_firmware(&firmware, file, codec->dev);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to request '%s'\n", file);
+ return ret;
+ }
+
+ if (sizeof(*hdr) >= firmware->size) {
+ dev_err(codec->dev, "%s: file too short, %d bytes\n",
+ file, firmware->size);
+ return -EINVAL;
+ }
+
+ hdr = (void*)&firmware->data[0];
+ if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+ dev_err(codec->dev, "%s: invalid magic\n", file);
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "%s: v%d.%d.%d\n", file,
+ (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+ (le32_to_cpu(hdr->ver) >> 8) & 0xff,
+ le32_to_cpu(hdr->ver) & 0xff);
+
+ pos = le32_to_cpu(hdr->len);
+
+ blocks = 0;
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*blk)) {
+ blk = (void*)(&firmware->data[pos]);
+
+ type = be32_to_cpu(blk->type) & 0xff;
+ offset = le32_to_cpu(blk->offset) & 0xffffff;
+
+ dev_dbg(codec->dev, "%s.%d: %x v%d.%d.%d\n",
+ file, blocks, le32_to_cpu(blk->id),
+ (le32_to_cpu(blk->ver) >> 16) & 0xff,
+ (le32_to_cpu(blk->ver) >> 8) & 0xff,
+ le32_to_cpu(blk->ver) & 0xff);
+ dev_dbg(codec->dev, "%s.%d: %d bytes at 0x%x in %x\n",
+ file, blocks, le32_to_cpu(blk->len), offset, type);
+
+ reg = 0;
+ region_name = "Unknown";
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ case WMFW_INFO_TEXT:
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "register";
+ reg = offset;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown region type %x\n", type);
+ break;
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, blk->data,
+ le32_to_cpu(blk->len));
+ if (ret != 0) {
+ dev_err(codec->dev,
+ "%s.%d: Failed to write to %x in %s\n",
+ file, blocks, reg, region_name);
+ }
+ }
+
+ pos += le32_to_cpu(blk->len) + sizeof(*blk);
+ blocks++;
+ }
+
+ if (pos > firmware->size)
+ dev_warn(codec->dev, "%s.%d: %d bytes at end of file\n",
+ file, blocks, pos - firmware->size);
+
+ return 0;
+}
+
+static int wm2200_dsp_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ int base = w->reg - WM2200_DSP_CONTROL_30;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ ret = wm2200_dsp_load(codec, base);
+ if (ret != 0)
+ return ret;
+
+ ret = wm2200_setup_algs(codec, base);
+ if (ret != 0)
+ return ret;
+
+ ret = wm2200_load_coeff(codec, base);
+ if (ret != 0)
+ return ret;
+
+ /* Start the core running */
+ snd_soc_update_bits(codec, w->reg,
+ WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
+ WM2200_DSP1_CORE_ENA | WM2200_DSP1_START);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Halt the core */
+ snd_soc_update_bits(codec, w->reg,
+ WM2200_DSP1_CORE_ENA | WM2200_DSP1_START,
+ 0);
+
+ snd_soc_update_bits(codec, base + WM2200_DSP_CONTROL_19,
+ WM2200_DSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static DECLARE_TLV_DB_SCALE(in_tlv, -6300, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
@@ -880,7 +1388,7 @@ static DECLARE_TLV_DB_SCALE(out_tlv, -6400, 100, 0);
static const char *wm2200_mixer_texts[] = {
"None",
"Tone Generator",
- "AEC loopback",
+ "AEC Loopback",
"IN1L",
"IN1R",
"IN2L",
@@ -976,6 +1484,20 @@ static int wm2200_mixer_values[] = {
static WM2200_MUX_CTL_DECL(name##_in3); \
static WM2200_MUX_CTL_DECL(name##_in4)
+#define WM2200_DSP_ENUMS(name, base_reg) \
+ static WM2200_MUX_ENUM_DECL(name##_aux1_enum, base_reg); \
+ static WM2200_MUX_ENUM_DECL(name##_aux2_enum, base_reg + 1); \
+ static WM2200_MUX_ENUM_DECL(name##_aux3_enum, base_reg + 2); \
+ static WM2200_MUX_ENUM_DECL(name##_aux4_enum, base_reg + 3); \
+ static WM2200_MUX_ENUM_DECL(name##_aux5_enum, base_reg + 4); \
+ static WM2200_MUX_ENUM_DECL(name##_aux6_enum, base_reg + 5); \
+ static WM2200_MUX_CTL_DECL(name##_aux1); \
+ static WM2200_MUX_CTL_DECL(name##_aux2); \
+ static WM2200_MUX_CTL_DECL(name##_aux3); \
+ static WM2200_MUX_CTL_DECL(name##_aux4); \
+ static WM2200_MUX_CTL_DECL(name##_aux5); \
+ static WM2200_MUX_CTL_DECL(name##_aux6);
+
static const struct snd_kcontrol_new wm2200_snd_controls[] = {
SOC_SINGLE("IN1 High Performance Switch", WM2200_IN1L_CONTROL,
WM2200_IN1_OSR_SHIFT, 1, 0),
@@ -1051,6 +1573,9 @@ WM2200_MIXER_ENUMS(DSP1R, WM2200_DSP1RMIX_INPUT_1_SOURCE);
WM2200_MIXER_ENUMS(DSP2L, WM2200_DSP2LMIX_INPUT_1_SOURCE);
WM2200_MIXER_ENUMS(DSP2R, WM2200_DSP2RMIX_INPUT_1_SOURCE);
+WM2200_DSP_ENUMS(DSP1, WM2200_DSP1AUX1MIX_INPUT_1_SOURCE);
+WM2200_DSP_ENUMS(DSP2, WM2200_DSP2AUX1MIX_INPUT_1_SOURCE);
+
WM2200_MIXER_ENUMS(LHPF1, WM2200_LHPF1MIX_INPUT_1_SOURCE);
WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
@@ -1064,8 +1589,19 @@ WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
WM2200_MUX(name_str " Input 4", &name##_in4_mux), \
SND_SOC_DAPM_MIXER(name_str " Mixer", SND_SOC_NOPM, 0, 0, NULL, 0)
+#define WM2200_DSP_WIDGETS(name, name_str) \
+ WM2200_MIXER_WIDGETS(name##L, name_str "L"), \
+ WM2200_MIXER_WIDGETS(name##R, name_str "R"), \
+ WM2200_MUX(name_str " Aux 1", &name##_aux1_mux), \
+ WM2200_MUX(name_str " Aux 2", &name##_aux2_mux), \
+ WM2200_MUX(name_str " Aux 3", &name##_aux3_mux), \
+ WM2200_MUX(name_str " Aux 4", &name##_aux4_mux), \
+ WM2200_MUX(name_str " Aux 5", &name##_aux5_mux), \
+ WM2200_MUX(name_str " Aux 6", &name##_aux6_mux)
+
#define WM2200_MIXER_INPUT_ROUTES(name) \
{ name, "Tone Generator", "Tone Generator" }, \
+ { name, "AEC Loopback", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
{ name, "IN2L", "IN2L PGA" }, \
@@ -1106,6 +1642,33 @@ WM2200_MIXER_ENUMS(LHPF2, WM2200_LHPF2MIX_INPUT_1_SOURCE);
WM2200_MIXER_INPUT_ROUTES(name " Input 3"), \
WM2200_MIXER_INPUT_ROUTES(name " Input 4")
+#define WM2200_DSP_AUX_ROUTES(name) \
+ { name, NULL, name " Aux 1" }, \
+ { name, NULL, name " Aux 2" }, \
+ { name, NULL, name " Aux 3" }, \
+ { name, NULL, name " Aux 4" }, \
+ { name, NULL, name " Aux 5" }, \
+ { name, NULL, name " Aux 6" }, \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 1"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 2"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 3"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 4"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 5"), \
+ WM2200_MIXER_INPUT_ROUTES(name " Aux 6")
+
+static const char *wm2200_aec_loopback_texts[] = {
+ "OUT1L", "OUT1R", "OUT2L", "OUT2R",
+};
+
+static const struct soc_enum wm2200_aec_loopback =
+ SOC_ENUM_SINGLE(WM2200_DAC_AEC_CONTROL_1,
+ WM2200_AEC_LOOPBACK_SRC_SHIFT,
+ ARRAY_SIZE(wm2200_aec_loopback_texts),
+ wm2200_aec_loopback_texts);
+
+static const struct snd_kcontrol_new wm2200_aec_loopback_mux =
+ SOC_DAPM_ENUM("AEC Loopback", wm2200_aec_loopback);
+
static const struct snd_soc_dapm_widget wm2200_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("SYSCLK", WM2200_CLOCKING_3, WM2200_SYSCLK_ENA_SHIFT, 0,
NULL, 0),
@@ -1165,8 +1728,12 @@ SND_SOC_DAPM_PGA("LHPF1", WM2200_HPLPF1_1, WM2200_LHPF1_ENA_SHIFT, 0,
SND_SOC_DAPM_PGA("LHPF2", WM2200_HPLPF2_1, WM2200_LHPF2_ENA_SHIFT, 0,
NULL, 0),
-SND_SOC_DAPM_PGA_E("DSP1", SND_SOC_NOPM, 0, 0, NULL, 0, NULL, 0),
-SND_SOC_DAPM_PGA_E("DSP2", SND_SOC_NOPM, 1, 0, NULL, 0, NULL, 0),
+SND_SOC_DAPM_PGA_E("DSP1", WM2200_DSP1_CONTROL_30, WM2200_DSP1_SYS_ENA_SHIFT,
+ 0, NULL, 0, wm2200_dsp_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+SND_SOC_DAPM_PGA_E("DSP2", WM2200_DSP2_CONTROL_30, WM2200_DSP2_SYS_ENA_SHIFT,
+ 0, NULL, 0, wm2200_dsp_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
SND_SOC_DAPM_AIF_OUT("AIF1TX1", "Capture", 0,
WM2200_AUDIO_IF_1_22, WM2200_AIF1TX1_ENA_SHIFT, 0),
@@ -1181,6 +1748,9 @@ SND_SOC_DAPM_AIF_OUT("AIF1TX5", "Capture", 4,
SND_SOC_DAPM_AIF_OUT("AIF1TX6", "Capture", 5,
WM2200_AUDIO_IF_1_22, WM2200_AIF1TX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_MUX("AEC Loopback", WM2200_DAC_AEC_CONTROL_1,
+ WM2200_AEC_LOOPBACK_ENA_SHIFT, 0, &wm2200_aec_loopback_mux),
+
SND_SOC_DAPM_PGA_S("OUT1L", 0, WM2200_OUTPUT_ENABLES,
WM2200_OUT1L_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA_S("OUT1R", 0, WM2200_OUTPUT_ENABLES,
@@ -1231,10 +1801,8 @@ WM2200_MIXER_WIDGETS(EQR, "EQR"),
WM2200_MIXER_WIDGETS(LHPF1, "LHPF1"),
WM2200_MIXER_WIDGETS(LHPF2, "LHPF2"),
-WM2200_MIXER_WIDGETS(DSP1L, "DSP1L"),
-WM2200_MIXER_WIDGETS(DSP1R, "DSP1R"),
-WM2200_MIXER_WIDGETS(DSP2L, "DSP2L"),
-WM2200_MIXER_WIDGETS(DSP2R, "DSP2R"),
+WM2200_DSP_WIDGETS(DSP1, "DSP1"),
+WM2200_DSP_WIDGETS(DSP2, "DSP2"),
WM2200_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
WM2200_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
@@ -1326,11 +1894,19 @@ static const struct snd_soc_dapm_route wm2200_dapm_routes[] = {
{ "SPK", NULL, "OUT2L" },
{ "SPK", NULL, "OUT2R" },
+ { "AEC Loopback", "OUT1L", "OUT1L" },
+ { "AEC Loopback", "OUT1R", "OUT1R" },
+ { "AEC Loopback", "OUT2L", "OUT2L" },
+ { "AEC Loopback", "OUT2R", "OUT2R" },
+
WM2200_MIXER_ROUTES("DSP1", "DSP1L"),
WM2200_MIXER_ROUTES("DSP1", "DSP1R"),
WM2200_MIXER_ROUTES("DSP2", "DSP2L"),
WM2200_MIXER_ROUTES("DSP2", "DSP2R"),
+ WM2200_DSP_AUX_ROUTES("DSP1"),
+ WM2200_DSP_AUX_ROUTES("DSP2"),
+
WM2200_MIXER_ROUTES("OUT1L", "OUT1L"),
WM2200_MIXER_ROUTES("OUT1R", "OUT1R"),
WM2200_MIXER_ROUTES("OUT2L", "OUT2L"),
@@ -1968,12 +2544,15 @@ static const struct regmap_config wm2200_regmap = {
.reg_bits = 16,
.val_bits = 16,
- .max_register = WM2200_MAX_REGISTER,
+ .max_register = WM2200_MAX_REGISTER + (ARRAY_SIZE(wm2200_ranges) *
+ WM2200_DSP_SPACING),
.reg_defaults = wm2200_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(wm2200_reg_defaults),
.volatile_reg = wm2200_volatile_register,
.readable_reg = wm2200_readable_register,
.cache_type = REGCACHE_RBTREE,
+ .ranges = wm2200_ranges,
+ .num_ranges = ARRAY_SIZE(wm2200_ranges),
};
static const unsigned int wm2200_dig_vu[] = {
@@ -2011,7 +2590,7 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
wm2200->dev = &i2c->dev;
init_completion(&wm2200->fll_lock);
- wm2200->regmap = regmap_init_i2c(i2c, &wm2200_regmap);
+ wm2200->regmap = devm_regmap_init_i2c(i2c, &wm2200_regmap);
if (IS_ERR(wm2200->regmap)) {
ret = PTR_ERR(wm2200->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -2027,8 +2606,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
for (i = 0; i < ARRAY_SIZE(wm2200->core_supplies); i++)
wm2200->core_supplies[i].supply = wm2200_core_supply_names[i];
- ret = regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm2200->core_supplies),
- wm2200->core_supplies);
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(wm2200->core_supplies),
+ wm2200->core_supplies);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to request core supplies: %d\n",
ret);
@@ -2044,8 +2624,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
}
if (wm2200->pdata.ldo_ena) {
- ret = gpio_request_one(wm2200->pdata.ldo_ena,
- GPIOF_OUT_INIT_HIGH, "WM2200 LDOENA");
+ ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.ldo_ena,
+ GPIOF_OUT_INIT_HIGH,
+ "WM2200 LDOENA");
if (ret < 0) {
dev_err(&i2c->dev, "Failed to request LDOENA %d: %d\n",
wm2200->pdata.ldo_ena, ret);
@@ -2055,8 +2636,9 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
}
if (wm2200->pdata.reset) {
- ret = gpio_request_one(wm2200->pdata.reset,
- GPIOF_OUT_INIT_HIGH, "WM2200 /RESET");
+ ret = devm_gpio_request_one(&i2c->dev, wm2200->pdata.reset,
+ GPIOF_OUT_INIT_HIGH,
+ "WM2200 /RESET");
if (ret < 0) {
dev_err(&i2c->dev, "Failed to request /RESET %d: %d\n",
wm2200->pdata.reset, ret);
@@ -2166,23 +2748,16 @@ static __devinit int wm2200_i2c_probe(struct i2c_client *i2c,
err_pm_runtime:
pm_runtime_disable(&i2c->dev);
err_reset:
- if (wm2200->pdata.reset) {
+ if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
- gpio_free(wm2200->pdata.reset);
- }
err_ldo:
- if (wm2200->pdata.ldo_ena) {
+ if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
- gpio_free(wm2200->pdata.ldo_ena);
- }
err_enable:
regulator_bulk_disable(ARRAY_SIZE(wm2200->core_supplies),
wm2200->core_supplies);
err_core:
- regulator_bulk_free(ARRAY_SIZE(wm2200->core_supplies),
- wm2200->core_supplies);
err_regmap:
- regmap_exit(wm2200->regmap);
err:
return ret;
}
@@ -2194,17 +2769,10 @@ static __devexit int wm2200_i2c_remove(struct i2c_client *i2c)
snd_soc_unregister_codec(&i2c->dev);
if (i2c->irq)
free_irq(i2c->irq, wm2200);
- if (wm2200->pdata.reset) {
+ if (wm2200->pdata.reset)
gpio_set_value_cansleep(wm2200->pdata.reset, 0);
- gpio_free(wm2200->pdata.reset);
- }
- if (wm2200->pdata.ldo_ena) {
+ if (wm2200->pdata.ldo_ena)
gpio_set_value_cansleep(wm2200->pdata.ldo_ena, 0);
- gpio_free(wm2200->pdata.ldo_ena);
- }
- regulator_bulk_free(ARRAY_SIZE(wm2200->core_supplies),
- wm2200->core_supplies);
- regmap_exit(wm2200->regmap);
return 0;
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 7f567585832e..9f57996ff272 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -1233,7 +1233,7 @@ static const struct snd_soc_dapm_route wm5100_dapm_routes[] = {
{ "PWM2", NULL, "PWM2 Driver" },
};
-static const __devinitconst struct reg_default wm5100_reva_patches[] = {
+static const struct reg_default wm5100_reva_patches[] = {
{ WM5100_AUDIO_IF_1_10, 0 },
{ WM5100_AUDIO_IF_1_11, 1 },
{ WM5100_AUDIO_IF_1_12, 2 },
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 7394e73fa43c..53793b1849f7 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -31,6 +31,7 @@
#include "arizona.h"
#include "wm5102.h"
+#include "wm_adsp.h"
struct wm5102_priv {
struct arizona_priv core;
@@ -42,6 +43,13 @@ static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
static DECLARE_TLV_DB_SCALE(noise_tlv, 0, 600, 0);
+static const struct wm_adsp_region wm5102_dsp1_regions[] = {
+ { .type = WMFW_ADSP2_PM, .base = 0x100000 },
+ { .type = WMFW_ADSP2_ZM, .base = 0x180000 },
+ { .type = WMFW_ADSP2_XM, .base = 0x190000 },
+ { .type = WMFW_ADSP2_YM, .base = 0x1a8000 },
+};
+
static const struct reg_default wm5102_sysclk_reva_patch[] = {
{ 0x3000, 0x2225 },
{ 0x3001, 0x3a03 },
@@ -687,6 +695,9 @@ ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1L", ARIZONA_DSP1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("DSP1R", ARIZONA_DSP1RMIX_INPUT_1_SOURCE),
+
SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
@@ -708,14 +719,6 @@ ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
-SOC_SINGLE("HPOUT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_1L,
- ARIZONA_OUT1_OSR_SHIFT, 1, 0),
-SOC_SINGLE("OUT2 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_2L,
- ARIZONA_OUT2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("EPOUT High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_3L,
- ARIZONA_OUT3_OSR_SHIFT, 1, 0),
-SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
- ARIZONA_OUT4_OSR_SHIFT, 1, 0),
SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
ARIZONA_OUT5_OSR_SHIFT, 1, 0),
@@ -745,17 +748,6 @@ SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_RANGE_TLV("HPOUT1 Volume", ARIZONA_OUTPUT_PATH_CONFIG_1L,
- ARIZONA_OUTPUT_PATH_CONFIG_1R,
- ARIZONA_OUT1L_PGA_VOL_SHIFT,
- 0x34, 0x40, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("OUT2 Volume", ARIZONA_OUTPUT_PATH_CONFIG_2L,
- ARIZONA_OUTPUT_PATH_CONFIG_2R,
- ARIZONA_OUT2L_PGA_VOL_SHIFT,
- 0x34, 0x40, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("EPOUT Volume", ARIZONA_OUTPUT_PATH_CONFIG_3L,
- ARIZONA_OUT3L_PGA_VOL_SHIFT, 0x34, 0x40, 0, ana_tlv),
-
SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
@@ -819,11 +811,15 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
+ARIZONA_DSP_AUX_ENUMS(DSP1, ARIZONA_DSP1AUX1MIX_INPUT_1_SOURCE);
static const char *wm5102_aec_loopback_texts[] = {
"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "EPOUT",
@@ -864,6 +860,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"),
SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
SND_SOC_DAPM_INPUT("IN1L"),
SND_SOC_DAPM_INPUT("IN1R"),
@@ -894,9 +891,9 @@ SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
- ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_MICB2_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
- ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+ ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
ARIZONA_NOISE_GEN_ENA_SHIFT, 0, NULL, 0),
@@ -996,6 +993,8 @@ SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+ARIZONA_DSP_WIDGETS(DSP1, "DSP1"),
+
SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
ARIZONA_AEC_LOOPBACK_ENA, 0, &wm5102_aec_loopback_mux),
@@ -1071,10 +1070,12 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
-ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
-ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
-ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
-ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+WM_ADSP2("DSP1", 0),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1094,6 +1095,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
{ name, "Noise Generator", "Noise Generator" }, \
{ name, "Tone Generator 1", "Tone Generator 1" }, \
{ name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
{ name, "AEC", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
@@ -1127,7 +1129,13 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
{ name, "ASRC1L", "ASRC1L" }, \
{ name, "ASRC1R", "ASRC1R" }, \
{ name, "ASRC2L", "ASRC2L" }, \
- { name, "ASRC2R", "ASRC2R" }
+ { name, "ASRC2R", "ASRC2R" }, \
+ { name, "DSP1.1", "DSP1" }, \
+ { name, "DSP1.2", "DSP1" }, \
+ { name, "DSP1.3", "DSP1" }, \
+ { name, "DSP1.4", "DSP1" }, \
+ { name, "DSP1.5", "DSP1" }, \
+ { name, "DSP1.6", "DSP1" }
static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "AIF2 Capture", NULL, "DBVDD2" },
@@ -1213,6 +1221,11 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "IN3L PGA", NULL, "IN3L" },
{ "IN3R PGA", NULL, "IN3R" },
+ { "ASRC1L", NULL, "ASRC1L Input" },
+ { "ASRC1R", NULL, "ASRC1R Input" },
+ { "ASRC2L", NULL, "ASRC2L Input" },
+ { "ASRC2R", NULL, "ASRC2R Input" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1255,10 +1268,12 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
- ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
- ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
- ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+ ARIZONA_MUX_ROUTES("ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R"),
+
+ ARIZONA_DSP_ROUTES("DSP1"),
{ "AEC Loopback", "HPOUT1L", "OUT1L" },
{ "AEC Loopback", "HPOUT1R", "OUT1R" },
@@ -1377,9 +1392,28 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
static int wm5102_codec_probe(struct snd_soc_codec *codec)
{
struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
codec->control_data = priv->core.arizona->regmap;
- return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+
+ ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ if (ret != 0)
+ return ret;
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm5102_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
}
#define WM5102_DIG_VU 0x0200
@@ -1406,6 +1440,7 @@ static unsigned int wm5102_digital_vu[] = {
static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
.probe = wm5102_codec_probe,
+ .remove = wm5102_codec_remove,
.idle_bias_off = true,
@@ -1424,7 +1459,7 @@ static int __devinit wm5102_probe(struct platform_device *pdev)
{
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct wm5102_priv *wm5102;
- int i;
+ int i, ret;
wm5102 = devm_kzalloc(&pdev->dev, sizeof(struct wm5102_priv),
GFP_KERNEL);
@@ -1434,6 +1469,19 @@ static int __devinit wm5102_probe(struct platform_device *pdev)
wm5102->core.arizona = arizona;
+ wm5102->core.adsp[0].part = "wm5102";
+ wm5102->core.adsp[0].num = 1;
+ wm5102->core.adsp[0].type = WMFW_ADSP2;
+ wm5102->core.adsp[0].base = ARIZONA_DSP1_CONTROL_1;
+ wm5102->core.adsp[0].dev = arizona->dev;
+ wm5102->core.adsp[0].regmap = arizona->regmap;
+ wm5102->core.adsp[0].mem = wm5102_dsp1_regions;
+ wm5102->core.adsp[0].num_mems = ARRAY_SIZE(wm5102_dsp1_regions);
+
+ ret = wm_adsp2_init(&wm5102->core.adsp[0], true);
+ if (ret != 0)
+ return ret;
+
for (i = 0; i < ARRAY_SIZE(wm5102->fll); i++)
wm5102->fll[i].vco_mult = 1;
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index 9211e4192f71..2a075ad00d5b 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -308,10 +308,10 @@ ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
-ARIZONA_MIXER_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
static const char *wm5110_aec_loopback_texts[] = {
"HPOUT1L", "HPOUT1R", "HPOUT2L", "HPOUT2R", "HPOUT3L", "HPOUT3R",
@@ -352,6 +352,7 @@ SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
SND_SOC_DAPM_SIGGEN("TONE"),
SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
SND_SOC_DAPM_INPUT("IN1L"),
SND_SOC_DAPM_INPUT("IN1R"),
@@ -585,10 +586,10 @@ ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
-ARIZONA_MIXER_WIDGETS(ASRC1L, "ASRC1L"),
-ARIZONA_MIXER_WIDGETS(ASRC1R, "ASRC1R"),
-ARIZONA_MIXER_WIDGETS(ASRC2L, "ASRC2L"),
-ARIZONA_MIXER_WIDGETS(ASRC2R, "ASRC2R"),
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -610,6 +611,7 @@ SND_SOC_DAPM_OUTPUT("SPKDAT2R"),
{ name, "Noise Generator", "Noise Generator" }, \
{ name, "Tone Generator 1", "Tone Generator 1" }, \
{ name, "Tone Generator 2", "Tone Generator 2" }, \
+ { name, "Haptics", "HAPTICS" }, \
{ name, "AEC", "AEC Loopback" }, \
{ name, "IN1L", "IN1L PGA" }, \
{ name, "IN1R", "IN1R PGA" }, \
@@ -786,10 +788,10 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
- ARIZONA_MIXER_ROUTES("ASRC1L", "ASRC1L"),
- ARIZONA_MIXER_ROUTES("ASRC1R", "ASRC1R"),
- ARIZONA_MIXER_ROUTES("ASRC2L", "ASRC2L"),
- ARIZONA_MIXER_ROUTES("ASRC2R", "ASRC2R"),
+ ARIZONA_MUX_ROUTES("ASRC1L"),
+ ARIZONA_MUX_ROUTES("ASRC1R"),
+ ARIZONA_MUX_ROUTES("ASRC2L"),
+ ARIZONA_MUX_ROUTES("ASRC2R"),
{ "HPOUT1L", NULL, "OUT1L" },
{ "HPOUT1R", NULL, "OUT1R" },
@@ -902,9 +904,29 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
static int wm5110_codec_probe(struct snd_soc_codec *codec)
{
struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+ int ret;
codec->control_data = priv->core.arizona->regmap;
- return snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ priv->core.arizona->dapm = &codec->dapm;
+
+ ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+ if (ret != 0)
+ return ret;
+
+ snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+ priv->core.arizona->dapm = &codec->dapm;
+
+ return 0;
+}
+
+static int wm5110_codec_remove(struct snd_soc_codec *codec)
+{
+ struct wm5110_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+ priv->core.arizona->dapm = NULL;
+
+ return 0;
}
#define WM5110_DIG_VU 0x0200
@@ -935,6 +957,7 @@ static unsigned int wm5110_digital_vu[] = {
static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
.probe = wm5110_codec_probe,
+ .remove = wm5110_codec_remove,
.idle_bias_off = true,
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
new file mode 100644
index 000000000000..7fdb68ef384e
--- /dev/null
+++ b/sound/soc/codecs/wm_adsp.c
@@ -0,0 +1,666 @@
+/*
+ * wm_adsp.c -- Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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/init.h>
+#include <linux/delay.h>
+#include <linux/firmware.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/registers.h>
+
+#include "wm_adsp.h"
+
+#define adsp_crit(_dsp, fmt, ...) \
+ dev_crit(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_err(_dsp, fmt, ...) \
+ dev_err(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_warn(_dsp, fmt, ...) \
+ dev_warn(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_info(_dsp, fmt, ...) \
+ dev_info(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+#define adsp_dbg(_dsp, fmt, ...) \
+ dev_dbg(_dsp->dev, "DSP%d: " fmt, _dsp->num, ##__VA_ARGS__)
+
+#define ADSP1_CONTROL_1 0x00
+#define ADSP1_CONTROL_2 0x02
+#define ADSP1_CONTROL_3 0x03
+#define ADSP1_CONTROL_4 0x04
+#define ADSP1_CONTROL_5 0x06
+#define ADSP1_CONTROL_6 0x07
+#define ADSP1_CONTROL_7 0x08
+#define ADSP1_CONTROL_8 0x09
+#define ADSP1_CONTROL_9 0x0A
+#define ADSP1_CONTROL_10 0x0B
+#define ADSP1_CONTROL_11 0x0C
+#define ADSP1_CONTROL_12 0x0D
+#define ADSP1_CONTROL_13 0x0F
+#define ADSP1_CONTROL_14 0x10
+#define ADSP1_CONTROL_15 0x11
+#define ADSP1_CONTROL_16 0x12
+#define ADSP1_CONTROL_17 0x13
+#define ADSP1_CONTROL_18 0x14
+#define ADSP1_CONTROL_19 0x16
+#define ADSP1_CONTROL_20 0x17
+#define ADSP1_CONTROL_21 0x18
+#define ADSP1_CONTROL_22 0x1A
+#define ADSP1_CONTROL_23 0x1B
+#define ADSP1_CONTROL_24 0x1C
+#define ADSP1_CONTROL_25 0x1E
+#define ADSP1_CONTROL_26 0x20
+#define ADSP1_CONTROL_27 0x21
+#define ADSP1_CONTROL_28 0x22
+#define ADSP1_CONTROL_29 0x23
+#define ADSP1_CONTROL_30 0x24
+#define ADSP1_CONTROL_31 0x26
+
+/*
+ * ADSP1 Control 19
+ */
+#define ADSP1_WDMA_BUFFER_LENGTH_MASK 0x00FF /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_SHIFT 0 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+#define ADSP1_WDMA_BUFFER_LENGTH_WIDTH 8 /* DSP1_WDMA_BUFFER_LENGTH - [7:0] */
+
+
+/*
+ * ADSP1 Control 30
+ */
+#define ADSP1_DBG_CLK_ENA 0x0008 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_MASK 0x0008 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_SHIFT 3 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_DBG_CLK_ENA_WIDTH 1 /* DSP1_DBG_CLK_ENA */
+#define ADSP1_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define ADSP1_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define ADSP1_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define ADSP1_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define ADSP1_START 0x0001 /* DSP1_START */
+#define ADSP1_START_MASK 0x0001 /* DSP1_START */
+#define ADSP1_START_SHIFT 0 /* DSP1_START */
+#define ADSP1_START_WIDTH 1 /* DSP1_START */
+
+#define ADSP2_CONTROL 0
+#define ADSP2_CLOCKING 1
+#define ADSP2_STATUS1 4
+
+/*
+ * ADSP2 Control
+ */
+
+#define ADSP2_MEM_ENA 0x0010 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_MASK 0x0010 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_SHIFT 4 /* DSP1_MEM_ENA */
+#define ADSP2_MEM_ENA_WIDTH 1 /* DSP1_MEM_ENA */
+#define ADSP2_SYS_ENA 0x0004 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_MASK 0x0004 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_SHIFT 2 /* DSP1_SYS_ENA */
+#define ADSP2_SYS_ENA_WIDTH 1 /* DSP1_SYS_ENA */
+#define ADSP2_CORE_ENA 0x0002 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_MASK 0x0002 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_SHIFT 1 /* DSP1_CORE_ENA */
+#define ADSP2_CORE_ENA_WIDTH 1 /* DSP1_CORE_ENA */
+#define ADSP2_START 0x0001 /* DSP1_START */
+#define ADSP2_START_MASK 0x0001 /* DSP1_START */
+#define ADSP2_START_SHIFT 0 /* DSP1_START */
+#define ADSP2_START_WIDTH 1 /* DSP1_START */
+
+/*
+ * ADSP2 clocking
+ */
+#define ADSP2_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
+#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
+
+/*
+ * ADSP2 Status 1
+ */
+#define ADSP2_RAM_RDY 0x0001
+#define ADSP2_RAM_RDY_MASK 0x0001
+#define ADSP2_RAM_RDY_SHIFT 0
+#define ADSP2_RAM_RDY_WIDTH 1
+
+
+static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
+ int type)
+{
+ int i;
+
+ for (i = 0; i < dsp->num_mems; i++)
+ if (dsp->mem[i].type == type)
+ return &dsp->mem[i];
+
+ return NULL;
+}
+
+static int wm_adsp_load(struct wm_adsp *dsp)
+{
+ const struct firmware *firmware;
+ struct regmap *regmap = dsp->regmap;
+ unsigned int pos = 0;
+ const struct wmfw_header *header;
+ const struct wmfw_adsp1_sizes *adsp1_sizes;
+ const struct wmfw_adsp2_sizes *adsp2_sizes;
+ const struct wmfw_footer *footer;
+ const struct wmfw_region *region;
+ const struct wm_adsp_region *mem;
+ const char *region_name;
+ char *file, *text;
+ unsigned int reg;
+ int regions = 0;
+ int ret, offset, type, sizes;
+
+ file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (file == NULL)
+ return -ENOMEM;
+
+ snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
+ file[PAGE_SIZE - 1] = '\0';
+
+ ret = request_firmware(&firmware, file, dsp->dev);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to request '%s'\n", file);
+ goto out;
+ }
+ ret = -EINVAL;
+
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ if (pos >= firmware->size) {
+ adsp_err(dsp, "%s: file too short, %zu bytes\n",
+ file, firmware->size);
+ goto out_fw;
+ }
+
+ header = (void*)&firmware->data[0];
+
+ if (memcmp(&header->magic[0], "WMFW", 4) != 0) {
+ adsp_err(dsp, "%s: invalid magic\n", file);
+ goto out_fw;
+ }
+
+ if (header->ver != 0) {
+ adsp_err(dsp, "%s: unknown file format %d\n",
+ file, header->ver);
+ goto out_fw;
+ }
+
+ if (header->core != dsp->type) {
+ adsp_err(dsp, "%s: invalid core %d != %d\n",
+ file, header->core, dsp->type);
+ goto out_fw;
+ }
+
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ pos = sizeof(*header) + sizeof(*adsp1_sizes) + sizeof(*footer);
+ adsp1_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp1_sizes[1]);
+ sizes = sizeof(*adsp1_sizes);
+
+ adsp_dbg(dsp, "%s: %d DM, %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp1_sizes->dm),
+ le32_to_cpu(adsp1_sizes->pm),
+ le32_to_cpu(adsp1_sizes->zm));
+ break;
+
+ case WMFW_ADSP2:
+ pos = sizeof(*header) + sizeof(*adsp2_sizes) + sizeof(*footer);
+ adsp2_sizes = (void *)&(header[1]);
+ footer = (void *)&(adsp2_sizes[1]);
+ sizes = sizeof(*adsp2_sizes);
+
+ adsp_dbg(dsp, "%s: %d XM, %d YM %d PM, %d ZM\n",
+ file, le32_to_cpu(adsp2_sizes->xm),
+ le32_to_cpu(adsp2_sizes->ym),
+ le32_to_cpu(adsp2_sizes->pm),
+ le32_to_cpu(adsp2_sizes->zm));
+ break;
+
+ default:
+ BUG_ON(NULL == "Unknown DSP type");
+ goto out_fw;
+ }
+
+ if (le32_to_cpu(header->len) != sizeof(*header) +
+ sizes + sizeof(*footer)) {
+ adsp_err(dsp, "%s: unexpected header length %d\n",
+ file, le32_to_cpu(header->len));
+ goto out_fw;
+ }
+
+ adsp_dbg(dsp, "%s: timestamp %llu\n", file,
+ le64_to_cpu(footer->timestamp));
+
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*region)) {
+ region = (void *)&(firmware->data[pos]);
+ region_name = "Unknown";
+ reg = 0;
+ text = NULL;
+ offset = le32_to_cpu(region->offset) & 0xffffff;
+ type = be32_to_cpu(region->type) & 0xff;
+ mem = wm_adsp_find_region(dsp, type);
+
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ region_name = "Firmware name";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_INFO_TEXT:
+ region_name = "Information";
+ text = kzalloc(le32_to_cpu(region->len) + 1,
+ GFP_KERNEL);
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "Absolute";
+ reg = offset;
+ break;
+ case WMFW_ADSP1_PM:
+ BUG_ON(!mem);
+ region_name = "PM";
+ reg = mem->base + (offset * 3);
+ break;
+ case WMFW_ADSP1_DM:
+ BUG_ON(!mem);
+ region_name = "DM";
+ reg = mem->base + (offset * 2);
+ break;
+ case WMFW_ADSP2_XM:
+ BUG_ON(!mem);
+ region_name = "XM";
+ reg = mem->base + (offset * 2);
+ break;
+ case WMFW_ADSP2_YM:
+ BUG_ON(!mem);
+ region_name = "YM";
+ reg = mem->base + (offset * 2);
+ break;
+ case WMFW_ADSP1_ZM:
+ BUG_ON(!mem);
+ region_name = "ZM";
+ reg = mem->base + (offset * 2);
+ break;
+ default:
+ adsp_warn(dsp,
+ "%s.%d: Unknown region type %x at %d(%x)\n",
+ file, regions, type, pos, pos);
+ break;
+ }
+
+ adsp_dbg(dsp, "%s.%d: %d bytes at %d in %s\n", file,
+ regions, le32_to_cpu(region->len), offset,
+ region_name);
+
+ if (text) {
+ memcpy(text, region->data, le32_to_cpu(region->len));
+ adsp_info(dsp, "%s: %s\n", file, text);
+ kfree(text);
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, region->data,
+ le32_to_cpu(region->len));
+ if (ret != 0) {
+ adsp_err(dsp,
+ "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+ file, regions,
+ le32_to_cpu(region->len), offset,
+ region_name, ret);
+ goto out_fw;
+ }
+ }
+
+ pos += le32_to_cpu(region->len) + sizeof(*region);
+ regions++;
+ }
+
+ if (pos > firmware->size)
+ adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+ file, regions, pos - firmware->size);
+
+out_fw:
+ release_firmware(firmware);
+out:
+ kfree(file);
+
+ return ret;
+}
+
+static int wm_adsp_load_coeff(struct wm_adsp *dsp)
+{
+ struct regmap *regmap = dsp->regmap;
+ struct wmfw_coeff_hdr *hdr;
+ struct wmfw_coeff_item *blk;
+ const struct firmware *firmware;
+ const char *region_name;
+ int ret, pos, blocks, type, offset, reg;
+ char *file;
+
+ file = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (file == NULL)
+ return -ENOMEM;
+
+ snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
+ file[PAGE_SIZE - 1] = '\0';
+
+ ret = request_firmware(&firmware, file, dsp->dev);
+ if (ret != 0) {
+ adsp_warn(dsp, "Failed to request '%s'\n", file);
+ ret = 0;
+ goto out;
+ }
+ ret = -EINVAL;
+
+ if (sizeof(*hdr) >= firmware->size) {
+ adsp_err(dsp, "%s: file too short, %zu bytes\n",
+ file, firmware->size);
+ goto out_fw;
+ }
+
+ hdr = (void*)&firmware->data[0];
+ if (memcmp(hdr->magic, "WMDR", 4) != 0) {
+ adsp_err(dsp, "%s: invalid magic\n", file);
+ return -EINVAL;
+ }
+
+ adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
+ (le32_to_cpu(hdr->ver) >> 16) & 0xff,
+ (le32_to_cpu(hdr->ver) >> 8) & 0xff,
+ le32_to_cpu(hdr->ver) & 0xff);
+
+ pos = le32_to_cpu(hdr->len);
+
+ blocks = 0;
+ while (pos < firmware->size &&
+ pos - firmware->size > sizeof(*blk)) {
+ blk = (void*)(&firmware->data[pos]);
+
+ type = be32_to_cpu(blk->type) & 0xff;
+ offset = le32_to_cpu(blk->offset) & 0xffffff;
+
+ adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
+ file, blocks, le32_to_cpu(blk->id),
+ (le32_to_cpu(blk->ver) >> 16) & 0xff,
+ (le32_to_cpu(blk->ver) >> 8) & 0xff,
+ le32_to_cpu(blk->ver) & 0xff);
+ adsp_dbg(dsp, "%s.%d: %d bytes at 0x%x in %x\n",
+ file, blocks, le32_to_cpu(blk->len), offset, type);
+
+ reg = 0;
+ region_name = "Unknown";
+ switch (type) {
+ case WMFW_NAME_TEXT:
+ case WMFW_INFO_TEXT:
+ break;
+ case WMFW_ABSOLUTE:
+ region_name = "register";
+ reg = offset;
+ break;
+ default:
+ adsp_err(dsp, "Unknown region type %x\n", type);
+ break;
+ }
+
+ if (reg) {
+ ret = regmap_raw_write(regmap, reg, blk->data,
+ le32_to_cpu(blk->len));
+ if (ret != 0) {
+ adsp_err(dsp,
+ "%s.%d: Failed to write to %x in %s\n",
+ file, blocks, reg, region_name);
+ }
+ }
+
+ pos += le32_to_cpu(blk->len) + sizeof(*blk);
+ blocks++;
+ }
+
+ if (pos > firmware->size)
+ adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
+ file, blocks, pos - firmware->size);
+
+out_fw:
+ release_firmware(firmware);
+out:
+ kfree(file);
+ return 0;
+}
+
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = &dsps[w->shift];
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, ADSP1_SYS_ENA);
+
+ ret = wm_adsp_load(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_load_coeff(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Start the core running */
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_CORE_ENA | ADSP1_START,
+ ADSP1_CORE_ENA | ADSP1_START);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ /* Halt the core */
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_CORE_ENA | ADSP1_START, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_19,
+ ADSP1_WDMA_BUFFER_LENGTH_MASK, 0);
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+
+err:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
+ ADSP1_SYS_ENA, 0);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_event);
+
+static int wm_adsp2_ena(struct wm_adsp *dsp)
+{
+ unsigned int val;
+ int ret, count;
+
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+ if (ret != 0)
+ return ret;
+
+ /* Wait for the RAM to start, should be near instantaneous */
+ count = 0;
+ do {
+ ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
+ &val);
+ if (ret != 0)
+ return ret;
+ } while (!(val & ADSP2_RAM_RDY) && ++count < 10);
+
+ if (!(val & ADSP2_RAM_RDY)) {
+ adsp_err(dsp, "Failed to start DSP RAM\n");
+ return -EBUSY;
+ }
+
+ adsp_dbg(dsp, "RAM ready after %d polls\n", count);
+ adsp_info(dsp, "RAM ready after %d polls\n", count);
+
+ return 0;
+}
+
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
+ struct wm_adsp *dsp = &dsps[w->shift];
+ unsigned int val;
+ int ret;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (dsp->dvfs) {
+ ret = regmap_read(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING, &val);
+ if (ret != 0) {
+ dev_err(dsp->dev,
+ "Failed to read clocking: %d\n", ret);
+ return ret;
+ }
+
+ if ((val & ADSP2_CLK_SEL_MASK) >= 3) {
+ ret = regulator_enable(dsp->dvfs);
+ if (ret != 0) {
+ dev_err(dsp->dev,
+ "Failed to enable supply: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(dsp->dvfs,
+ 1800000,
+ 1800000);
+ if (ret != 0) {
+ dev_err(dsp->dev,
+ "Failed to raise supply: %d\n",
+ ret);
+ return ret;
+ }
+ }
+ }
+
+ ret = wm_adsp2_ena(dsp);
+ if (ret != 0)
+ return ret;
+
+ ret = wm_adsp_load(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = wm_adsp_load_coeff(dsp);
+ if (ret != 0)
+ goto err;
+
+ ret = regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_CORE_ENA | ADSP2_START,
+ ADSP2_CORE_ENA | ADSP2_START);
+ if (ret != 0)
+ goto err;
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA | ADSP2_CORE_ENA |
+ ADSP2_START, 0);
+
+ if (dsp->dvfs) {
+ ret = regulator_set_voltage(dsp->dvfs, 1200000,
+ 1800000);
+ if (ret != 0)
+ dev_warn(dsp->dev,
+ "Failed to lower supply: %d\n",
+ ret);
+
+ ret = regulator_disable(dsp->dvfs);
+ if (ret != 0)
+ dev_err(dsp->dev,
+ "Failed to enable supply: %d\n",
+ ret);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+err:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA | ADSP2_CORE_ENA | ADSP2_START, 0);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_event);
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
+{
+ int ret;
+
+ if (dvfs) {
+ adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
+ if (IS_ERR(adsp->dvfs)) {
+ ret = PTR_ERR(adsp->dvfs);
+ dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_enable(adsp->dvfs);
+ if (ret != 0) {
+ dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
+ if (ret != 0) {
+ dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_disable(adsp->dvfs);
+ if (ret != 0) {
+ dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
new file mode 100644
index 000000000000..ffd29a4609e2
--- /dev/null
+++ b/sound/soc/codecs/wm_adsp.h
@@ -0,0 +1,59 @@
+/*
+ * wm_adsp.h -- Wolfson ADSP support
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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 __WM_ADSP_H
+#define __WM_ADSP_H
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+
+#include "wmfw.h"
+
+struct regulator;
+
+struct wm_adsp_region {
+ int type;
+ unsigned int base;
+};
+
+struct wm_adsp {
+ const char *part;
+ int num;
+ int type;
+ struct device *dev;
+ struct regmap *regmap;
+
+ int base;
+
+ const struct wm_adsp_region *mem;
+ int num_mems;
+
+ struct regulator *dvfs;
+};
+
+#define WM_ADSP1(wname, num) \
+ { .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+ .shift = num, .event = wm_adsp1_event, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+#define WM_ADSP2(wname, num) \
+{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
+ .shift = num, .event = wm_adsp2_event, \
+ .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+
+int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs);
+int wm_adsp1_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+int wm_adsp2_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event);
+
+#endif
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
new file mode 100644
index 000000000000..5632ded67fdd
--- /dev/null
+++ b/sound/soc/codecs/wmfw.h
@@ -0,0 +1,128 @@
+/*
+ * wmfw.h - Wolfson firmware format information
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@opensource.wolfsonmicro.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 __WMFW_H
+#define __WMFW_H
+
+#include <linux/types.h>
+
+struct wmfw_header {
+ char magic[4];
+ __le32 len;
+ __le16 rev;
+ u8 core;
+ u8 ver;
+} __packed;
+
+struct wmfw_footer {
+ __le64 timestamp;
+ __le32 checksum;
+} __packed;
+
+struct wmfw_adsp1_sizes {
+ __le32 dm;
+ __le32 pm;
+ __le32 zm;
+} __packed;
+
+struct wmfw_adsp2_sizes {
+ __le32 xm;
+ __le32 ym;
+ __le32 pm;
+ __le32 zm;
+} __packed;
+
+struct wmfw_region {
+ union {
+ __be32 type;
+ __le32 offset;
+ };
+ __le32 len;
+ u8 data[];
+} __packed;
+
+struct wmfw_id_hdr {
+ __be32 core_id;
+ __be32 core_rev;
+ __be32 id;
+ __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_id_hdr {
+ struct wmfw_id_hdr fw;
+ __be32 zm;
+ __be32 dm;
+ __be32 algs;
+} __packed;
+
+struct wmfw_adsp2_id_hdr {
+ struct wmfw_id_hdr fw;
+ __be32 zm;
+ __be32 xm;
+ __be32 ym;
+ __be32 algs;
+} __packed;
+
+struct wmfw_alg_hdr {
+ __be32 id;
+ __be32 ver;
+} __packed;
+
+struct wmfw_adsp1_alg_hdr {
+ struct wmfw_alg_hdr alg;
+ __be32 zm;
+ __be32 dm;
+} __packed;
+
+struct wmfw_adsp2_alg_hdr {
+ struct wmfw_alg_hdr alg;
+ __be32 zm;
+ __be32 xm;
+ __be32 ym;
+} __packed;
+
+struct wmfw_coeff_hdr {
+ u8 magic[4];
+ __le32 len;
+ __le32 ver;
+ u8 data[];
+} __packed;
+
+struct wmfw_coeff_item {
+ union {
+ __be32 type;
+ __le32 offset;
+ };
+ __le32 id;
+ __le32 ver;
+ __le32 sr;
+ __le32 len;
+ u8 data[];
+} __packed;
+
+#define WMFW_ADSP1 1
+#define WMFW_ADSP2 2
+
+#define WMFW_ABSOLUTE 0xf0
+#define WMFW_NAME_TEXT 0xfe
+#define WMFW_INFO_TEXT 0xff
+
+#define WMFW_ADSP1_PM 2
+#define WMFW_ADSP1_DM 3
+#define WMFW_ADSP1_ZM 4
+
+#define WMFW_ADSP2_PM 2
+#define WMFW_ADSP2_ZM 4
+#define WMFW_ADSP2_XM 5
+#define WMFW_ADSP2_YM 6
+
+#endif