summaryrefslogtreecommitdiffstats
path: root/sound/soc/codecs
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc/codecs')
-rw-r--r--sound/soc/codecs/88pm860x-codec.c6
-rw-r--r--sound/soc/codecs/ac97.c7
-rw-r--r--sound/soc/codecs/ad1980.c12
-rw-r--r--sound/soc/codecs/adau1701.c321
-rw-r--r--sound/soc/codecs/stac9766.c26
-rw-r--r--sound/soc/codecs/tas5086.c330
-rw-r--r--sound/soc/codecs/tlv320aic3x.c6
-rw-r--r--sound/soc/codecs/wm8400.c9
-rw-r--r--sound/soc/codecs/wm8903.c6
-rw-r--r--sound/soc/codecs/wm8904.c9
-rw-r--r--sound/soc/codecs/wm8990.c11
-rw-r--r--sound/soc/codecs/wm8991.h9
-rw-r--r--sound/soc/codecs/wm8994.c12
-rw-r--r--sound/soc/codecs/wm8995.h7
-rw-r--r--sound/soc/codecs/wm9705.c16
-rw-r--r--sound/soc/codecs/wm9712.c18
-rw-r--r--sound/soc/codecs/wm9713.c18
-rw-r--r--sound/soc/codecs/wm_adsp.c459
-rw-r--r--sound/soc/codecs/wm_adsp.h13
-rw-r--r--sound/soc/codecs/wm_hubs.c6
20 files changed, 1126 insertions, 175 deletions
diff --git a/sound/soc/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index 60159c07448..e2bd3dd02c0 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -120,10 +120,8 @@
* before DAC & PGA in DAPM power-off sequence.
*/
#define PM860X_DAPM_OUTPUT(wname, wevent) \
-{ .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, \
- .shift = 0, .invert = 0, .kcontrol_news = NULL, \
- .num_kcontrols = 0, .event = wevent, \
- .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD, }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, 0, 0, NULL, 0, wevent, \
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD)
struct pm860x_det {
struct snd_soc_jack *hp_jack;
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index ef2ae32ffc6..ec7351803c2 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -62,13 +62,13 @@ static struct snd_soc_dai_driver ac97_dai = {
static unsigned int ac97_read(struct snd_soc_codec *codec,
unsigned int reg)
{
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
}
static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
unsigned int val)
{
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
return 0;
}
@@ -79,7 +79,8 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
int ret;
/* add codec as bus device for standard ac97 */
- ret = snd_ac97_bus(codec->card->snd_card, 0, &soc_ac97_ops, NULL, &ac97_bus);
+ ret = snd_ac97_bus(codec->card->snd_card, 0, soc_ac97_ops, NULL,
+ &ac97_bus);
if (ret < 0)
return ret;
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index f385342947d..89fcf7d6e7b 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -108,7 +108,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
case AC97_EXTENDED_STATUS:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
default:
reg = reg >> 1;
@@ -124,7 +124,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < ARRAY_SIZE(ad1980_reg))
cache[reg] = val;
@@ -154,13 +154,13 @@ static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
u16 retry_cnt = 0;
retry:
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, AC97_RESET) == 0x0090)
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
/* Set bit 16slot in register 74h, then every slot will has only 16
* bits. This command is sent out in 20bit mode, in which case the
* first nibble of data is eaten by the addr. (Tag is always 16 bit)*/
@@ -186,7 +186,7 @@ static int ad1980_soc_probe(struct snd_soc_codec *codec)
printk(KERN_INFO "AD1980 SoC Audio Codec\n");
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index dafdbe87ede..d1124a5b347 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -13,6 +13,10 @@
#include <linux/i2c.h>
#include <linux/delay.h>
#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -21,16 +25,19 @@
#include "sigmadsp.h"
#include "adau1701.h"
-#define ADAU1701_DSPCTRL 0x1c
-#define ADAU1701_SEROCTL 0x1e
-#define ADAU1701_SERICTL 0x1f
+#define ADAU1701_DSPCTRL 0x081c
+#define ADAU1701_SEROCTL 0x081e
+#define ADAU1701_SERICTL 0x081f
-#define ADAU1701_AUXNPOW 0x22
+#define ADAU1701_AUXNPOW 0x0822
+#define ADAU1701_PINCONF_0 0x0820
+#define ADAU1701_PINCONF_1 0x0821
+#define ADAU1701_AUXNPOW 0x0822
-#define ADAU1701_OSCIPOW 0x26
-#define ADAU1701_DACSET 0x27
+#define ADAU1701_OSCIPOW 0x0826
+#define ADAU1701_DACSET 0x0827
-#define ADAU1701_NUM_REGS 0x28
+#define ADAU1701_MAX_REGISTER 0x0828
#define ADAU1701_DSPCTRL_CR (1 << 2)
#define ADAU1701_DSPCTRL_DAM (1 << 3)
@@ -84,10 +91,18 @@
#define ADAU1701_OSCIPOW_OPD 0x04
#define ADAU1701_DACSET_DACINIT 1
+#define ADAU1707_CLKDIV_UNSET (-1UL)
+
#define ADAU1701_FIRMWARE "adau1701.bin"
struct adau1701 {
+ int gpio_nreset;
+ int gpio_pll_mode[2];
unsigned int dai_fmt;
+ unsigned int pll_clkdiv;
+ unsigned int sysclk;
+ struct regmap *regmap;
+ u8 pin_config[12];
};
static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -119,10 +134,13 @@ static const struct snd_soc_dapm_route adau1701_dapm_routes[] = {
{ "ADC", NULL, "IN1" },
};
-static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
+static unsigned int adau1701_register_size(struct device *dev,
unsigned int reg)
{
switch (reg) {
+ case ADAU1701_PINCONF_0:
+ case ADAU1701_PINCONF_1:
+ return 3;
case ADAU1701_DSPCTRL:
case ADAU1701_SEROCTL:
case ADAU1701_AUXNPOW:
@@ -133,33 +151,42 @@ static unsigned int adau1701_register_size(struct snd_soc_codec *codec,
return 1;
}
- dev_err(codec->dev, "Unsupported register address: %d\n", reg);
+ dev_err(dev, "Unsupported register address: %d\n", reg);
return 0;
}
-static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
- unsigned int value)
+static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ADAU1701_DACSET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int adau1701_reg_write(void *context, unsigned int reg,
+ unsigned int value)
{
+ struct i2c_client *client = context;
unsigned int i;
unsigned int size;
- uint8_t buf[4];
+ uint8_t buf[5];
int ret;
- size = adau1701_register_size(codec, reg);
+ size = adau1701_register_size(&client->dev, reg);
if (size == 0)
return -EINVAL;
- snd_soc_cache_write(codec, reg, value);
-
- buf[0] = 0x08;
- buf[1] = reg;
+ buf[0] = reg >> 8;
+ buf[1] = reg & 0xff;
for (i = size + 1; i >= 2; --i) {
buf[i] = value;
value >>= 8;
}
- ret = i2c_master_send(to_i2c_client(codec->dev), buf, size + 2);
+ ret = i2c_master_send(client, buf, size + 2);
if (ret == size + 2)
return 0;
else if (ret < 0)
@@ -168,21 +195,107 @@ static int adau1701_write(struct snd_soc_codec *codec, unsigned int reg,
return -EIO;
}
-static unsigned int adau1701_read(struct snd_soc_codec *codec, unsigned int reg)
+static int adau1701_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
{
- unsigned int value;
- unsigned int ret;
+ int ret;
+ unsigned int i;
+ unsigned int size;
+ uint8_t send_buf[2], recv_buf[3];
+ struct i2c_client *client = context;
+ struct i2c_msg msgs[2];
+
+ size = adau1701_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
- ret = snd_soc_cache_read(codec, reg, &value);
- if (ret)
+ send_buf[0] = reg >> 8;
+ send_buf[1] = reg & 0xff;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
- return value;
+ *value = 0;
+
+ for (i = 0; i < size; i++)
+ *value |= recv_buf[i] << (i * 8);
+
+ return 0;
}
-static int adau1701_load_firmware(struct snd_soc_codec *codec)
+static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
{
- return process_sigma_firmware(codec->control_data, ADAU1701_FIRMWARE);
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ struct i2c_client *client = to_i2c_client(codec->dev);
+ int ret;
+
+ if (clkdiv != ADAU1707_CLKDIV_UNSET &&
+ gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
+ gpio_is_valid(adau1701->gpio_pll_mode[1])) {
+ switch (clkdiv) {
+ case 64:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+ break;
+ case 256:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 0);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+ break;
+ case 384:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+ break;
+ case 0: /* fallback */
+ case 512:
+ gpio_set_value(adau1701->gpio_pll_mode[0], 1);
+ gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+ break;
+ }
+ }
+
+ adau1701->pll_clkdiv = clkdiv;
+
+ if (gpio_is_valid(adau1701->gpio_nreset)) {
+ gpio_set_value(adau1701->gpio_nreset, 0);
+ /* minimum reset time is 20ns */
+ udelay(1);
+ gpio_set_value(adau1701->gpio_nreset, 1);
+ /* power-up time may be as long as 85ms */
+ mdelay(85);
+ }
+
+ /*
+ * Postpone the firmware download to a point in time when we
+ * know the correct PLL setup
+ */
+ if (clkdiv != ADAU1707_CLKDIV_UNSET) {
+ ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
+ if (ret) {
+ dev_warn(codec->dev, "Failed to load firmware\n");
+ return ret;
+ }
+ }
+
+ regmap_write(adau1701->regmap, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
+ regmap_write(adau1701->regmap, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+
+ regcache_mark_dirty(adau1701->regmap);
+ regcache_sync(adau1701->regmap);
+
+ return 0;
}
static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
@@ -221,7 +334,7 @@ static int adau1701_set_capture_pcm_format(struct snd_soc_codec *codec,
mask |= ADAU1701_SEROCTL_MSB_DEALY_MASK;
}
- snd_soc_update_bits(codec, ADAU1701_SEROCTL, mask, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL, mask, val);
return 0;
}
@@ -249,7 +362,7 @@ static int adau1701_set_playback_pcm_format(struct snd_soc_codec *codec,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_SERICTL,
+ regmap_update_bits(adau1701->regmap, ADAU1701_SERICTL,
ADAU1701_SERICTL_MODE_MASK, val);
return 0;
@@ -259,8 +372,22 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+ unsigned int clkdiv = adau1701->sysclk / params_rate(params);
snd_pcm_format_t format;
unsigned int val;
+ int ret;
+
+ /*
+ * If the mclk/lrclk ratio changes, the chip needs updated PLL
+ * mode GPIO settings, and a full reset cycle, including a new
+ * firmware upload.
+ */
+ if (clkdiv != adau1701->pll_clkdiv) {
+ ret = adau1701_reset(codec, clkdiv);
+ if (ret < 0)
+ return ret;
+ }
switch (params_rate(params)) {
case 192000:
@@ -276,7 +403,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_DSPCTRL,
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
ADAU1701_DSPCTRL_SR_MASK, val);
format = params_format(params);
@@ -352,8 +479,8 @@ static int adau1701_set_dai_fmt(struct snd_soc_dai *codec_dai,
adau1701->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
- snd_soc_write(codec, ADAU1701_SERICTL, serictl);
- snd_soc_update_bits(codec, ADAU1701_SEROCTL,
+ regmap_write(adau1701->regmap, ADAU1701_SERICTL, serictl);
+ regmap_update_bits(adau1701->regmap, ADAU1701_SEROCTL,
~ADAU1701_SEROCTL_WORD_LEN_MASK, seroctl);
return 0;
@@ -363,6 +490,7 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
unsigned int mask = ADAU1701_AUXNPOW_VBPD | ADAU1701_AUXNPOW_VRPD;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (level) {
case SND_SOC_BIAS_ON:
@@ -371,11 +499,13 @@ static int adau1701_set_bias_level(struct snd_soc_codec *codec,
break;
case SND_SOC_BIAS_STANDBY:
/* Enable VREF and VREF buffer */
- snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, 0x00);
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, 0x00);
break;
case SND_SOC_BIAS_OFF:
/* Disable VREF and VREF buffer */
- snd_soc_update_bits(codec, ADAU1701_AUXNPOW, mask, mask);
+ regmap_update_bits(adau1701->regmap,
+ ADAU1701_AUXNPOW, mask, mask);
break;
}
@@ -387,6 +517,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
{
struct snd_soc_codec *codec = dai->codec;
unsigned int mask = ADAU1701_DSPCTRL_DAM;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
unsigned int val;
if (mute)
@@ -394,7 +525,7 @@ static int adau1701_digital_mute(struct snd_soc_dai *dai, int mute)
else
val = mask;
- snd_soc_update_bits(codec, ADAU1701_DSPCTRL, mask, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL, mask, val);
return 0;
}
@@ -403,6 +534,7 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
int source, unsigned int freq, int dir)
{
unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
switch (clk_id) {
case ADAU1701_CLK_SRC_OSC:
@@ -415,7 +547,9 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
return -EINVAL;
}
- snd_soc_update_bits(codec, ADAU1701_OSCIPOW, ADAU1701_OSCIPOW_OPD, val);
+ regmap_update_bits(adau1701->regmap, ADAU1701_OSCIPOW,
+ ADAU1701_OSCIPOW_OPD, val);
+ adau1701->sysclk = freq;
return 0;
}
@@ -452,18 +586,45 @@ static struct snd_soc_dai_driver adau1701_dai = {
.symmetric_rates = 1,
};
+#ifdef CONFIG_OF
+static const struct of_device_id adau1701_dt_ids[] = {
+ { .compatible = "adi,adau1701", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau1701_dt_ids);
+#endif
+
static int adau1701_probe(struct snd_soc_codec *codec)
{
- int ret;
+ int i, ret;
+ unsigned int val;
+ struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
+
+ /*
+ * Let the pll_clkdiv variable default to something that won't happen
+ * at runtime. That way, we can postpone the firmware download from
+ * adau1701_reset() to a point in time when we know the correct PLL
+ * mode parameters.
+ */
+ adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
+
+ /* initalize with pre-configured pll mode settings */
+ ret = adau1701_reset(codec, adau1701->pll_clkdiv);
+ if (ret < 0)
+ return ret;
+
+ /* set up pin config */
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i] << (i * 4);
- codec->control_data = to_i2c_client(codec->dev);
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_0, val);
- ret = adau1701_load_firmware(codec);
- if (ret)
- dev_warn(codec->dev, "Failed to load firmware\n");
+ val = 0;
+ for (i = 0; i < 6; i++)
+ val |= adau1701->pin_config[i + 6] << (i * 4);
- snd_soc_write(codec, ADAU1701_DACSET, ADAU1701_DACSET_DACINIT);
- snd_soc_write(codec, ADAU1701_DSPCTRL, ADAU1701_DSPCTRL_CR);
+ regmap_write(adau1701->regmap, ADAU1701_PINCONF_1, val);
return 0;
}
@@ -473,9 +634,6 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
.set_bias_level = adau1701_set_bias_level,
.idle_bias_off = true,
- .reg_cache_size = ADAU1701_NUM_REGS,
- .reg_word_size = sizeof(u16),
-
.controls = adau1701_controls,
.num_controls = ARRAY_SIZE(adau1701_controls),
.dapm_widgets = adau1701_dapm_widgets,
@@ -483,22 +641,86 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
.dapm_routes = adau1701_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(adau1701_dapm_routes),
- .write = adau1701_write,
- .read = adau1701_read,
-
.set_sysclk = adau1701_set_sysclk,
};
+static const struct regmap_config adau1701_regmap = {
+ .reg_bits = 16,
+ .val_bits = 32,
+ .max_register = ADAU1701_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = adau1701_volatile_reg,
+ .reg_write = adau1701_reg_write,
+ .reg_read = adau1701_reg_read,
+};
+
static int adau1701_i2c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct adau1701 *adau1701;
+ struct device *dev = &client->dev;
+ int gpio_nreset = -EINVAL;
+ int gpio_pll_mode[2] = { -EINVAL, -EINVAL };
int ret;
- adau1701 = devm_kzalloc(&client->dev, sizeof(*adau1701), GFP_KERNEL);
+ adau1701 = devm_kzalloc(dev, sizeof(*adau1701), GFP_KERNEL);
if (!adau1701)
return -ENOMEM;
+ adau1701->regmap = devm_regmap_init(dev, NULL, client,
+ &adau1701_regmap);
+ if (IS_ERR(adau1701->regmap))
+ return PTR_ERR(adau1701->regmap);
+
+ if (dev->of_node) {
+ gpio_nreset = of_get_named_gpio(dev->of_node, "reset-gpio", 0);
+ if (gpio_nreset < 0 && gpio_nreset != -ENOENT)
+ return gpio_nreset;
+
+ gpio_pll_mode[0] = of_get_named_gpio(dev->of_node,
+ "adi,pll-mode-gpios", 0);
+ if (gpio_pll_mode[0] < 0 && gpio_pll_mode[0] != -ENOENT)
+ return gpio_pll_mode[0];
+
+ gpio_pll_mode[1] = of_get_named_gpio(dev->of_node,
+ "adi,pll-mode-gpios", 1);
+ if (gpio_pll_mode[1] < 0 && gpio_pll_mode[1] != -ENOENT)
+ return gpio_pll_mode[1];
+
+ of_property_read_u32(dev->of_node, "adi,pll-clkdiv",
+ &adau1701->pll_clkdiv);
+
+ of_property_read_u8_array(dev->of_node, "adi,pin-config",
+ adau1701->pin_config,
+ ARRAY_SIZE(adau1701->pin_config));
+ }
+
+ if (gpio_is_valid(gpio_nreset)) {
+ ret = devm_gpio_request_one(dev, gpio_nreset, GPIOF_OUT_INIT_LOW,
+ "ADAU1701 Reset");
+ if (ret < 0)
+ return ret;
+ }
+
+ if (gpio_is_valid(gpio_pll_mode[0]) &&
+ gpio_is_valid(gpio_pll_mode[1])) {
+ ret = devm_gpio_request_one(dev, gpio_pll_mode[0],
+ GPIOF_OUT_INIT_LOW,
+ "ADAU1701 PLL mode 0");
+ if (ret < 0)
+ return ret;
+
+ ret = devm_gpio_request_one(dev, gpio_pll_mode[1],
+ GPIOF_OUT_INIT_LOW,
+ "ADAU1701 PLL mode 1");
+ if (ret < 0)
+ return ret;
+ }
+
+ adau1701->gpio_nreset = gpio_nreset;
+ adau1701->gpio_pll_mode[0] = gpio_pll_mode[0];
+ adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
+
i2c_set_clientdata(client, adau1701);
ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
&adau1701_dai, 1);
@@ -521,6 +743,7 @@ static struct i2c_driver adau1701_i2c_driver = {
.driver = {
.name = "adau1701",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(adau1701_dt_ids),
},
.probe = adau1701_i2c_probe,
.remove = adau1701_i2c_remove,
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 2eda85ba79a..a5455c1aea4 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -28,8 +28,6 @@
#include "stac9766.h"
-#define STAC9766_VERSION "0.10"
-
/*
* STAC9766 register cache
*/
@@ -145,14 +143,14 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return 0;
}
if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
return -EIO;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
cache[reg / 2] = val;
return 0;
}
@@ -164,7 +162,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
if (reg > AC97_STAC_PAGE0) {
stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
- val = soc_ac97_ops.read(codec->ac97, reg - AC97_STAC_PAGE0);
+ val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
return val;
}
@@ -175,7 +173,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
reg == AC97_VENDOR_ID2) {
- val = soc_ac97_ops.read(codec->ac97, reg);
+ val = soc_ac97_ops->read(codec->ac97, reg);
return val;
}
return cache[reg / 2];
@@ -242,15 +240,15 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
return -EIO;
return 0;
@@ -274,7 +272,7 @@ reset:
return -EIO;
}
codec->ac97->bus->ops->warm_reset(codec->ac97);
- id = soc_ac97_ops.read(codec->ac97, AC97_VENDOR_ID2);
+ id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
if (id != 0x4c13) {
stac9766_reset(codec, 0);
reset++;
@@ -338,9 +336,7 @@ static int stac9766_codec_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- printk(KERN_INFO "STAC9766 SoC Audio Codec %s\n", STAC9766_VERSION);
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index d447c4aa1d5..6d31d88f720 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -83,6 +83,14 @@
#define TAS5086_SPLIT_CAP_CHARGE 0x1a /* Split cap charge period register */
#define TAS5086_OSC_TRIM 0x1b /* Oscillator trim register */
#define TAS5086_BKNDERR 0x1c
+#define TAS5086_INPUT_MUX 0x20
+#define TAS5086_PWM_OUTPUT_MUX 0x25
+
+#define TAS5086_MAX_REGISTER TAS5086_PWM_OUTPUT_MUX
+
+#define TAS5086_PWM_START_MIDZ_FOR_START_1 (1 << 7)
+#define TAS5086_PWM_START_MIDZ_FOR_START_2 (1 << 6)
+#define TAS5086_PWM_START_CHANNEL_MASK (0x3f)
/*
* Default TAS5086 power-up configuration
@@ -119,9 +127,30 @@ static const struct reg_default tas5086_reg_defaults[] = {
{ 0x1c, 0x05 },
};
+static int tas5086_register_size(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case TAS5086_CLOCK_CONTROL ... TAS5086_BKNDERR:
+ return 1;
+ case TAS5086_INPUT_MUX:
+ case TAS5086_PWM_OUTPUT_MUX:
+ return 4;
+ }
+
+ dev_err(dev, "Unsupported register address: %d\n", reg);
+ return 0;
+}
+
static bool tas5086_accessible_reg(struct device *dev, unsigned int reg)
{
- return !((reg == 0x0f) || (reg >= 0x11 && reg <= 0x17));
+ switch (reg) {
+ case 0x0f:
+ case 0x11 ... 0x17:
+ case 0x1d ... 0x1f:
+ return false;
+ default:
+ return true;
+ }
}
static bool tas5086_volatile_reg(struct device *dev, unsigned int reg)
@@ -140,6 +169,76 @@ static bool tas5086_writeable_reg(struct device *dev, unsigned int reg)
return tas5086_accessible_reg(dev, reg) && (reg != TAS5086_DEV_ID);
}
+static int tas5086_reg_write(void *context, unsigned int reg,
+ unsigned int value)
+{
+ struct i2c_client *client = context;
+ unsigned int i, size;
+ uint8_t buf[5];
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ buf[0] = reg;
+
+ for (i = size; i >= 1; --i) {
+ buf[i] = value;
+ value >>= 8;
+ }
+
+ ret = i2c_master_send(client, buf, size + 1);
+ if (ret == size + 1)
+ return 0;
+ else if (ret < 0)
+ return ret;
+ else
+ return -EIO;
+}
+
+static int tas5086_reg_read(void *context, unsigned int reg,
+ unsigned int *value)
+{
+ struct i2c_client *client = context;
+ uint8_t send_buf, recv_buf[4];
+ struct i2c_msg msgs[2];
+ unsigned int size;
+ unsigned int i;
+ int ret;
+
+ size = tas5086_register_size(&client->dev, reg);
+ if (size == 0)
+ return -EINVAL;
+
+ send_buf = reg;
+
+ msgs[0].addr = client->addr;
+ msgs[0].len = sizeof(send_buf);
+ msgs[0].buf = &send_buf;
+ msgs[0].flags = 0;
+
+ msgs[1].addr = client->addr;
+ msgs[1].len = size;
+ msgs[1].buf = recv_buf;
+ msgs[1].flags = I2C_M_RD;
+
+ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret < 0)
+ return ret;
+ else if (ret != ARRAY_SIZE(msgs))
+ return -EIO;
+
+ *value = 0;
+
+ for (i = 0; i < size; i++) {
+ *value <<= 8;
+ *value |= recv_buf[i];
+ }
+
+ return 0;
+}
+
struct tas5086_private {
struct regmap *regmap;
unsigned int mclk, sclk;
@@ -376,6 +475,202 @@ static const struct snd_kcontrol_new tas5086_controls[] = {
tas5086_get_deemph, tas5086_put_deemph),
};
+/* Input mux controls */
+static const char *tas5086_dapm_sdin_texts[] =
+{
+ "SDIN1-L", "SDIN1-R", "SDIN2-L", "SDIN2-R",
+ "SDIN3-L", "SDIN3-R", "Ground (0)", "nc"
+};
+
+static const struct soc_enum tas5086_dapm_input_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 20, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 16, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 12, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 8, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 4, 8, tas5086_dapm_sdin_texts),
+ SOC_ENUM_SINGLE(TAS5086_INPUT_MUX, 0, 8, tas5086_dapm_sdin_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_input_mux_controls[] = {
+ SOC_DAPM_ENUM("Channel 1 input", tas5086_dapm_input_mux_enum[0]),
+ SOC_DAPM_ENUM("Channel 2 input", tas5086_dapm_input_mux_enum[1]),
+ SOC_DAPM_ENUM("Channel 3 input", tas5086_dapm_input_mux_enum[2]),
+ SOC_DAPM_ENUM("Channel 4 input", tas5086_dapm_input_mux_enum[3]),
+ SOC_DAPM_ENUM("Channel 5 input", tas5086_dapm_input_mux_enum[4]),
+ SOC_DAPM_ENUM("Channel 6 input", tas5086_dapm_input_mux_enum[5]),
+};
+
+/* Output mux controls */
+static const char *tas5086_dapm_channel_texts[] =
+ { "Channel 1 Mux", "Channel 2 Mux", "Channel 3 Mux",
+ "Channel 4 Mux", "Channel 5 Mux", "Channel 6 Mux" };
+
+static const struct soc_enum tas5086_dapm_output_mux_enum[] = {
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 20, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 16, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 12, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 8, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 4, 6, tas5086_dapm_channel_texts),
+ SOC_ENUM_SINGLE(TAS5086_PWM_OUTPUT_MUX, 0, 6, tas5086_dapm_channel_texts),
+};
+
+static const struct snd_kcontrol_new tas5086_dapm_output_mux_controls[] = {
+ SOC_DAPM_ENUM("PWM1 Output", tas5086_dapm_output_mux_enum[0]),
+ SOC_DAPM_ENUM("PWM2 Output", tas5086_dapm_output_mux_enum[1]),
+ SOC_DAPM_ENUM("PWM3 Output", tas5086_dapm_output_mux_enum[2]),
+ SOC_DAPM_ENUM("PWM4 Output", tas5086_dapm_output_mux_enum[3]),
+ SOC_DAPM_ENUM("PWM5 Output", tas5086_dapm_output_mux_enum[4]),
+ SOC_DAPM_ENUM("PWM6 Output", tas5086_dapm_output_mux_enum[5]),
+};
+
+static const struct snd_soc_dapm_widget tas5086_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("SDIN1-L"),
+ SND_SOC_DAPM_INPUT("SDIN1-R"),
+ SND_SOC_DAPM_INPUT("SDIN2-L"),
+ SND_SOC_DAPM_INPUT("SDIN2-R"),
+ SND_SOC_DAPM_INPUT("SDIN3-L"),
+ SND_SOC_DAPM_INPUT("SDIN3-R"),
+ SND_SOC_DAPM_INPUT("SDIN4-L"),
+ SND_SOC_DAPM_INPUT("SDIN4-R"),
+
+ SND_SOC_DAPM_OUTPUT("PWM1"),
+ SND_SOC_DAPM_OUTPUT("PWM2"),
+ SND_SOC_DAPM_OUTPUT("PWM3"),
+ SND_SOC_DAPM_OUTPUT("PWM4"),
+ SND_SOC_DAPM_OUTPUT("PWM5"),
+ SND_SOC_DAPM_OUTPUT("PWM6"),
+
+ SND_SOC_DAPM_MUX("Channel 1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[0]),
+ SND_SOC_DAPM_MUX("Channel 2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[1]),
+ SND_SOC_DAPM_MUX("Channel 3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[2]),
+ SND_SOC_DAPM_MUX("Channel 4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[3]),
+ SND_SOC_DAPM_MUX("Channel 5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[4]),
+ SND_SOC_DAPM_MUX("Channel 6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_input_mux_controls[5]),
+
+ SND_SOC_DAPM_MUX("PWM1 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[0]),
+ SND_SOC_DAPM_MUX("PWM2 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[1]),
+ SND_SOC_DAPM_MUX("PWM3 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[2]),
+ SND_SOC_DAPM_MUX("PWM4 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[3]),
+ SND_SOC_DAPM_MUX("PWM5 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[4]),
+ SND_SOC_DAPM_MUX("PWM6 Mux", SND_SOC_NOPM, 0, 0,
+ &tas5086_dapm_output_mux_controls[5]),
+};
+
+static const struct snd_soc_dapm_route tas5086_dapm_routes[] = {
+ /* SDIN inputs -> channel muxes */
+ { "Channel 1 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 1 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 1 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 1 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 1 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 1 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 2 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 2 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 2 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 2 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 2 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 2 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 3 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 3 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 3 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 3 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 3 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 3 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 4 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 4 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 4 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 4 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 4 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 4 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 5 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 5 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 5 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 5 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 5 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 5 Mux", "SDIN3-R", "SDIN3-R" },
+
+ { "Channel 6 Mux", "SDIN1-L", "SDIN1-L" },
+ { "Channel 6 Mux", "SDIN1-R", "SDIN1-R" },
+ { "Channel 6 Mux", "SDIN2-L", "SDIN2-L" },
+ { "Channel 6 Mux", "SDIN2-R", "SDIN2-R" },
+ { "Channel 6 Mux", "SDIN3-L", "SDIN3-L" },
+ { "Channel 6 Mux", "SDIN3-R", "SDIN3-R" },
+
+ /* Channel muxes -> PWM muxes */
+ { "PWM1 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM2 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM3 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM4 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM5 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+ { "PWM6 Mux", "Channel 1 Mux", "Channel 1 Mux" },
+
+ { "PWM1 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM2 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM3 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM4 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM5 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+ { "PWM6 Mux", "Channel 2 Mux", "Channel 2 Mux" },
+
+ { "PWM1 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM2 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM3 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM4 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM5 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+ { "PWM6 Mux", "Channel 3 Mux", "Channel 3 Mux" },
+
+ { "PWM1 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM2 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM3 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM4 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM5 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+ { "PWM6 Mux", "Channel 4 Mux", "Channel 4 Mux" },
+
+ { "PWM1 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM2 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM3 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM4 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM5 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+ { "PWM6 Mux", "Channel 5 Mux", "Channel 5 Mux" },
+
+ { "PWM1 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM2 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM3 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM4 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM5 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+ { "PWM6 Mux", "Channel 6 Mux", "Channel 6 Mux" },
+
+ /* The PWM muxes are directly connected to the PWM outputs */
+ { "PWM1", NULL, "PWM1 Mux" },
+ { "PWM2", NULL, "PWM2 Mux" },
+ { "PWM3", NULL, "PWM3 Mux" },
+ { "PWM4", NULL, "PWM4 Mux" },
+ { "PWM5", NULL, "PWM5 Mux" },
+ { "PWM6", NULL, "PWM6 Mux" },
+
+};
+
static const struct snd_soc_dai_ops tas5086_dai_ops = {
.hw_params = tas5086_hw_params,
.set_sysclk = tas5086_set_dai_sysclk,
@@ -426,13 +721,34 @@ static int tas5086_probe(struct snd_soc_codec *codec)
{
struct tas5086_private *priv = snd_soc_codec_get_drvdata(codec);
int charge_period = 1300000; /* hardware default is 1300 ms */
+ u8 pwm_start_mid_z = 0;
int i, ret;
if (of_match_device(of_match_ptr(tas5086_dt_ids), codec->dev)) {
struct device_node *of_node = codec->dev->of_node;
of_property_read_u32(of_node, "ti,charge-period", &charge_period);
+
+ for (i = 0; i < 6; i++) {
+ char name[25];
+
+ snprintf(name, sizeof(name),
+ "ti,mid-z-channel-%d", i + 1);
+
+ if (of_get_property(of_node, name, NULL) != NULL)
+ pwm_start_mid_z |= 1 << i;
+ }
}
+ /*
+ * If any of the channels is configured to start in Mid-Z mode,
+ * configure 'part 1' of the PWM starts to use Mid-Z, and tell
+ * all configured mid-z channels to start start under 'part 1'.
+ */
+ if (pwm_start_mid_z)
+ regmap_write(priv->regmap, TAS5086_PWM_START,
+ TAS5086_PWM_START_MIDZ_FOR_START_1 |
+ pwm_start_mid_z);
+
/* lookup and set split-capacitor charge period */
if (charge_period == 0) {
regmap_write(priv->regmap, TAS5086_SPLIT_CAP_CHARGE, 0);
@@ -490,6 +806,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
.resume = tas5086_soc_resume,
.controls = tas5086_controls,
.num_controls = ARRAY_SIZE(tas5086_controls),
+ .dapm_widgets = tas5086_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
+ .dapm_routes = tas5086_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
};
static const struct i2c_device_id tas5086_i2c_id[] = {
@@ -500,14 +820,16 @@ MODULE_DEVICE_TABLE(i2c, tas5086_i2c_id);
static const struct regmap_config tas5086_regmap = {
.reg_bits = 8,
- .val_bits = 8,
- .max_register = ARRAY_SIZE(tas5086_reg_defaults),
+ .val_bits = 32,
+ .max_register = TAS5086_MAX_REGISTER,
.reg_defaults = tas5086_reg_defaults,
.num_reg_defaults = ARRAY_SIZE(tas5086_reg_defaults),
.cache_type = REGCACHE_RBTREE,
.volatile_reg = tas5086_volatile_reg,
.writeable_reg = tas5086_writeable_reg,
.readable_reg = tas5086_accessible_reg,
+ .reg_read = tas5086_reg_read,
+ .reg_write = tas5086_reg_write,
};
static int tas5086_i2c_probe(struct i2c_client *i2c,
@@ -522,7 +844,7 @@ static int tas5086_i2c_probe(struct i2c_client *i2c,
if (!priv)
return -ENOMEM;
- priv->regmap = devm_regmap_init_i2c(i2c, &tas5086_regmap);
+ priv->regmap = devm_regmap_init(dev, NULL, i2c, &tas5086_regmap);
if (IS_ERR(priv->regmap)) {
ret = PTR_ERR(priv->regmap);
dev_err(&i2c->dev, "Failed to create regmap: %d\n", ret);
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 1514bf845e4..e5b92688313 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -128,10 +128,8 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
};
#define SOC_DAPM_SINGLE_AIC3X(xname, reg, shift, mask, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw_aic3x, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, mask, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, mask, invert, \
+ snd_soc_dapm_get_volsw, snd_soc_dapm_put_volsw_aic3x)
/*
* All input lines are connected when !0xf and disconnected with 0xf bit field,
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index af6d227e67b..d2a09285028 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -143,13 +143,8 @@ static int wm8400_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
}
#define WM8400_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert, tlv_array) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm8400_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm8400_outpga_put_volsw_vu, tlv_array)
static const char *wm8400_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 9d88437cdcd..fa24cedee68 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -403,10 +403,8 @@ static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
}
#define SOC_DAPM_SINGLE_W(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = wm8903_class_w_put, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8903_class_w_put)
static int wm8903_deemph[] = { 0, 32000, 44100, 48000 };
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index 3ff195c541d..4c9fb142cb2 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -603,13 +603,8 @@ SOC_DOUBLE_R("Capture Switch", WM8904_ANALOGUE_LEFT_INPUT_0,
SOC_SINGLE("High Pass Filter Switch", WM8904_ADC_DIGITAL_0, 4, 1, 0),
SOC_ENUM("High Pass Filter Mode", hpf_mode),
-
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
- .name = "ADC 128x OSR Switch",
- .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,
- .put = wm8904_adc_osr_put,
- .private_value = SOC_SINGLE_VALUE(WM8904_ANALOGUE_ADC_0, 0, 1, 0),
-},
+SOC_SINGLE_EXT("ADC 128x OSR Switch", WM8904_ANALOGUE_ADC_0, 0, 1, 0,
+ snd_soc_get_volsw, wm8904_adc_osr_put),
};
static const char *drc_path_text[] = {
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 837978e16e9..253c88bb7a4 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -151,14 +151,9 @@ static int wm899x_outpga_put_volsw_vu(struct snd_kcontrol *kcontrol,
}
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
- tlv_array) {\
- .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ tlv_array) \
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
static const char *wm8990_digital_sidetone[] =
diff --git a/sound/soc/codecs/wm8991.h b/sound/soc/codecs/wm8991.h
index 8a942efd18a..07707d8d7e2 100644
--- a/sound/soc/codecs/wm8991.h
+++ b/sound/soc/codecs/wm8991.h
@@ -822,12 +822,7 @@
#define SOC_WM899X_OUTPGA_SINGLE_R_TLV(xname, reg, shift, max, invert,\
tlv_array) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
- .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
- SNDRV_CTL_ELEM_ACCESS_READWRITE,\
- .tlv.p = (tlv_array), \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_get_volsw, .put = wm899x_outpga_put_volsw_vu, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT_TLV(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm899x_outpga_put_volsw_vu, tlv_array)
#endif /* _WM8991_H */
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index 29e95f93d48..9e13edd8129 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -289,10 +289,8 @@ static const DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
static const DECLARE_TLV_DB_SCALE(mixin_boost_tlv, 0, 900, 0);
#define WM8994_DRC_SWITCH(xname, reg, shift) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
- .put = wm8994_put_drc_sw, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, 1, 0) }
+ SOC_SINGLE_EXT(xname, reg, shift, 1, 0, \
+ snd_soc_get_volsw, wm8994_put_drc_sw)
static int wm8994_put_drc_sw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
@@ -1432,10 +1430,8 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
};
#define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = wm8994_put_class_w, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_get_volsw, wm8994_put_class_w)
static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
diff --git a/sound/soc/codecs/wm8995.h b/sound/soc/codecs/wm8995.h
index 5642121c497..508ad27fe2b 100644
--- a/sound/soc/codecs/wm8995.h
+++ b/sound/soc/codecs/wm8995.h
@@ -4237,11 +4237,8 @@
#define WM8995_SPK2_MUTE_SEQ1_WIDTH 8 /* SPK2_MUTE_SEQ1 - [7:0] */
#define WM8995_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = wm8995_put_class_w, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) \
-}
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, wm8995_put_class_w)
struct wm8995_reg_access {
u16 read;
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 05b1f346695..70ce6793c5b 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -209,7 +209,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
case AC97_RESET:
case AC97_VENDOR_ID1:
case AC97_VENDOR_ID2:
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
default:
reg = reg >> 1;
@@ -225,7 +225,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9705_reg)))
cache[reg] = val;
@@ -294,8 +294,8 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
static int wm9705_reset(struct snd_soc_codec *codec)
{
- if (soc_ac97_ops.reset) {
- soc_ac97_ops.reset(codec->ac97);
+ if (soc_ac97_ops->reset) {
+ soc_ac97_ops->reset(codec->ac97);
if (ac97_read(codec, 0) == wm9705_reg[0])
return 0; /* Success */
}
@@ -306,7 +306,7 @@ static int wm9705_reset(struct snd_soc_codec *codec)
#ifdef CONFIG_PM
static int wm9705_soc_suspend(struct snd_soc_codec *codec)
{
- soc_ac97_ops.write(codec->ac97, AC97_POWERDOWN, 0xffff);
+ soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
return 0;
}
@@ -323,7 +323,7 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
}
for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
return 0;
@@ -337,9 +337,7 @@ static int wm9705_soc_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- printk(KERN_INFO "WM9705 SoC Audio Codec\n");
-
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 8e9a6a3eeb1..c5eb746087b 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -455,7 +455,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_REC_GAIN)
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
else {
reg = reg >> 1;
@@ -472,7 +472,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
u16 *cache = codec->reg_cache;
if (reg < 0x7c)
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9712_reg)))
cache[reg] = val;
@@ -581,15 +581,15 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) == wm9712_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) != wm9712_reg[0])
goto err;
return 0;
@@ -624,7 +624,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
(i > 0x58 && i != 0x5c))
continue;
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
}
@@ -635,7 +635,7 @@ static int wm9712_soc_probe(struct snd_soc_codec *codec)
{
int ret = 0;
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0) {
printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
return ret;
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index f7afa68d8c7..a53e175c015 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -652,7 +652,7 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
reg == AC97_CD)
- return soc_ac97_ops.read(codec->ac97, reg);
+ return soc_ac97_ops->read(codec->ac97, reg);
else {
reg = reg >> 1;
@@ -668,7 +668,7 @@ static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
{
u16 *cache = codec->reg_cache;
if (reg < 0x7c)
- soc_ac97_ops.write(codec->ac97, reg, val);
+ soc_ac97_ops->write(codec->ac97, reg, val);
reg = reg >> 1;
if (reg < (ARRAY_SIZE(wm9713_reg)))
cache[reg] = val;
@@ -1095,15 +1095,15 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
{
- if (try_warm && soc_ac97_ops.warm_reset) {
- soc_ac97_ops.warm_reset(codec->ac97);
+ if (try_warm && soc_ac97_ops->warm_reset) {
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) == wm9713_reg[0])
return 1;
}
- soc_ac97_ops.reset(codec->ac97);
- if (soc_ac97_ops.warm_reset)
- soc_ac97_ops.warm_reset(codec->ac97);
+ soc_ac97_ops->reset(codec->ac97);
+ if (soc_ac97_ops->warm_reset)
+ soc_ac97_ops->warm_reset(codec->ac97);
if (ac97_read(codec, 0) != wm9713_reg[0])
return -EIO;
return 0;
@@ -1180,7 +1180,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
i == AC97_EXTENDED_MSTATUS || i > 0x66)
continue;
- soc_ac97_ops.write(codec->ac97, i, cache[i>>1]);
+ soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
}
}
@@ -1197,7 +1197,7 @@ static int wm9713_soc_probe(struct snd_soc_codec *codec)
return -ENOMEM;
snd_soc_codec_set_drvdata(codec, wm9713);
- ret = snd_soc_new_ac97_codec(codec, &soc_ac97_ops, 0);
+ ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
if (ret < 0)
goto codec_err;
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index 3470b649c0b..ddba3fea39e 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -21,6 +21,7 @@
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
+#include <linux/workqueue.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
@@ -215,6 +216,36 @@ static struct {
[WM_ADSP_FW_RX_ANC] = { .file = "rx-anc" },
};
+struct wm_coeff_ctl_ops {
+ int (*xget)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xput)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol);
+ int (*xinfo)(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo);
+};
+
+struct wm_coeff {
+ struct device *dev;
+ struct list_head ctl_list;
+ struct regmap *regmap;
+};
+
+struct wm_coeff_ctl {
+ const char *name;
+ struct snd_card *card;
+ struct wm_adsp_alg_region region;
+ struct wm_coeff_ctl_ops ops;
+ struct wm_adsp *adsp;
+ void *private;
+ unsigned int enabled:1;
+ struct list_head list;
+ void *cache;
+ size_t len;
+ unsigned int set:1;
+ struct snd_kcontrol *kcontrol;
+};
+
static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -334,6 +365,181 @@ static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
}
}
+static int wm_coeff_info(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_info *uinfo)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+
+ uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+ uinfo->count = ctl->len;
+ return 0;
+}
+
+static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
+ const void *buf, size_t len)
+{
+ struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_write(wm_coeff->regmap, reg, scratch,
+ ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to write %zu bytes to %x\n",
+ ctl->len, reg);
+ kfree(scratch);
+ return ret;
+ }
+
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(ctl->cache, p, ctl->len);
+
+ if (!ctl->enabled) {
+ ctl->set = 1;
+ return 0;
+ }
+
+ return wm_coeff_write_control(kcontrol, p, ctl->len);
+}
+
+static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
+ void *buf, size_t len)
+{
+ struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ struct wm_adsp_alg_region *region = &ctl->region;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp *adsp = ctl->adsp;
+ void *scratch;
+ int ret;
+ unsigned int reg;
+
+ mem = wm_adsp_find_region(adsp, region->type);
+ if (!mem) {
+ adsp_err(adsp, "No base for region %x\n",
+ region->type);
+ return -EINVAL;
+ }
+
+ reg = ctl->region.base;
+ reg = wm_adsp_region_to_reg(mem, reg);
+
+ scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
+ if (!scratch)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len);
+ if (ret) {
+ adsp_err(adsp, "Failed to read %zu bytes from %x\n",
+ ctl->len, reg);
+ kfree(scratch);
+ return ret;
+ }
+
+ memcpy(buf, scratch, ctl->len);
+ kfree(scratch);
+
+ return 0;
+}
+
+static int wm_coeff_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
+ char *p = ucontrol->value.bytes.data;
+
+ memcpy(p, ctl->cache, ctl->len);
+ return 0;
+}
+
+static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff,
+ struct wm_coeff_ctl *ctl,
+ const struct snd_kcontrol_new *kctl)
+{
+ int ret;
+ struct snd_kcontrol *kcontrol;
+
+ kcontrol = snd_ctl_new1(kctl, wm_coeff);
+ ret = snd_ctl_add(ctl->card, kcontrol);
+ if (ret < 0) {
+ dev_err(wm_coeff->dev, "Failed to add %s: %d\n",
+ kctl->name, ret);
+ return ret;
+ }
+ ctl->kcontrol = kcontrol;
+ return 0;
+}
+
+struct wmfw_ctl_work {
+ struct wm_coeff *wm_coeff;
+ struct wm_coeff_ctl *ctl;
+ struct work_struct work;
+};
+
+static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
+ struct wm_coeff_ctl *ctl)
+{
+ struct snd_kcontrol_new *kcontrol;
+ int ret;
+
+ if (!wm_coeff || !ctl || !ctl->name || !ctl->card)
+ return -EINVAL;
+
+ kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
+ if (!kcontrol)
+ return -ENOMEM;
+ kcontrol->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+
+ kcontrol->name = ctl->name;
+ kcontrol->info = wm_coeff_info;
+ kcontrol->get = wm_coeff_get;
+ kcontrol->put = wm_coeff_put;
+ kcontrol->private_value = (unsigned long)ctl;
+
+ ret = wm_coeff_add_kcontrol(wm_coeff,
+ ctl, kcontrol);
+ if (ret < 0)
+ goto err_kcontrol;
+
+ kfree(kcontrol);
+
+ list_add(&ctl->list, &wm_coeff->ctl_list);
+ return 0;
+
+err_kcontrol:
+ kfree(kcontrol);
+ return ret;
+}
+
static int wm_adsp_load(struct wm_adsp *dsp)
{
LIST_HEAD(buf_list);
@@ -547,7 +753,157 @@ out:
return ret;
}
-static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &wm_coeff->ctl_list,
+ list) {
+ if (!ctl->enabled || ctl->set)
+ continue;
+ ret = wm_coeff_read_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff)
+{
+ struct wm_coeff_ctl *ctl;
+ int ret;
+
+ list_for_each_entry(ctl, &wm_coeff->ctl_list,
+ list) {
+ if (!ctl->enabled)
+ continue;
+ if (ctl->set) {
+ ret = wm_coeff_write_control(ctl->kcontrol,
+ ctl->cache,
+ ctl->len);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void wm_adsp_ctl_work(struct work_struct *work)
+{
+ struct wmfw_ctl_work *ctl_work = container_of(work,
+ struct wmfw_ctl_work,
+ work);
+
+ wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl);
+ kfree(ctl_work);
+}
+
+static int wm_adsp_create_control(struct snd_soc_codec *codec,
+ const struct wm_adsp_alg_region *region)
+
+{
+ struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
+ struct wm_coeff_ctl *ctl;
+ struct wmfw_ctl_work *ctl_work;
+ char *name;
+ char *region_name;
+ int ret;
+
+ name = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!name)
+ return -ENOMEM;
+
+ switch (region->type) {
+ case WMFW_ADSP1_PM:
+ region_name = "PM";
+ break;
+ case WMFW_ADSP1_DM:
+ region_name = "DM";
+ break;
+ case WMFW_ADSP2_XM:
+ region_name = "XM";
+ break;
+ case WMFW_ADSP2_YM:
+ region_name = "YM";
+ break;
+ case WMFW_ADSP1_ZM:
+ region_name = "ZM";
+ break;
+ default:
+ ret = -EINVAL;
+ goto err_name;
+ }
+
+ snprintf(name, PAGE_SIZE, "DSP%d %s %x",
+ dsp->num, region_name, region->alg);
+
+ list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+ list) {
+ if (!strcmp(ctl->name, name)) {
+ if (!ctl->enabled)
+ ctl->enabled = 1;
+ goto found;
+ }
+ }
+
+ ctl = kzalloc(sizeof(*ctl), GFP_KERNEL);
+ if (!ctl) {
+ ret = -ENOMEM;
+ goto err_name;
+ }
+ ctl->region = *region;
+ ctl->name = kmemdup(name, strlen(name) + 1, GFP_KERNEL);
+ if (!ctl->name) {
+ ret = -ENOMEM;
+ goto err_ctl;
+ }
+ ctl->enabled = 1;
+ ctl->set = 0;
+ ctl->ops.xget = wm_coeff_get;
+ ctl->ops.xput = wm_coeff_put;
+ ctl->card = codec->card->snd_card;
+ ctl->adsp = dsp;
+
+ ctl->len = region->len;
+ ctl->cache = kzalloc(ctl->len, GFP_KERNEL);
+ if (!ctl->cache) {
+ ret = -ENOMEM;
+ goto err_ctl_name;
+ }
+
+ ctl_work = kzalloc(sizeof(*ctl_work), GFP_KERNEL);
+ if (!ctl_work) {
+ ret = -ENOMEM;
+ goto err_ctl_cache;
+ }
+
+ ctl_work->wm_coeff = dsp->wm_coeff;
+ ctl_work->ctl = ctl;
+ INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
+ schedule_work(&ctl_work->work);
+
+found:
+ kfree(name);
+
+ return 0;
+
+err_ctl_cache:
+ kfree(ctl->cache);
+err_ctl_name:
+ kfree(ctl->name);
+err_ctl:
+ kfree(ctl);
+err_name:
+ kfree(name);
+ return ret;
+}
+
+static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
{
struct regmap *regmap = dsp->regmap;
struct wmfw_adsp1_id_hdr adsp1_id;
@@ -730,7 +1086,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_DM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].dm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
+ region->len -= be32_to_cpu(adsp1_alg[i].dm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -738,7 +1103,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP1_ZM;
region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
region->base = be32_to_cpu(adsp1_alg[i].zm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp1_alg[i].zm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp1_alg[i].alg.id));
+ }
break;
case WMFW_ADSP2:
@@ -758,7 +1132,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_XM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].xm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
+ region->len -= be32_to_cpu(adsp2_alg[i].xm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -766,7 +1149,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_YM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].ym);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
+ region->len -= be32_to_cpu(adsp2_alg[i].ym);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
region = kzalloc(sizeof(*region), GFP_KERNEL);
if (!region)
@@ -774,7 +1166,16 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
region->type = WMFW_ADSP2_ZM;
region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
region->base = be32_to_cpu(adsp2_alg[i].zm);
+ region->len = 0;
list_add_tail(&region->list, &dsp->alg_regions);
+ if (i + 1 < algs) {
+ region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
+ region->len -= be32_to_cpu(adsp2_alg[i].zm);
+ wm_adsp_create_control(codec, region);
+ } else {
+ adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
+ be32_to_cpu(adsp2_alg[i].alg.id));
+ }
break;
}
}
@@ -986,6 +1387,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_soc_codec *codec = w->codec;
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
+ struct wm_coeff_ctl *ctl;
int ret;
int val;
@@ -1023,7 +1425,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp);
+ ret = wm_adsp_setup_algs(dsp, codec);
if (ret != 0)
goto err;
@@ -1031,6 +1433,16 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
/* Start the core running */
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_CORE_ENA | ADSP1_START,
@@ -1047,6 +1459,11 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_SYS_ENA, 0);
+
+ list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+ list) {
+ ctl->enabled = 0;
+ }
break;
default:
@@ -1099,6 +1516,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
struct wm_adsp_alg_region *alg_region;
+ struct wm_coeff_ctl *ctl;
unsigned int val;
int ret;
@@ -1164,7 +1582,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
- ret = wm_adsp_setup_algs(dsp);
+ ret = wm_adsp_setup_algs(dsp, codec);
if (ret != 0)
goto err;
@@ -1172,6 +1590,16 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ /* Initialize caches for enabled and unset controls */
+ ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp->wm_coeff);
+ if (ret != 0)
+ goto err;
+
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START,
@@ -1209,6 +1637,11 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ret);
}
+ list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+ list) {
+ ctl->enabled = 0;
+ }
+
while (!list_empty(&dsp->alg_regions)) {
alg_region = list_first_entry(&dsp->alg_regions,
struct wm_adsp_alg_region,
@@ -1247,36 +1680,48 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
INIT_LIST_HEAD(&adsp->alg_regions);
+ adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff),
+ GFP_KERNEL);
+ if (!adsp->wm_coeff)
+ return -ENOMEM;
+ adsp->wm_coeff->regmap = adsp->regmap;
+ adsp->wm_coeff->dev = adsp->dev;
+ INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list);
+
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;
+ goto out_coeff;
}
ret = regulator_enable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
ret);
- return ret;
+ goto out_coeff;
}
ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
if (ret != 0) {
dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
ret);
- return ret;
+ goto out_coeff;
}
ret = regulator_disable(adsp->dvfs);
if (ret != 0) {
dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
ret);
- return ret;
+ goto out_coeff;
}
}
return 0;
+
+out_coeff:
+ kfree(adsp->wm_coeff);
+ return ret;
}
EXPORT_SYMBOL_GPL(wm_adsp2_init);
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index fea51462752..9f922c82536 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -30,6 +30,7 @@ struct wm_adsp_alg_region {
unsigned int alg;
int type;
unsigned int base;
+ size_t len;
};
struct wm_adsp {
@@ -55,17 +56,17 @@ struct wm_adsp {
bool running;
struct regulator *dvfs;
+
+ struct wm_coeff *wm_coeff;
};
#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 }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+ wm_adsp1_event, 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 }
+ SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
+ wm_adsp2_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
extern const struct snd_kcontrol_new wm_adsp1_fw_controls[];
extern const struct snd_kcontrol_new wm_adsp2_fw_controls[];
diff --git a/sound/soc/codecs/wm_hubs.c b/sound/soc/codecs/wm_hubs.c
index f5d81b94875..2d9e099415a 100644
--- a/sound/soc/codecs/wm_hubs.c
+++ b/sound/soc/codecs/wm_hubs.c
@@ -693,10 +693,8 @@ void wm_hubs_update_class_w(struct snd_soc_codec *codec)
EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
#define WM_HUBS_SINGLE_W(xname, reg, shift, max, invert) \
-{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
- .info = snd_soc_info_volsw, \
- .get = snd_soc_dapm_get_volsw, .put = class_w_put_volsw, \
- .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert) }
+ SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
+ snd_soc_dapm_get_volsw, class_w_put_volsw)
static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)