aboutsummaryrefslogtreecommitdiffstats
path: root/sound/soc
diff options
context:
space:
mode:
Diffstat (limited to 'sound/soc')
-rw-r--r--sound/soc/atmel/Kconfig6
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c4
-rw-r--r--sound/soc/atmel/atmel-pcm.c2
-rw-r--r--sound/soc/atmel/atmel-pcm.h6
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c14
-rw-r--r--sound/soc/atmel/sam9g20_wm8731.c6
-rw-r--r--sound/soc/codecs/ak4642.c33
-rw-r--r--sound/soc/codecs/arizona.c257
-rw-r--r--sound/soc/codecs/arizona.h26
-rw-r--r--sound/soc/codecs/cs4271.c40
-rw-r--r--sound/soc/codecs/cs42l52.c8
-rw-r--r--sound/soc/codecs/lm49453.c106
-rw-r--r--sound/soc/codecs/sgtl5000.c4
-rw-r--r--sound/soc/codecs/sta529.c9
-rw-r--r--sound/soc/codecs/tlv320aic3x.c4
-rw-r--r--sound/soc/codecs/wm2000.c35
-rw-r--r--sound/soc/codecs/wm2200.c15
-rw-r--r--sound/soc/codecs/wm5100.c6
-rw-r--r--sound/soc/codecs/wm5102.c223
-rw-r--r--sound/soc/codecs/wm5110.c114
-rw-r--r--sound/soc/codecs/wm_adsp.c526
-rw-r--r--sound/soc/codecs/wm_adsp.h18
-rw-r--r--sound/soc/codecs/wmfw.h15
-rw-r--r--sound/soc/davinci/davinci-mcasp.c2
-rw-r--r--sound/soc/dwc/designware_i2s.c4
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c21
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c22
-rw-r--r--sound/soc/fsl/imx-pcm.c32
-rw-r--r--sound/soc/fsl/imx-pcm.h18
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c2
-rw-r--r--sound/soc/soc-compress.c123
-rw-r--r--sound/soc/soc-core.c63
-rw-r--r--sound/soc/soc-dapm.c18
-rw-r--r--sound/soc/soc-pcm.c20
-rw-r--r--sound/soc/ux500/mop500.c2
35 files changed, 1421 insertions, 383 deletions
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index d1b691bf8e2d..3fdd87fa18a9 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -1,6 +1,6 @@
config SND_ATMEL_SOC
tristate "SoC Audio for the Atmel System-on-Chip"
- depends on ARCH_AT91
+ depends on HAS_IOMEM
help
Say Y or M if you want to add support for codecs attached to
the ATMEL SSC interface. You will also need
@@ -24,7 +24,7 @@ config SND_ATMEL_SOC_SSC
config SND_AT91_SOC_SAM9G20_WM8731
tristate "SoC Audio support for WM8731-based At91sam9g20 evaluation board"
- depends on ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
+ depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC && AT91_PROGRAMMABLE_CLOCKS
select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_SOC_WM8731
@@ -34,7 +34,7 @@ config SND_AT91_SOC_SAM9G20_WM8731
config SND_AT91_SOC_AFEB9260
tristate "SoC Audio support for AFEB9260 board"
- depends on ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
+ depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
select SND_ATMEL_SOC_PDC
select SND_ATMEL_SOC_SSC
select SND_SOC_TLV320AIC23
diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c
index 6a293c713a38..054ea4d9326a 100644
--- a/sound/soc/atmel/atmel-pcm-pdc.c
+++ b/sound/soc/atmel/atmel-pcm-pdc.c
@@ -159,7 +159,7 @@ static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
pr_debug("atmel-pcm: "
"hw_params: DMA for %s initialized "
- "(dma_bytes=%u, period_size=%u)\n",
+ "(dma_bytes=%zu, period_size=%zu)\n",
prtd->params->name,
runtime->dma_bytes,
prtd->period_size);
@@ -201,7 +201,7 @@ static int atmel_pcm_trigger(struct snd_pcm_substream *substream,
int ret = 0;
pr_debug("atmel-pcm:buffer_size = %ld,"
- "dma_area = %p, dma_bytes = %u\n",
+ "dma_area = %p, dma_bytes = %zu\n",
rtd->buffer_size, rtd->dma_area, rtd->dma_bytes);
switch (cmd) {
diff --git a/sound/soc/atmel/atmel-pcm.c b/sound/soc/atmel/atmel-pcm.c
index e99f1811300a..3109db7b9017 100644
--- a/sound/soc/atmel/atmel-pcm.c
+++ b/sound/soc/atmel/atmel-pcm.c
@@ -49,7 +49,7 @@ static int atmel_pcm_preallocate_dma_buffer(struct snd_pcm *pcm,
buf->private_data = NULL;
buf->area = dma_alloc_coherent(pcm->card->dev, size,
&buf->addr, GFP_KERNEL);
- pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%d\n",
+ pr_debug("atmel-pcm: alloc dma buffer: area=%p, addr=%p, size=%zu\n",
(void *)buf->area, (void *)buf->addr, size);
if (!buf->area)
diff --git a/sound/soc/atmel/atmel-pcm.h b/sound/soc/atmel/atmel-pcm.h
index bb45d20e7250..12ae814eff21 100644
--- a/sound/soc/atmel/atmel-pcm.h
+++ b/sound/soc/atmel/atmel-pcm.h
@@ -88,7 +88,8 @@ void atmel_pcm_free(struct snd_pcm *pcm);
int atmel_pcm_mmap(struct snd_pcm_substream *substream,
struct vm_area_struct *vma);
-#ifdef CONFIG_SND_ATMEL_SOC_PDC
+#if defined(CONFIG_SND_ATMEL_SOC_PDC) || \
+ defined(CONFIG_SND_ATMEL_SOC_PDC_MODULE)
int atmel_pcm_pdc_platform_register(struct device *dev);
void atmel_pcm_pdc_platform_unregister(struct device *dev);
#else
@@ -101,7 +102,8 @@ static inline void atmel_pcm_pdc_platform_unregister(struct device *dev)
}
#endif
-#ifdef CONFIG_SND_ATMEL_SOC_DMA
+#if defined(CONFIG_SND_ATMEL_SOC_DMA) || \
+ defined(CONFIG_SND_ATMEL_SOC_DMA_MODULE)
int atmel_pcm_dma_platform_register(struct device *dev);
void atmel_pcm_dma_platform_unregister(struct device *dev);
#else
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 1c7663422054..e13580d6c476 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -42,8 +42,6 @@
#include <sound/initval.h>
#include <sound/soc.h>
-#include <mach/hardware.h>
-
#include "atmel-pcm.h"
#include "atmel_ssc_dai.h"
@@ -679,15 +677,6 @@ static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
# define atmel_ssc_resume NULL
#endif /* CONFIG_PM */
-static int atmel_ssc_probe(struct snd_soc_dai *dai)
-{
- struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
-
- snd_soc_dai_set_drvdata(dai, ssc_p);
-
- return 0;
-}
-
#define ATMEL_SSC_RATES (SNDRV_PCM_RATE_8000_96000)
#define ATMEL_SSC_FORMATS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_S16_LE |\
@@ -703,7 +692,6 @@ static const struct snd_soc_dai_ops atmel_ssc_dai_ops = {
};
static struct snd_soc_dai_driver atmel_ssc_dai = {
- .probe = atmel_ssc_probe,
.suspend = atmel_ssc_suspend,
.resume = atmel_ssc_resume,
.playback = {
@@ -790,8 +778,8 @@ void atmel_ssc_put_audio(int ssc_id)
{
struct ssc_device *ssc = ssc_info[ssc_id].ssc;
- ssc_free(ssc);
asoc_ssc_exit(&ssc->pdev->dev);
+ ssc_free(ssc);
}
EXPORT_SYMBOL_GPL(atmel_ssc_put_audio);
diff --git a/sound/soc/atmel/sam9g20_wm8731.c b/sound/soc/atmel/sam9g20_wm8731.c
index da976291da9e..2d6fbd0125b9 100644
--- a/sound/soc/atmel/sam9g20_wm8731.c
+++ b/sound/soc/atmel/sam9g20_wm8731.c
@@ -305,10 +305,10 @@ static int at91sam9g20ek_audio_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
- atmel_ssc_put_audio(0);
- snd_soc_unregister_card(card);
- clk_put(mclk);
+ clk_disable(mclk);
mclk = NULL;
+ snd_soc_unregister_card(card);
+ atmel_ssc_put_audio(0);
return 0;
}
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 1f0cdab03294..2d0378709702 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -26,6 +26,7 @@
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/slab.h>
+#include <linux/of_device.h>
#include <linux/module.h>
#include <sound/soc.h>
#include <sound/initval.h>
@@ -513,12 +514,31 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4648 = {
};
#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+static struct of_device_id ak4642_of_match[];
static int ak4642_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ struct device_node *np = i2c->dev.of_node;
+ const struct snd_soc_codec_driver *driver;
+
+ driver = NULL;
+ if (np) {
+ const struct of_device_id *of_id;
+
+ of_id = of_match_device(ak4642_of_match, &i2c->dev);
+ if (of_id)
+ driver = of_id->data;
+ } else {
+ driver = (struct snd_soc_codec_driver *)id->driver_data;
+ }
+
+ if (!driver) {
+ dev_err(&i2c->dev, "no driver\n");
+ return -EINVAL;
+ }
+
return snd_soc_register_codec(&i2c->dev,
- (struct snd_soc_codec_driver *)id->driver_data,
- &ak4642_dai, 1);
+ driver, &ak4642_dai, 1);
}
static int ak4642_i2c_remove(struct i2c_client *client)
@@ -527,6 +547,14 @@ static int ak4642_i2c_remove(struct i2c_client *client)
return 0;
}
+static struct of_device_id ak4642_of_match[] = {
+ { .compatible = "asahi-kasei,ak4642", .data = &soc_codec_dev_ak4642},
+ { .compatible = "asahi-kasei,ak4643", .data = &soc_codec_dev_ak4642},
+ { .compatible = "asahi-kasei,ak4648", .data = &soc_codec_dev_ak4648},
+ {},
+};
+MODULE_DEVICE_TABLE(of, ak4642_of_match);
+
static const struct i2c_device_id ak4642_i2c_id[] = {
{ "ak4642", (kernel_ulong_t)&soc_codec_dev_ak4642 },
{ "ak4643", (kernel_ulong_t)&soc_codec_dev_ak4642 },
@@ -539,6 +567,7 @@ static struct i2c_driver ak4642_i2c_driver = {
.driver = {
.name = "ak4642-codec",
.owner = THIS_MODULE,
+ .of_match_table = ak4642_of_match,
},
.probe = ak4642_i2c_probe,
.remove = ak4642_i2c_remove,
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index adf397b9d0e6..d824c984c8a4 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -56,14 +56,14 @@
#define arizona_fll_warn(_fll, fmt, ...) \
dev_warn(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define arizona_fll_dbg(_fll, fmt, ...) \
- dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
+ dev_dbg(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
#define arizona_aif_err(_dai, fmt, ...) \
dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
#define arizona_aif_warn(_dai, fmt, ...) \
dev_warn(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
#define arizona_aif_dbg(_dai, fmt, ...) \
- dev_err(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
+ dev_dbg(_dai->dev, "AIF%d: " fmt, _dai->id, ##__VA_ARGS__)
const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
@@ -141,6 +141,30 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"ASRC1R",
"ASRC2L",
"ASRC2R",
+ "ISRC1INT1",
+ "ISRC1INT2",
+ "ISRC1INT3",
+ "ISRC1INT4",
+ "ISRC1DEC1",
+ "ISRC1DEC2",
+ "ISRC1DEC3",
+ "ISRC1DEC4",
+ "ISRC2INT1",
+ "ISRC2INT2",
+ "ISRC2INT3",
+ "ISRC2INT4",
+ "ISRC2DEC1",
+ "ISRC2DEC2",
+ "ISRC2DEC3",
+ "ISRC2DEC4",
+ "ISRC3INT1",
+ "ISRC3INT2",
+ "ISRC3INT3",
+ "ISRC3INT4",
+ "ISRC3DEC1",
+ "ISRC3DEC2",
+ "ISRC3DEC3",
+ "ISRC3DEC4",
};
EXPORT_SYMBOL_GPL(arizona_mixer_texts);
@@ -220,6 +244,30 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
0x91,
0x92,
0x93,
+ 0xa0, /* ISRC1INT1 */
+ 0xa1,
+ 0xa2,
+ 0xa3,
+ 0xa4, /* ISRC1DEC1 */
+ 0xa5,
+ 0xa6,
+ 0xa7,
+ 0xa8, /* ISRC2DEC1 */
+ 0xa9,
+ 0xaa,
+ 0xab,
+ 0xac, /* ISRC2INT1 */
+ 0xad,
+ 0xae,
+ 0xaf,
+ 0xb0, /* ISRC3DEC1 */
+ 0xb1,
+ 0xb2,
+ 0xb3,
+ 0xb4, /* ISRC3INT1 */
+ 0xb5,
+ 0xb6,
+ 0xb7,
};
EXPORT_SYMBOL_GPL(arizona_mixer_values);
@@ -275,6 +323,15 @@ const struct soc_enum arizona_lhpf4_mode =
arizona_lhpf_mode_text);
EXPORT_SYMBOL_GPL(arizona_lhpf4_mode);
+static const char *arizona_ng_hold_text[] = {
+ "30ms", "120ms", "250ms", "500ms",
+};
+
+const struct soc_enum arizona_ng_hold =
+ SOC_ENUM_SINGLE(ARIZONA_NOISE_GATE_CONTROL, ARIZONA_NGATE_HOLD_SHIFT,
+ 4, arizona_ng_hold_text);
+EXPORT_SYMBOL_GPL(arizona_ng_hold);
+
int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
int event)
{
@@ -417,6 +474,10 @@ int arizona_set_sysclk(struct snd_soc_codec *codec, int clk_id,
case 147456000:
val |= 6 << ARIZONA_SYSCLK_FREQ_SHIFT;
break;
+ case 0:
+ dev_dbg(arizona->dev, "%s cleared\n", name);
+ *clk = freq;
+ return 0;
default:
return -EINVAL;
}
@@ -446,15 +507,9 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_A:
mode = 0;
break;
- case SND_SOC_DAIFMT_DSP_B:
- mode = 1;
- break;
case SND_SOC_DAIFMT_I2S:
mode = 2;
break;
- case SND_SOC_DAIFMT_LEFT_J:
- mode = 3;
- break;
default:
arizona_aif_err(dai, "Unsupported DAI format %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
@@ -641,6 +696,9 @@ static int arizona_startup(struct snd_pcm_substream *substream,
return 0;
}
+ if (base_rate == 0)
+ return 0;
+
if (base_rate % 8000)
constraint = &arizona_44k1_constraint;
else
@@ -651,25 +709,81 @@ static int arizona_startup(struct snd_pcm_substream *substream,
constraint);
}
+static int arizona_hw_params_rate(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ int base = dai->driver->base;
+ int i, sr_val;
+
+ /*
+ * We will need to be more flexible than this in future,
+ * currently we use a single sample rate for SYSCLK.
+ */
+ for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
+ if (arizona_sr_vals[i] == params_rate(params))
+ break;
+ if (i == ARRAY_SIZE(arizona_sr_vals)) {
+ arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ sr_val = i;
+
+ switch (dai_priv->clk) {
+ case ARIZONA_CLK_SYSCLK:
+ snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
+ ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
+ if (base)
+ snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK, 0);
+ break;
+ case ARIZONA_CLK_ASYNCCLK:
+ snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
+ ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
+ if (base)
+ snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_RATE_MASK,
+ 8 << ARIZONA_AIF1_RATE_SHIFT);
+ break;
+ default:
+ arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static int arizona_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 arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
+ struct arizona *arizona = priv->arizona;
int base = dai->driver->base;
const int *rates;
- int i;
- int bclk, lrclk, wl, frame, sr_val;
+ int i, ret;
+ int chan_limit = arizona->pdata.max_channels_clocked[dai->id - 1];
+ int bclk, lrclk, wl, frame, bclk_target;
if (params_rate(params) % 8000)
rates = &arizona_44k1_bclk_rates[0];
else
rates = &arizona_48k_bclk_rates[0];
+ bclk_target = snd_soc_params_to_bclk(params);
+ if (chan_limit && chan_limit < params_channels(params)) {
+ arizona_aif_dbg(dai, "Limiting to %d channels\n", chan_limit);
+ bclk_target /= params_channels(params);
+ bclk_target *= chan_limit;
+ }
+
for (i = 0; i < ARRAY_SIZE(arizona_44k1_bclk_rates); i++) {
- if (rates[i] >= snd_soc_params_to_bclk(params) &&
+ if (rates[i] >= bclk_target &&
rates[i] % params_rate(params) == 0) {
bclk = i;
break;
@@ -681,17 +795,7 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- for (i = 0; i < ARRAY_SIZE(arizona_sr_vals); i++)
- if (arizona_sr_vals[i] == params_rate(params))
- break;
- if (i == ARRAY_SIZE(arizona_sr_vals)) {
- arizona_aif_err(dai, "Unsupported sample rate %dHz\n",
- params_rate(params));
- return -EINVAL;
- }
- sr_val = i;
-
- lrclk = snd_soc_params_to_bclk(params) / params_rate(params);
+ lrclk = rates[bclk] / params_rate(params);
arizona_aif_dbg(dai, "BCLK %dHz LRCLK %dHz\n",
rates[bclk], rates[bclk] / lrclk);
@@ -699,27 +803,9 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
wl = snd_pcm_format_width(params_format(params));
frame = wl << ARIZONA_AIF1TX_WL_SHIFT | wl;
- /*
- * We will need to be more flexible than this in future,
- * currently we use a single sample rate for SYSCLK.
- */
- switch (dai_priv->clk) {
- case ARIZONA_CLK_SYSCLK:
- snd_soc_update_bits(codec, ARIZONA_SAMPLE_RATE_1,
- ARIZONA_SAMPLE_RATE_1_MASK, sr_val);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK, 0);
- break;
- case ARIZONA_CLK_ASYNCCLK:
- snd_soc_update_bits(codec, ARIZONA_ASYNC_SAMPLE_RATE_1,
- ARIZONA_ASYNC_SAMPLE_RATE_MASK, sr_val);
- snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
- ARIZONA_AIF1_RATE_MASK, 8);
- break;
- default:
- arizona_aif_err(dai, "Invalid clock %d\n", dai_priv->clk);
- return -EINVAL;
- }
+ ret = arizona_hw_params_rate(substream, params, dai);
+ if (ret != 0)
+ return ret;
snd_soc_update_bits(codec, base + ARIZONA_AIF_BCLK_CTRL,
ARIZONA_AIF1_BCLK_FREQ_MASK, bclk);
@@ -794,11 +880,27 @@ static int arizona_dai_set_sysclk(struct snd_soc_dai *dai,
return snd_soc_dapm_sync(&codec->dapm);
}
+static int arizona_set_tristate(struct snd_soc_dai *dai, int tristate)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ int base = dai->driver->base;
+ unsigned int reg;
+
+ if (tristate)
+ reg = ARIZONA_AIF1_TRI;
+ else
+ reg = 0;
+
+ return snd_soc_update_bits(codec, base + ARIZONA_AIF_RATE_CTRL,
+ ARIZONA_AIF1_TRI, reg);
+}
+
const struct snd_soc_dai_ops arizona_dai_ops = {
.startup = arizona_startup,
.set_fmt = arizona_set_fmt,
.hw_params = arizona_hw_params,
.set_sysclk = arizona_dai_set_sysclk,
+ .set_tristate = arizona_set_tristate,
};
EXPORT_SYMBOL_GPL(arizona_dai_ops);
@@ -812,17 +914,6 @@ int arizona_init_dai(struct arizona_priv *priv, int id)
}
EXPORT_SYMBOL_GPL(arizona_init_dai);
-static irqreturn_t arizona_fll_lock(int irq, void *data)
-{
- struct arizona_fll *fll = data;
-
- arizona_fll_dbg(fll, "Lock status changed\n");
-
- complete(&fll->lock);
-
- return IRQ_HANDLED;
-}
-
static irqreturn_t arizona_fll_clock_ok(int irq, void *data)
{
struct arizona_fll *fll = data;
@@ -915,7 +1006,7 @@ static int arizona_calc_fll(struct arizona_fll *fll,
cfg->n = target / (ratio * Fref);
- if (target % Fref) {
+ if (target % (ratio * Fref)) {
gcd_fll = gcd(target, ratio * Fref);
arizona_fll_dbg(fll, "GCD=%u\n", gcd_fll);
@@ -927,6 +1018,15 @@ static int arizona_calc_fll(struct arizona_fll *fll,
cfg->lambda = 0;
}
+ /* Round down to 16bit range with cost of accuracy lost.
+ * Denominator must be bigger than numerator so we only
+ * take care of it.
+ */
+ while (cfg->lambda >= (1 << 16)) {
+ cfg->theta >>= 1;
+ cfg->lambda >>= 1;
+ }
+
arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
cfg->n, cfg->theta, cfg->lambda);
arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
@@ -1062,7 +1162,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
{
int ret;
- init_completion(&fll->lock);
init_completion(&fll->ok);
fll->id = id;
@@ -1073,13 +1172,6 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
snprintf(fll->clock_ok_name, sizeof(fll->clock_ok_name),
"FLL%d clock OK", id);
- ret = arizona_request_irq(arizona, lock_irq, fll->lock_name,
- arizona_fll_lock, fll);
- if (ret != 0) {
- dev_err(arizona->dev, "Failed to get FLL%d lock IRQ: %d\n",
- id, ret);
- }
-
ret = arizona_request_irq(arizona, ok_irq, fll->clock_ok_name,
arizona_fll_clock_ok, fll);
if (ret != 0) {
@@ -1087,10 +1179,47 @@ int arizona_init_fll(struct arizona *arizona, int id, int base, int lock_irq,
id, ret);
}
+ regmap_update_bits(arizona->regmap, fll->base + 1,
+ ARIZONA_FLL1_FREERUN, 0);
+
return 0;
}
EXPORT_SYMBOL_GPL(arizona_init_fll);
+/**
+ * arizona_set_output_mode - Set the mode of the specified output
+ *
+ * @codec: Device to configure
+ * @output: Output number
+ * @diff: True to set the output to differential mode
+ *
+ * Some systems use external analogue switches to connect more
+ * analogue devices to the CODEC than are supported by the device. In
+ * some systems this requires changing the switched output from single
+ * ended to differential mode dynamically at runtime, an operation
+ * supported using this function.
+ *
+ * Most systems have a single static configuration and should use
+ * platform data instead.
+ */
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output, bool diff)
+{
+ unsigned int reg, val;
+
+ if (output < 1 || output > 6)
+ return -EINVAL;
+
+ reg = ARIZONA_OUTPUT_PATH_CONFIG_1L + (output - 1) * 8;
+
+ if (diff)
+ val = ARIZONA_OUT1_MONO;
+ else
+ val = 0;
+
+ return snd_soc_update_bits(codec, reg, ARIZONA_OUT1_MONO, val);
+}
+EXPORT_SYMBOL_GPL(arizona_set_output_mode);
+
MODULE_DESCRIPTION("ASoC Wolfson Arizona class device support");
MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/arizona.h b/sound/soc/codecs/arizona.h
index 41dae1ed3b71..116372c91f5d 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -34,15 +34,15 @@
#define ARIZONA_FLL_SRC_MCLK1 0
#define ARIZONA_FLL_SRC_MCLK2 1
-#define ARIZONA_FLL_SRC_SLIMCLK 2
-#define ARIZONA_FLL_SRC_FLL1 3
-#define ARIZONA_FLL_SRC_FLL2 4
-#define ARIZONA_FLL_SRC_AIF1BCLK 5
-#define ARIZONA_FLL_SRC_AIF2BCLK 6
-#define ARIZONA_FLL_SRC_AIF3BCLK 7
-#define ARIZONA_FLL_SRC_AIF1LRCLK 8
-#define ARIZONA_FLL_SRC_AIF2LRCLK 9
-#define ARIZONA_FLL_SRC_AIF3LRCLK 10
+#define ARIZONA_FLL_SRC_SLIMCLK 3
+#define ARIZONA_FLL_SRC_FLL1 4
+#define ARIZONA_FLL_SRC_FLL2 5
+#define ARIZONA_FLL_SRC_AIF1BCLK 8
+#define ARIZONA_FLL_SRC_AIF2BCLK 9
+#define ARIZONA_FLL_SRC_AIF3BCLK 10
+#define ARIZONA_FLL_SRC_AIF1LRCLK 12
+#define ARIZONA_FLL_SRC_AIF2LRCLK 13
+#define ARIZONA_FLL_SRC_AIF3LRCLK 14
#define ARIZONA_MIXER_VOL_MASK 0x00FE
#define ARIZONA_MIXER_VOL_SHIFT 1
@@ -66,7 +66,7 @@ struct arizona_priv {
struct arizona_dai_priv dai[ARIZONA_MAX_DAI];
};
-#define ARIZONA_NUM_MIXER_INPUTS 75
+#define ARIZONA_NUM_MIXER_INPUTS 99
extern const unsigned int arizona_mixer_tlv[];
extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
@@ -176,6 +176,8 @@ extern const struct soc_enum arizona_lhpf2_mode;
extern const struct soc_enum arizona_lhpf3_mode;
extern const struct soc_enum arizona_lhpf4_mode;
+extern const struct soc_enum arizona_ng_hold;
+
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
@@ -195,7 +197,6 @@ struct arizona_fll {
int id;
unsigned int base;
unsigned int vco_mult;
- struct completion lock;
struct completion ok;
unsigned int fref;
unsigned int fout;
@@ -211,4 +212,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
extern int arizona_init_dai(struct arizona_priv *priv, int dai);
+int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
+ bool diff);
+
#endif
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 4f1127935fdf..2415a4118dbd 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -167,6 +167,8 @@ struct cs4271_private {
int gpio_nreset;
/* GPIO that disable serial bus, if any */
int gpio_disable;
+ /* enable soft reset workaround */
+ bool enable_soft_reset;
};
/*
@@ -325,6 +327,33 @@ static int cs4271_hw_params(struct snd_pcm_substream *substream,
int i, ret;
unsigned int ratio, val;
+ if (cs4271->enable_soft_reset) {
+ /*
+ * Put the codec in soft reset and back again in case it's not
+ * currently streaming data. This way of bringing the codec in
+ * sync to the current clocks is not explicitly documented in
+ * the data sheet, but it seems to work fine, and in contrast
+ * to a read hardware reset, we don't have to sync back all
+ * registers every time.
+ */
+
+ if ((substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
+ !dai->capture_active) ||
+ (substream->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ !dai->playback_active)) {
+ ret = snd_soc_update_bits(codec, CS4271_MODE2,
+ CS4271_MODE2_PDN,
+ CS4271_MODE2_PDN);
+ if (ret < 0)
+ return ret;
+
+ ret = snd_soc_update_bits(codec, CS4271_MODE2,
+ CS4271_MODE2_PDN, 0);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
cs4271->rate = params_rate(params);
/* Configure DAC */
@@ -474,16 +503,20 @@ 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;
+ bool amutec_eq_bmutec = false;
#ifdef CONFIG_OF
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,
+ if (of_get_property(codec->dev->of_node,
"cirrus,amutec-eq-bmutec", NULL))
- amutec_eq_bmutec = 1;
+ amutec_eq_bmutec = true;
+
+ if (of_get_property(codec->dev->of_node,
+ "cirrus,enable-soft-reset", NULL))
+ cs4271->enable_soft_reset = true;
}
#endif
@@ -492,6 +525,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
gpio_nreset = cs4271plat->gpio_nreset;
amutec_eq_bmutec = cs4271plat->amutec_eq_bmutec;
+ cs4271->enable_soft_reset = cs4271plat->enable_soft_reset;
}
if (gpio_nreset >= 0)
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 99bb1c69499e..0f6f481cec09 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -737,7 +737,7 @@ static const struct cs42l52_clk_para clk_map_table[] = {
static int cs42l52_get_clk(int mclk, int rate)
{
- int i, ret = 0;
+ int i, ret = -EINVAL;
u_int mclk1, mclk2 = 0;
for (i = 0; i < ARRAY_SIZE(clk_map_table); i++) {
@@ -749,8 +749,6 @@ static int cs42l52_get_clk(int mclk, int rate)
}
}
}
- if (ret > ARRAY_SIZE(clk_map_table))
- return -EINVAL;
return ret;
}
@@ -1040,7 +1038,7 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
int ret;
- cs42l52->beep = input_allocate_device();
+ cs42l52->beep = devm_input_allocate_device(codec->dev);
if (!cs42l52->beep) {
dev_err(codec->dev, "Failed to allocate beep device\n");
return;
@@ -1061,7 +1059,6 @@ static void cs42l52_init_beep(struct snd_soc_codec *codec)
ret = input_register_device(cs42l52->beep);
if (ret != 0) {
- input_free_device(cs42l52->beep);
cs42l52->beep = NULL;
dev_err(codec->dev, "Failed to register beep device\n");
}
@@ -1078,7 +1075,6 @@ static void cs42l52_free_beep(struct snd_soc_codec *codec)
struct cs42l52_private *cs42l52 = snd_soc_codec_get_drvdata(codec);
device_remove_file(codec->dev, &dev_attr_beep);
- input_unregister_device(cs42l52->beep);
cancel_work_sync(&cs42l52->beep_work);
cs42l52->beep = NULL;
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index d75257d40a49..e19490cfb3a8 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -111,9 +111,9 @@ static struct reg_default lm49453_reg_defs[] = {
{ 101, 0x00 },
{ 102, 0x00 },
{ 103, 0x01 },
- { 105, 0x01 },
- { 106, 0x00 },
- { 107, 0x01 },
+ { 104, 0x01 },
+ { 105, 0x00 },
+ { 106, 0x01 },
{ 107, 0x00 },
{ 108, 0x00 },
{ 109, 0x00 },
@@ -163,56 +163,25 @@ static struct reg_default lm49453_reg_defs[] = {
{ 184, 0x00 },
{ 185, 0x00 },
{ 186, 0x00 },
- { 189, 0x00 },
+ { 187, 0x00 },
{ 188, 0x00 },
- { 194, 0x00 },
- { 195, 0x00 },
- { 196, 0x00 },
- { 197, 0x00 },
- { 200, 0x00 },
- { 201, 0x00 },
- { 202, 0x00 },
- { 203, 0x00 },
- { 204, 0x00 },
- { 205, 0x00 },
- { 208, 0x00 },
+ { 189, 0x00 },
+ { 208, 0x06 },
{ 209, 0x00 },
- { 210, 0x00 },
- { 211, 0x00 },
- { 213, 0x00 },
- { 214, 0x00 },
- { 215, 0x00 },
- { 216, 0x00 },
- { 217, 0x00 },
- { 218, 0x00 },
- { 219, 0x00 },
+ { 210, 0x08 },
+ { 211, 0x54 },
+ { 212, 0x14 },
+ { 213, 0x0d },
+ { 214, 0x0d },
+ { 215, 0x14 },
+ { 216, 0x60 },
{ 221, 0x00 },
{ 222, 0x00 },
+ { 223, 0x00 },
{ 224, 0x00 },
- { 225, 0x00 },
- { 226, 0x00 },
- { 227, 0x00 },
- { 228, 0x00 },
- { 229, 0x00 },
- { 230, 0x13 },
- { 231, 0x00 },
- { 232, 0x80 },
- { 233, 0x0C },
- { 234, 0xDD },
- { 235, 0x00 },
- { 236, 0x04 },
- { 237, 0x00 },
- { 238, 0x00 },
- { 239, 0x00 },
- { 240, 0x00 },
- { 241, 0x00 },
- { 242, 0x00 },
- { 243, 0x00 },
- { 244, 0x00 },
- { 245, 0x00 },
{ 248, 0x00 },
{ 249, 0x00 },
- { 254, 0x00 },
+ { 250, 0x00 },
{ 255, 0x00 },
};
@@ -525,36 +494,41 @@ SOC_DAPM_SINGLE("Port2_2 Switch", LM49453_P0_PORT2_TX2_REG, 7, 1, 0),
};
/* TLV Declarations */
-static const DECLARE_TLV_DB_SCALE(digital_tlv, -7650, 150, 1);
-static const DECLARE_TLV_DB_SCALE(port_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(adc_dac_tlv, -7650, 150, 1);
+static const DECLARE_TLV_DB_SCALE(mic_tlv, 0, 200, 1);
+static const DECLARE_TLV_DB_SCALE(port_tlv, -1800, 600, 0);
+static const DECLARE_TLV_DB_SCALE(stn_tlv, -7200, 150, 0);
static const struct snd_kcontrol_new lm49453_sidetone_mixer_controls[] = {
/* Sidetone supports mono only */
SOC_DAPM_SINGLE_TLV("Sidetone ADCL Volume", LM49453_P0_STN_VOL_ADCL_REG,
- 0, 0x3F, 0, digital_tlv),
+ 0, 0x3F, 0, stn_tlv),
SOC_DAPM_SINGLE_TLV("Sidetone ADCR Volume", LM49453_P0_STN_VOL_ADCR_REG,
- 0, 0x3F, 0, digital_tlv),
+ 0, 0x3F, 0, stn_tlv),
SOC_DAPM_SINGLE_TLV("Sidetone DMIC1L Volume", LM49453_P0_STN_VOL_DMIC1L_REG,
- 0, 0x3F, 0, digital_tlv),
+ 0, 0x3F, 0, stn_tlv),
SOC_DAPM_SINGLE_TLV("Sidetone DMIC1R Volume", LM49453_P0_STN_VOL_DMIC1R_REG,
- 0, 0x3F, 0, digital_tlv),
+ 0, 0x3F, 0, stn_tlv),
SOC_DAPM_SINGLE_TLV("Sidetone DMIC2L Volume", LM49453_P0_STN_VOL_DMIC2L_REG,
- 0, 0x3F, 0, digital_tlv),
+ 0, 0x3F, 0, stn_tlv),
SOC_DAPM_SINGLE_TLV("Sidetone DMIC2R Volume", LM49453_P0_STN_VOL_DMIC2R_REG,
- 0, 0x3F, 0, digital_tlv),
+ 0, 0x3F, 0, stn_tlv),
};
static const struct snd_kcontrol_new lm49453_snd_controls[] = {
/* mic1 and mic2 supports mono only */
- SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_ADC_LEVELL_REG, 0, 6,
- 0, digital_tlv),
- SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_ADC_LEVELR_REG, 0, 6,
- 0, digital_tlv),
+ SOC_SINGLE_TLV("Mic1 Volume", LM49453_P0_MICL_REG, 0, 15, 0, mic_tlv),
+ SOC_SINGLE_TLV("Mic2 Volume", LM49453_P0_MICR_REG, 0, 15, 0, mic_tlv),
+
+ SOC_SINGLE_TLV("ADCL Volume", LM49453_P0_ADC_LEVELL_REG, 0, 63,
+ 0, adc_dac_tlv),
+ SOC_SINGLE_TLV("ADCR Volume", LM49453_P0_ADC_LEVELR_REG, 0, 63,
+ 0, adc_dac_tlv),
SOC_DOUBLE_R_TLV("DMIC1 Volume", LM49453_P0_DMIC1_LEVELL_REG,
- LM49453_P0_DMIC1_LEVELR_REG, 0, 6, 0, digital_tlv),
+ LM49453_P0_DMIC1_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
SOC_DOUBLE_R_TLV("DMIC2 Volume", LM49453_P0_DMIC2_LEVELL_REG,
- LM49453_P0_DMIC2_LEVELR_REG, 0, 6, 0, digital_tlv),
+ LM49453_P0_DMIC2_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
SOC_DAPM_ENUM("Mic2Mode", lm49453_mic2mode_enum),
SOC_DAPM_ENUM("DMIC12 SRC", lm49453_dmic12_cfg_enum),
@@ -569,16 +543,16 @@ static const struct snd_kcontrol_new lm49453_snd_controls[] = {
2, 1, 0),
SOC_DOUBLE_R_TLV("DAC HP Volume", LM49453_P0_DAC_HP_LEVELL_REG,
- LM49453_P0_DAC_HP_LEVELR_REG, 0, 6, 0, digital_tlv),
+ LM49453_P0_DAC_HP_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
SOC_DOUBLE_R_TLV("DAC LO Volume", LM49453_P0_DAC_LO_LEVELL_REG,
- LM49453_P0_DAC_LO_LEVELR_REG, 0, 6, 0, digital_tlv),
+ LM49453_P0_DAC_LO_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
SOC_DOUBLE_R_TLV("DAC LS Volume", LM49453_P0_DAC_LS_LEVELL_REG,
- LM49453_P0_DAC_LS_LEVELR_REG, 0, 6, 0, digital_tlv),
+ LM49453_P0_DAC_LS_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
SOC_DOUBLE_R_TLV("DAC HA Volume", LM49453_P0_DAC_HA_LEVELL_REG,
- LM49453_P0_DAC_HA_LEVELR_REG, 0, 6, 0, digital_tlv),
+ LM49453_P0_DAC_HA_LEVELR_REG, 0, 63, 0, adc_dac_tlv),
SOC_SINGLE_TLV("EP Volume", LM49453_P0_DAC_LS_LEVELL_REG,
- 0, 6, 0, digital_tlv),
+ 0, 63, 0, adc_dac_tlv),
SOC_SINGLE_TLV("PORT1_1_RX_LVL Volume", LM49453_P0_PORT1_RX_LVL1_REG,
0, 3, 0, port_tlv),
@@ -1218,7 +1192,7 @@ static int lm49453_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
}
snd_soc_update_bits(codec, LM49453_P0_AUDIO_PORT1_BASIC_REG,
- LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(1)|BIT(5),
+ LM49453_AUDIO_PORT1_BASIC_FMT_MASK|BIT(0)|BIT(5),
(aif_val | mode | clk_phase));
snd_soc_write(codec, LM49453_P0_AUDIO_PORT1_RX_MSB_REG, clk_shift);
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index cb1675cd8e1c..92bbfec9b107 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -401,7 +401,7 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
5, 1, 0),
SOC_SINGLE_TLV("Mic Volume", SGTL5000_CHIP_MIC_CTRL,
- 0, 4, 0, mic_gain_tlv),
+ 0, 3, 0, mic_gain_tlv),
};
/* mute the codec used by alsa core */
@@ -1344,7 +1344,7 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
SGTL5000_HP_ZCD_EN |
SGTL5000_ADC_ZCD_EN);
- snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 0);
+ snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 2);
/*
* disable DAP
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index ab355c4f0b2d..40c07be9b581 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -74,9 +74,10 @@
SNDRV_PCM_FMTBIT_S32_LE)
#define S2PC_VALUE 0x98
#define CLOCK_OUT 0x60
-#define LEFT_J_DATA_FORMAT 0x10
-#define I2S_DATA_FORMAT 0x12
-#define RIGHT_J_DATA_FORMAT 0x14
+#define DATA_FORMAT_MSK 0x0E
+#define LEFT_J_DATA_FORMAT 0x00
+#define I2S_DATA_FORMAT 0x02
+#define RIGHT_J_DATA_FORMAT 0x04
#define CODEC_MUTE_VAL 0x80
#define POWER_CNTLMSAK 0x40
@@ -289,7 +290,7 @@ static int sta529_set_dai_fmt(struct snd_soc_dai *codec_dai, u32 fmt)
return -EINVAL;
}
- snd_soc_update_bits(codec, STA529_S2PCFG0, 0x0D, mode);
+ snd_soc_update_bits(codec, STA529_S2PCFG0, DATA_FORMAT_MSK, mode);
return 0;
}
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index 5708a973a776..49891432af74 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1210,13 +1210,13 @@ static struct snd_soc_dai_driver aic3x_dai = {
.name = "tlv320aic3x-hifi",
.playback = {
.stream_name = "Playback",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = AIC3X_RATES,
.formats = AIC3X_FORMATS,},
.capture = {
.stream_name = "Capture",
- .channels_min = 1,
+ .channels_min = 2,
.channels_max = 2,
.rates = AIC3X_RATES,
.formats = AIC3X_FORMATS,},
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index 1cbe88f01d63..eb96b8768098 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -76,6 +76,8 @@ struct wm2000_priv {
int anc_download_size;
char *anc_download;
+
+ struct mutex lock;
};
static int wm2000_write(struct i2c_client *i2c, unsigned int reg,
@@ -209,9 +211,9 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue)
ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY);
if (wm2000->speech_clarity)
- ret &= ~WM2000_SPEECH_CLARITY;
- else
ret |= WM2000_SPEECH_CLARITY;
+ else
+ ret &= ~WM2000_SPEECH_CLARITY;
wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret);
wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33);
@@ -599,13 +601,20 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
int anc_active = ucontrol->value.enumerated.item[0];
+ int ret;
if (anc_active > 1)
return -EINVAL;
+ mutex_lock(&wm2000->lock);
+
wm2000->anc_active = anc_active;
- return wm2000_anc_set_mode(wm2000);
+ ret = wm2000_anc_set_mode(wm2000);
+
+ mutex_unlock(&wm2000->lock);
+
+ return ret;
}
static int wm2000_speaker_get(struct snd_kcontrol *kcontrol,
@@ -625,13 +634,20 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
int val = ucontrol->value.enumerated.item[0];
+ int ret;
if (val > 1)
return -EINVAL;
+ mutex_lock(&wm2000->lock);
+
wm2000->spk_ena = val;
- return wm2000_anc_set_mode(wm2000);
+ ret = wm2000_anc_set_mode(wm2000);
+
+ mutex_unlock(&wm2000->lock);
+
+ return ret;
}
static const struct snd_kcontrol_new wm2000_controls[] = {
@@ -648,6 +664,9 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = w->codec;
struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
+ int ret;
+
+ mutex_lock(&wm2000->lock);
if (SND_SOC_DAPM_EVENT_ON(event))
wm2000->anc_eng_ena = 1;
@@ -655,7 +674,11 @@ static int wm2000_anc_power_event(struct snd_soc_dapm_widget *w,
if (SND_SOC_DAPM_EVENT_OFF(event))
wm2000->anc_eng_ena = 0;
- return wm2000_anc_set_mode(wm2000);
+ ret = wm2000_anc_set_mode(wm2000);
+
+ mutex_unlock(&wm2000->lock);
+
+ return ret;
}
static const struct snd_soc_dapm_widget wm2000_dapm_widgets[] = {
@@ -782,6 +805,8 @@ static int wm2000_i2c_probe(struct i2c_client *i2c,
return -ENOMEM;
}
+ mutex_init(&wm2000->lock);
+
dev_set_drvdata(&i2c->dev, wm2000);
wm2000->regmap = devm_regmap_init_i2c(i2c, &wm2000_regmap);
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index afcf31df77e0..d5371e0d8909 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -1019,8 +1019,6 @@ static const char *wm2200_mixer_texts[] = {
"EQR",
"LHPF1",
"LHPF2",
- "LHPF3",
- "LHPF4",
"DSP1.1",
"DSP1.2",
"DSP1.3",
@@ -1053,7 +1051,6 @@ static int wm2200_mixer_values[] = {
0x25,
0x50, /* EQ */
0x51,
- 0x52,
0x60, /* LHPF1 */
0x61, /* LHPF2 */
0x68, /* DSP1 */
@@ -1129,9 +1126,9 @@ SOC_DOUBLE_R_TLV("IN3 Volume", WM2200_IN3L_CONTROL, WM2200_IN3R_CONTROL,
SOC_DOUBLE_R("IN1 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L,
WM2200_ADC_DIGITAL_VOLUME_1R, WM2200_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L,
+SOC_DOUBLE_R("IN2 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_2L,
WM2200_ADC_DIGITAL_VOLUME_2R, WM2200_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_1L,
+SOC_DOUBLE_R("IN3 Digital Switch", WM2200_ADC_DIGITAL_VOLUME_3L,
WM2200_ADC_DIGITAL_VOLUME_3R, WM2200_IN3L_MUTE_SHIFT, 1, 1),
SOC_DOUBLE_R_TLV("IN1 Digital Volume", WM2200_ADC_DIGITAL_VOLUME_1L,
@@ -1566,15 +1563,9 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_A:
fmt_val = 0;
break;
- case SND_SOC_DAIFMT_DSP_B:
- fmt_val = 1;
- break;
case SND_SOC_DAIFMT_I2S:
fmt_val = 2;
break;
- case SND_SOC_DAIFMT_LEFT_J:
- fmt_val = 3;
- break;
default:
dev_err(codec->dev, "Unsupported DAI format %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
@@ -1626,7 +1617,7 @@ static int wm2200_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
WM2200_AIF1TX_LRCLK_MSTR | WM2200_AIF1TX_LRCLK_INV,
lrclk);
snd_soc_update_bits(codec, WM2200_AUDIO_IF_1_5,
- WM2200_AIF1_FMT_MASK << 1, fmt_val << 1);
+ WM2200_AIF1_FMT_MASK, fmt_val);
return 0;
}
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 5a5f36936235..54397a508073 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -1279,15 +1279,9 @@ static int wm5100_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
case SND_SOC_DAIFMT_DSP_A:
mask = 0;
break;
- case SND_SOC_DAIFMT_DSP_B:
- mask = 1;
- break;
case SND_SOC_DAIFMT_I2S:
mask = 2;
break;
- case SND_SOC_DAIFMT_LEFT_J:
- mask = 3;
- break;
default:
dev_err(codec->dev, "Unsupported DAI format %d\n",
fmt & SND_SOC_DAIFMT_FORMAT_MASK);
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index 688ade080589..5e85b645f2eb 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -36,12 +36,16 @@
struct wm5102_priv {
struct arizona_priv core;
struct arizona_fll fll[2];
+
+ unsigned int spk_ena:2;
+ unsigned int spk_ena_pending:1;
};
static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
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 DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
static const struct wm_adsp_region wm5102_dsp1_regions[] = {
{ .type = WMFW_ADSP2_PM, .base = 0x100000 },
@@ -600,6 +604,17 @@ static int wm5102_sysclk_ev(struct snd_soc_dapm_widget *w,
return 0;
}
+#define WM5102_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG EPOUT Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
static const struct snd_kcontrol_new wm5102_snd_controls[] = {
SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
ARIZONA_IN1_OSR_SHIFT, 1, 0),
@@ -608,32 +623,44 @@ SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
ARIZONA_IN3_OSR_SHIFT, 1, 0),
-SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
- ARIZONA_IN1R_CONTROL,
- ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
- ARIZONA_IN2R_CONTROL,
- ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
- ARIZONA_IN3R_CONTROL,
- ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-
-SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
- ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-
-SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
- ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+ ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+ ARIZONA_IN3R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+ ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+ ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
@@ -771,6 +798,22 @@ SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM5102_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5102_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5102_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5102_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5102_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5102_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5102_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5102_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5102_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+
ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
@@ -787,6 +830,47 @@ ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
};
+static int wm5102_spk_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol,
+ int event)
+{
+ struct snd_soc_codec *codec = w->codec;
+ struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+ struct wm5102_priv *wm5102 = snd_soc_codec_get_drvdata(codec);
+
+ if (arizona->rev < 1)
+ return 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (!wm5102->spk_ena) {
+ snd_soc_write(codec, 0x4f5, 0x25a);
+ wm5102->spk_ena_pending = true;
+ }
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ if (wm5102->spk_ena_pending) {
+ msleep(75);
+ snd_soc_write(codec, 0x4f5, 0xda);
+ wm5102->spk_ena_pending = false;
+ wm5102->spk_ena++;
+ }
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wm5102->spk_ena--;
+ if (!wm5102->spk_ena)
+ snd_soc_write(codec, 0x4f5, 0x25a);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ if (!wm5102->spk_ena)
+ snd_soc_write(codec, 0x4f5, 0x0da);
+ break;
+ }
+
+ return 0;
+}
+
+
ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
@@ -836,6 +920,18 @@ 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_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
ARIZONA_MIXER_ENUMS(DSP1L, ARIZONA_DSP1LMIX_INPUT_1_SOURCE);
ARIZONA_MIXER_ENUMS(DSP1R, ARIZONA_DSP1RMIX_INPUT_1_SOURCE);
@@ -852,8 +948,7 @@ static const unsigned int wm5102_aec_loopback_values[] = {
static const struct soc_enum wm5102_aec_loopback =
SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
- ARIZONA_AEC_LOOPBACK_SRC_SHIFT,
- ARIZONA_AEC_LOOPBACK_SRC_MASK,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
ARRAY_SIZE(wm5102_aec_loopback_texts),
wm5102_aec_loopback_texts,
wm5102_aec_loopback_values);
@@ -959,6 +1054,26 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+ ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+ ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
@@ -1034,10 +1149,10 @@ SND_SOC_DAPM_PGA_E("OUT3L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT4L", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ ARIZONA_OUT4L_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT4R", ARIZONA_OUTPUT_ENABLES_1,
- ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+ ARIZONA_OUT4R_ENA_SHIFT, 0, NULL, 0, wm5102_spk_ev,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
@@ -1095,6 +1210,18 @@ ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
WM_ADSP2("DSP1", 0),
SND_SOC_DAPM_OUTPUT("HPOUT1L"),
@@ -1150,6 +1277,14 @@ SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
{ name, "ASRC1R", "ASRC1R" }, \
{ name, "ASRC2L", "ASRC2L" }, \
{ name, "ASRC2R", "ASRC2R" }, \
+ { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+ { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+ { name, "ISRC1INT1", "ISRC1INT1" }, \
+ { name, "ISRC1INT2", "ISRC1INT2" }, \
+ { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+ { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+ { name, "ISRC2INT1", "ISRC2INT1" }, \
+ { name, "ISRC2INT2", "ISRC2INT2" }, \
{ name, "DSP1.1", "DSP1" }, \
{ name, "DSP1.2", "DSP1" }, \
{ name, "DSP1.3", "DSP1" }, \
@@ -1246,6 +1381,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "ASRC2L", NULL, "ASRC2L Input" },
{ "ASRC2R", NULL, "ASRC2R Input" },
+ { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" },
+ { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" },
+
+ { "ISRC1INT1", NULL, "ISRC1INT1 Input" },
+ { "ISRC1INT2", NULL, "ISRC1INT2 Input" },
+
+ { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" },
+ { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" },
+
+ { "ISRC2INT1", NULL, "ISRC2INT1 Input" },
+ { "ISRC2INT2", NULL, "ISRC2INT2 Input" },
+
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1293,6 +1440,18 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
ARIZONA_MUX_ROUTES("ASRC2L"),
ARIZONA_MUX_ROUTES("ASRC2R"),
+ ARIZONA_MUX_ROUTES("ISRC1INT1"),
+ ARIZONA_MUX_ROUTES("ISRC1INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC1DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC1DEC2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2INT1"),
+ ARIZONA_MUX_ROUTES("ISRC2INT2"),
+
+ ARIZONA_MUX_ROUTES("ISRC2DEC1"),
+ ARIZONA_MUX_ROUTES("ISRC2DEC2"),
+
ARIZONA_DSP_ROUTES("DSP1"),
{ "AEC Loopback", "HPOUT1L", "OUT1L" },
@@ -1420,6 +1579,10 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
if (ret != 0)
return ret;
+ ret = snd_soc_add_codec_controls(codec, wm_adsp_fw_controls, 1);
+ if (ret != 0)
+ return ret;
+
snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
priv->core.arizona->dapm = &codec->dapm;
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index ae80c8c28536..23199372d518 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -41,6 +41,21 @@ static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
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 DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define WM5110_NG_SRC(name, base) \
+ SOC_SINGLE(name " NG HPOUT1L Switch", base, 0, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT1R Switch", base, 1, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2L Switch", base, 2, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT2R Switch", base, 3, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3L Switch", base, 4, 1, 0), \
+ SOC_SINGLE(name " NG HPOUT3R Switch", base, 5, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTL Switch", base, 6, 1, 0), \
+ SOC_SINGLE(name " NG SPKOUTR Switch", base, 7, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2L Switch", base, 10, 1, 0), \
+ SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
static const struct snd_kcontrol_new wm5110_snd_controls[] = {
SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
@@ -52,37 +67,52 @@ SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL,
ARIZONA_IN4_OSR_SHIFT, 1, 0),
-SOC_DOUBLE_R_RANGE_TLV("IN1 Volume", ARIZONA_IN1L_CONTROL,
- ARIZONA_IN1R_CONTROL,
- ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
- ARIZONA_IN2R_CONTROL,
- ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_DOUBLE_R_RANGE_TLV("IN3 Volume", ARIZONA_IN3L_CONTROL,
- ARIZONA_IN3R_CONTROL,
- ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-
-SOC_DOUBLE_R("IN1 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN2 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN3 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
- ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
-SOC_DOUBLE_R("IN4 Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L,
- ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_MUTE_SHIFT, 1, 1),
-
-SOC_DOUBLE_R_TLV("IN1 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
- ARIZONA_ADC_DIGITAL_VOLUME_1R, ARIZONA_IN1L_DIG_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
- ARIZONA_ADC_DIGITAL_VOLUME_2R, ARIZONA_IN2L_DIG_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN3 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
- ARIZONA_ADC_DIGITAL_VOLUME_3R, ARIZONA_IN3L_DIG_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
-SOC_DOUBLE_R_TLV("IN4 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
- ARIZONA_ADC_DIGITAL_VOLUME_4R, ARIZONA_IN4L_DIG_VOL_SHIFT,
- 0xbf, 0, digital_tlv),
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+ ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+ ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+ ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+ ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+ ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+ ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_SINGLE("IN1L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN1R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN2R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+ ARIZONA_IN3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN3R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+ ARIZONA_IN3R_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN4L Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+ ARIZONA_IN4L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("IN4R Digital Switch", ARIZONA_ADC_DIGITAL_VOLUME_4R,
+ ARIZONA_IN4R_MUTE_SHIFT, 1, 1),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+ ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+ ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+ ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+ ARIZONA_IN2R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3L,
+ ARIZONA_IN3L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN3R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_3R,
+ ARIZONA_IN3R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4L,
+ ARIZONA_IN4L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN4R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_4R,
+ ARIZONA_IN4R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
@@ -263,6 +293,25 @@ SOC_DOUBLE("SPKDAT2 Switch", ARIZONA_PDM_SPK2_CTRL_1, ARIZONA_SPK2L_MUTE_SHIFT,
SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+ ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM5110_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM5110_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM5110_NG_SRC("HPOUT2L", ARIZONA_NOISE_GATE_SELECT_2L),
+WM5110_NG_SRC("HPOUT2R", ARIZONA_NOISE_GATE_SELECT_2R),
+WM5110_NG_SRC("HPOUT3L", ARIZONA_NOISE_GATE_SELECT_3L),
+WM5110_NG_SRC("HPOUT3R", ARIZONA_NOISE_GATE_SELECT_3R),
+WM5110_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM5110_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM5110_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM5110_NG_SRC("SPKDAT1R", ARIZONA_NOISE_GATE_SELECT_5R),
+WM5110_NG_SRC("SPKDAT2L", ARIZONA_NOISE_GATE_SELECT_6L),
+WM5110_NG_SRC("SPKDAT2R", ARIZONA_NOISE_GATE_SELECT_6R),
+
ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
@@ -344,8 +393,7 @@ static const unsigned int wm5110_aec_loopback_values[] = {
static const struct soc_enum wm5110_aec_loopback =
SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
- ARIZONA_AEC_LOOPBACK_SRC_SHIFT,
- ARIZONA_AEC_LOOPBACK_SRC_MASK,
+ ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
ARRAY_SIZE(wm5110_aec_loopback_texts),
wm5110_aec_loopback_texts,
wm5110_aec_loopback_values);
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index ffc89fab96fb..f3f7e75f8628 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -15,6 +15,7 @@
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/firmware.h>
+#include <linux/list.h>
#include <linux/pm.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -103,9 +104,19 @@
#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
+/*
+ * ADSP1 Control 31
+ */
+#define ADSP1_CLK_SEL_MASK 0x0007 /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
+#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
+
+#define ADSP2_CONTROL 0x0
+#define ADSP2_CLOCKING 0x1
+#define ADSP2_STATUS1 0x4
+#define ADSP2_WDMA_CONFIG_1 0x30
+#define ADSP2_WDMA_CONFIG_2 0x31
+#define ADSP2_RDMA_CONFIG_1 0x34
/*
* ADSP2 Control
@@ -143,6 +154,109 @@
#define ADSP2_RAM_RDY_SHIFT 0
#define ADSP2_RAM_RDY_WIDTH 1
+struct wm_adsp_buf {
+ struct list_head list;
+ void *buf;
+};
+
+static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
+ struct list_head *list)
+{
+ struct wm_adsp_buf *buf = kzalloc(sizeof(*buf), GFP_KERNEL);
+
+ if (buf == NULL)
+ return NULL;
+
+ buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
+ if (!buf->buf) {
+ kfree(buf);
+ return NULL;
+ }
+
+ if (list)
+ list_add_tail(&buf->list, list);
+
+ return buf;
+}
+
+static void wm_adsp_buf_free(struct list_head *list)
+{
+ while (!list_empty(list)) {
+ struct wm_adsp_buf *buf = list_first_entry(list,
+ struct wm_adsp_buf,
+ list);
+ list_del(&buf->list);
+ kfree(buf->buf);
+ kfree(buf);
+ }
+}
+
+#define WM_ADSP_NUM_FW 4
+
+static const char *wm_adsp_fw_text[WM_ADSP_NUM_FW] = {
+ "MBC/VSS", "Tx", "Tx Speaker", "Rx ANC"
+};
+
+static struct {
+ const char *file;
+} wm_adsp_fw[WM_ADSP_NUM_FW] = {
+ { .file = "mbc-vss" },
+ { .file = "tx" },
+ { .file = "tx-spk" },
+ { .file = "rx-anc" },
+};
+
+static int wm_adsp_fw_get(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+ ucontrol->value.integer.value[0] = adsp[e->shift_l].fw;
+
+ return 0;
+}
+
+static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+ struct wm_adsp *adsp = snd_soc_codec_get_drvdata(codec);
+
+ if (ucontrol->value.integer.value[0] == adsp[e->shift_l].fw)
+ return 0;
+
+ if (ucontrol->value.integer.value[0] >= WM_ADSP_NUM_FW)
+ return -EINVAL;
+
+ if (adsp[e->shift_l].running)
+ return -EBUSY;
+
+ adsp[e->shift_l].fw = ucontrol->value.integer.value[0];
+
+ return 0;
+}
+
+static const struct soc_enum wm_adsp_fw_enum[] = {
+ SOC_ENUM_SINGLE(0, 0, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+};
+
+const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
+ SOC_ENUM_EXT("DSP1 Firmware", wm_adsp_fw_enum[0],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP2 Firmware", wm_adsp_fw_enum[1],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP3 Firmware", wm_adsp_fw_enum[2],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+};
+EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
int type)
@@ -156,8 +270,29 @@ static struct wm_adsp_region const *wm_adsp_find_region(struct wm_adsp *dsp,
return NULL;
}
+static unsigned int wm_adsp_region_to_reg(struct wm_adsp_region const *region,
+ unsigned int offset)
+{
+ switch (region->type) {
+ case WMFW_ADSP1_PM:
+ return region->base + (offset * 3);
+ case WMFW_ADSP1_DM:
+ return region->base + (offset * 2);
+ case WMFW_ADSP2_XM:
+ return region->base + (offset * 2);
+ case WMFW_ADSP2_YM:
+ return region->base + (offset * 2);
+ case WMFW_ADSP1_ZM:
+ return region->base + (offset * 2);
+ default:
+ WARN_ON(NULL != "Unknown memory region type");
+ return offset;
+ }
+}
+
static int wm_adsp_load(struct wm_adsp *dsp)
{
+ LIST_HEAD(buf_list);
const struct firmware *firmware;
struct regmap *regmap = dsp->regmap;
unsigned int pos = 0;
@@ -169,6 +304,7 @@ static int wm_adsp_load(struct wm_adsp *dsp)
const struct wm_adsp_region *mem;
const char *region_name;
char *file, *text;
+ struct wm_adsp_buf *buf;
unsigned int reg;
int regions = 0;
int ret, offset, type, sizes;
@@ -177,7 +313,8 @@ static int wm_adsp_load(struct wm_adsp *dsp)
if (file == NULL)
return -ENOMEM;
- snprintf(file, PAGE_SIZE, "%s-dsp%d.wmfw", dsp->part, dsp->num);
+ snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.wmfw", dsp->part, dsp->num,
+ wm_adsp_fw[dsp->fw].file);
file[PAGE_SIZE - 1] = '\0';
ret = request_firmware(&firmware, file, dsp->dev);
@@ -282,27 +419,27 @@ static int wm_adsp_load(struct wm_adsp *dsp)
case WMFW_ADSP1_PM:
BUG_ON(!mem);
region_name = "PM";
- reg = mem->base + (offset * 3);
+ reg = wm_adsp_region_to_reg(mem, offset);
break;
case WMFW_ADSP1_DM:
BUG_ON(!mem);
region_name = "DM";
- reg = mem->base + (offset * 2);
+ reg = wm_adsp_region_to_reg(mem, offset);
break;
case WMFW_ADSP2_XM:
BUG_ON(!mem);
region_name = "XM";
- reg = mem->base + (offset * 2);
+ reg = wm_adsp_region_to_reg(mem, offset);
break;
case WMFW_ADSP2_YM:
BUG_ON(!mem);
region_name = "YM";
- reg = mem->base + (offset * 2);
+ reg = wm_adsp_region_to_reg(mem, offset);
break;
case WMFW_ADSP1_ZM:
BUG_ON(!mem);
region_name = "ZM";
- reg = mem->base + (offset * 2);
+ reg = wm_adsp_region_to_reg(mem, offset);
break;
default:
adsp_warn(dsp,
@@ -322,8 +459,16 @@ static int wm_adsp_load(struct wm_adsp *dsp)
}
if (reg) {
- ret = regmap_raw_write(regmap, reg, region->data,
- le32_to_cpu(region->len));
+ buf = wm_adsp_buf_alloc(region->data,
+ le32_to_cpu(region->len),
+ &buf_list);
+ if (!buf) {
+ adsp_err(dsp, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ ret = regmap_raw_write_async(regmap, reg, buf->buf,
+ le32_to_cpu(region->len));
if (ret != 0) {
adsp_err(dsp,
"%s.%d: Failed to write %d bytes at %d in %s: %d\n",
@@ -337,12 +482,20 @@ static int wm_adsp_load(struct wm_adsp *dsp)
pos += le32_to_cpu(region->len) + sizeof(*region);
regions++;
}
-
+
+ ret = regmap_async_complete(regmap);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+ goto out_fw;
+ }
+
if (pos > firmware->size)
adsp_warn(dsp, "%s.%d: %zu bytes at end of file\n",
file, regions, pos - firmware->size);
out_fw:
+ regmap_async_complete(regmap);
+ wm_adsp_buf_free(&buf_list);
release_firmware(firmware);
out:
kfree(file);
@@ -350,21 +503,222 @@ out:
return ret;
}
+static int wm_adsp_setup_algs(struct wm_adsp *dsp)
+{
+ struct regmap *regmap = dsp->regmap;
+ struct wmfw_adsp1_id_hdr adsp1_id;
+ struct wmfw_adsp2_id_hdr adsp2_id;
+ struct wmfw_adsp1_alg_hdr *adsp1_alg;
+ struct wmfw_adsp2_alg_hdr *adsp2_alg;
+ void *alg, *buf;
+ struct wm_adsp_alg_region *region;
+ const struct wm_adsp_region *mem;
+ unsigned int pos, term;
+ size_t algs, buf_size;
+ __be32 val;
+ int i, ret;
+
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ mem = wm_adsp_find_region(dsp, WMFW_ADSP1_DM);
+ break;
+ case WMFW_ADSP2:
+ mem = wm_adsp_find_region(dsp, WMFW_ADSP2_XM);
+ break;
+ default:
+ mem = NULL;
+ break;
+ }
+
+ if (mem == NULL) {
+ BUG_ON(mem != NULL);
+ return -EINVAL;
+ }
+
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ ret = regmap_raw_read(regmap, mem->base, &adsp1_id,
+ sizeof(adsp1_id));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ buf = &adsp1_id;
+ buf_size = sizeof(adsp1_id);
+
+ algs = be32_to_cpu(adsp1_id.algs);
+ adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+ be32_to_cpu(adsp1_id.fw.id),
+ (be32_to_cpu(adsp1_id.fw.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp1_id.fw.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp1_id.fw.ver) & 0xff,
+ algs);
+
+ pos = sizeof(adsp1_id) / 2;
+ term = pos + ((sizeof(*adsp1_alg) * algs) / 2);
+ break;
+
+ case WMFW_ADSP2:
+ ret = regmap_raw_read(regmap, mem->base, &adsp2_id,
+ sizeof(adsp2_id));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm info: %d\n",
+ ret);
+ return ret;
+ }
+
+ buf = &adsp2_id;
+ buf_size = sizeof(adsp2_id);
+
+ algs = be32_to_cpu(adsp2_id.algs);
+ adsp_info(dsp, "Firmware: %x v%d.%d.%d, %zu algorithms\n",
+ be32_to_cpu(adsp2_id.fw.id),
+ (be32_to_cpu(adsp2_id.fw.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp2_id.fw.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp2_id.fw.ver) & 0xff,
+ algs);
+
+ pos = sizeof(adsp2_id) / 2;
+ term = pos + ((sizeof(*adsp2_alg) * algs) / 2);
+ break;
+
+ default:
+ BUG_ON(NULL == "Unknown DSP type");
+ return -EINVAL;
+ }
+
+ if (algs == 0) {
+ adsp_err(dsp, "No algorithms\n");
+ return -EINVAL;
+ }
+
+ if (algs > 1024) {
+ adsp_err(dsp, "Algorithm count %zx excessive\n", algs);
+ print_hex_dump_bytes(dev_name(dsp->dev), DUMP_PREFIX_OFFSET,
+ buf, buf_size);
+ return -EINVAL;
+ }
+
+ /* Read the terminator first to validate the length */
+ ret = regmap_raw_read(regmap, mem->base + term, &val, sizeof(val));
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm list end: %d\n",
+ ret);
+ return ret;
+ }
+
+ if (be32_to_cpu(val) != 0xbedead)
+ adsp_warn(dsp, "Algorithm list end %x 0x%x != 0xbeadead\n",
+ term, be32_to_cpu(val));
+
+ alg = kzalloc((term - pos) * 2, GFP_KERNEL | GFP_DMA);
+ if (!alg)
+ return -ENOMEM;
+
+ ret = regmap_raw_read(regmap, mem->base + pos, alg, (term - pos) * 2);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read algorithm list: %d\n",
+ ret);
+ goto out;
+ }
+
+ adsp1_alg = alg;
+ adsp2_alg = alg;
+
+ for (i = 0; i < algs; i++) {
+ switch (dsp->type) {
+ case WMFW_ADSP1:
+ adsp_info(dsp, "%d: ID %x v%d.%d.%d DM@%x ZM@%x\n",
+ i, be32_to_cpu(adsp1_alg[i].alg.id),
+ (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp1_alg[i].alg.ver) & 0xff,
+ be32_to_cpu(adsp1_alg[i].dm),
+ be32_to_cpu(adsp1_alg[i].zm));
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP1_DM;
+ region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp1_alg[i].dm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP1_ZM;
+ region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp1_alg[i].zm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+ break;
+
+ case WMFW_ADSP2:
+ adsp_info(dsp,
+ "%d: ID %x v%d.%d.%d XM@%x YM@%x ZM@%x\n",
+ i, be32_to_cpu(adsp2_alg[i].alg.id),
+ (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff0000) >> 16,
+ (be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff00) >> 8,
+ be32_to_cpu(adsp2_alg[i].alg.ver) & 0xff,
+ be32_to_cpu(adsp2_alg[i].xm),
+ be32_to_cpu(adsp2_alg[i].ym),
+ be32_to_cpu(adsp2_alg[i].zm));
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_XM;
+ region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp2_alg[i].xm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_YM;
+ region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp2_alg[i].ym);
+ list_add_tail(&region->list, &dsp->alg_regions);
+
+ region = kzalloc(sizeof(*region), GFP_KERNEL);
+ if (!region)
+ return -ENOMEM;
+ region->type = WMFW_ADSP2_ZM;
+ region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
+ region->base = be32_to_cpu(adsp2_alg[i].zm);
+ list_add_tail(&region->list, &dsp->alg_regions);
+ break;
+ }
+ }
+
+out:
+ kfree(alg);
+ return ret;
+}
+
static int wm_adsp_load_coeff(struct wm_adsp *dsp)
{
+ LIST_HEAD(buf_list);
struct regmap *regmap = dsp->regmap;
struct wmfw_coeff_hdr *hdr;
struct wmfw_coeff_item *blk;
const struct firmware *firmware;
+ const struct wm_adsp_region *mem;
+ struct wm_adsp_alg_region *alg_region;
const char *region_name;
int ret, pos, blocks, type, offset, reg;
char *file;
+ struct wm_adsp_buf *buf;
+ int tmp;
file = kzalloc(PAGE_SIZE, GFP_KERNEL);
if (file == NULL)
return -ENOMEM;
- snprintf(file, PAGE_SIZE, "%s-dsp%d.bin", dsp->part, dsp->num);
+ snprintf(file, PAGE_SIZE, "%s-dsp%d-%s.bin", dsp->part, dsp->num,
+ wm_adsp_fw[dsp->fw].file);
file[PAGE_SIZE - 1] = '\0';
ret = request_firmware(&firmware, file, dsp->dev);
@@ -384,7 +738,17 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
hdr = (void*)&firmware->data[0];
if (memcmp(hdr->magic, "WMDR", 4) != 0) {
adsp_err(dsp, "%s: invalid magic\n", file);
- return -EINVAL;
+ goto out_fw;
+ }
+
+ switch (be32_to_cpu(hdr->rev) & 0xff) {
+ case 1:
+ break;
+ default:
+ adsp_err(dsp, "%s: Unsupported coefficient file format %d\n",
+ file, be32_to_cpu(hdr->rev) & 0xff);
+ ret = -EINVAL;
+ goto out_fw;
}
adsp_dbg(dsp, "%s: v%d.%d.%d\n", file,
@@ -399,8 +763,8 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
pos - firmware->size > sizeof(*blk)) {
blk = (void*)(&firmware->data[pos]);
- type = be32_to_cpu(blk->type) & 0xff;
- offset = le32_to_cpu(blk->offset) & 0xffffff;
+ type = le16_to_cpu(blk->type);
+ offset = le16_to_cpu(blk->offset);
adsp_dbg(dsp, "%s.%d: %x v%d.%d.%d\n",
file, blocks, le32_to_cpu(blk->id),
@@ -413,21 +777,65 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
reg = 0;
region_name = "Unknown";
switch (type) {
- case WMFW_NAME_TEXT:
- case WMFW_INFO_TEXT:
+ case (WMFW_NAME_TEXT << 8):
+ case (WMFW_INFO_TEXT << 8):
break;
- case WMFW_ABSOLUTE:
+ case (WMFW_ABSOLUTE << 8):
region_name = "register";
reg = offset;
break;
+
+ case WMFW_ADSP1_DM:
+ case WMFW_ADSP1_ZM:
+ case WMFW_ADSP2_XM:
+ case WMFW_ADSP2_YM:
+ adsp_dbg(dsp, "%s.%d: %d bytes in %x for %x\n",
+ file, blocks, le32_to_cpu(blk->len),
+ type, le32_to_cpu(blk->id));
+
+ mem = wm_adsp_find_region(dsp, type);
+ if (!mem) {
+ adsp_err(dsp, "No base for region %x\n", type);
+ break;
+ }
+
+ reg = 0;
+ list_for_each_entry(alg_region,
+ &dsp->alg_regions, list) {
+ if (le32_to_cpu(blk->id) == alg_region->alg &&
+ type == alg_region->type) {
+ reg = alg_region->base;
+ reg = wm_adsp_region_to_reg(mem,
+ reg);
+ reg += offset;
+ }
+ }
+
+ if (reg == 0)
+ adsp_err(dsp, "No %x for algorithm %x\n",
+ type, le32_to_cpu(blk->id));
+ break;
+
default:
- adsp_err(dsp, "Unknown region type %x\n", type);
+ adsp_err(dsp, "%s.%d: Unknown region type %x at %d\n",
+ file, blocks, type, pos);
break;
}
if (reg) {
- ret = regmap_raw_write(regmap, reg, blk->data,
- le32_to_cpu(blk->len));
+ buf = wm_adsp_buf_alloc(blk->data,
+ le32_to_cpu(blk->len),
+ &buf_list);
+ if (!buf) {
+ adsp_err(dsp, "Out of memory\n");
+ return -ENOMEM;
+ }
+
+ adsp_dbg(dsp, "%s.%d: Writing %d bytes at %x\n",
+ file, blocks, le32_to_cpu(blk->len),
+ reg);
+ ret = regmap_raw_write_async(regmap, reg, buf->buf,
+ le32_to_cpu(blk->len));
if (ret != 0) {
adsp_err(dsp,
"%s.%d: Failed to write to %x in %s\n",
@@ -435,21 +843,39 @@ static int wm_adsp_load_coeff(struct wm_adsp *dsp)
}
}
- pos += le32_to_cpu(blk->len) + sizeof(*blk);
+ tmp = le32_to_cpu(blk->len) % 4;
+ if (tmp)
+ pos += le32_to_cpu(blk->len) + (4 - tmp) + sizeof(*blk);
+ else
+ pos += le32_to_cpu(blk->len) + sizeof(*blk);
+
blocks++;
}
+ ret = regmap_async_complete(regmap);
+ if (ret != 0)
+ adsp_err(dsp, "Failed to complete async write: %d\n", ret);
+
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);
+ wm_adsp_buf_free(&buf_list);
out:
kfree(file);
return 0;
}
+int wm_adsp1_init(struct wm_adsp *adsp)
+{
+ INIT_LIST_HEAD(&adsp->alg_regions);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp1_init);
+
int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event)
@@ -458,16 +884,46 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
struct wm_adsp *dsp = &dsps[w->shift];
int ret;
+ int val;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_SYS_ENA, ADSP1_SYS_ENA);
+ /*
+ * For simplicity set the DSP clock rate to be the
+ * SYSCLK rate rather than making it configurable.
+ */
+ if(dsp->sysclk_reg) {
+ ret = regmap_read(dsp->regmap, dsp->sysclk_reg, &val);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to read SYSCLK state: %d\n",
+ ret);
+ return ret;
+ }
+
+ val = (val & dsp->sysclk_mask)
+ >> dsp->sysclk_shift;
+
+ ret = regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP1_CONTROL_31,
+ ADSP1_CLK_SEL_MASK, val);
+ if (ret != 0) {
+ adsp_err(dsp, "Failed to set clock rate: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
ret = wm_adsp_load(dsp);
if (ret != 0)
goto err;
+ ret = wm_adsp_setup_algs(dsp);
+ if (ret != 0)
+ goto err;
+
ret = wm_adsp_load_coeff(dsp);
if (ret != 0)
goto err;
@@ -539,6 +995,7 @@ int wm_adsp2_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_adsp_alg_region *alg_region;
unsigned int val;
int ret;
@@ -604,6 +1061,10 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ ret = wm_adsp_setup_algs(dsp);
+ if (ret != 0)
+ goto err;
+
ret = wm_adsp_load_coeff(dsp);
if (ret != 0)
goto err;
@@ -614,13 +1075,22 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
ADSP2_CORE_ENA | ADSP2_START);
if (ret != 0)
goto err;
+
+ dsp->running = true;
break;
case SND_SOC_DAPM_PRE_PMD:
+ dsp->running = false;
+
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
ADSP2_SYS_ENA | ADSP2_CORE_ENA |
ADSP2_START, 0);
+ /* Make sure DMAs are quiesced */
+ regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_1, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2_WDMA_CONFIG_2, 0);
+ regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
+
if (dsp->dvfs) {
ret = regulator_set_voltage(dsp->dvfs, 1200000,
1800000);
@@ -635,6 +1105,14 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
"Failed to enable supply: %d\n",
ret);
}
+
+ while (!list_empty(&dsp->alg_regions)) {
+ alg_region = list_first_entry(&dsp->alg_regions,
+ struct wm_adsp_alg_region,
+ list);
+ list_del(&alg_region->list);
+ kfree(alg_region);
+ }
break;
default:
@@ -664,6 +1142,8 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
return ret;
}
+ INIT_LIST_HEAD(&adsp->alg_regions);
+
if (dvfs) {
adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
if (IS_ERR(adsp->dvfs)) {
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index ffd29a4609e2..cb8871a3ec00 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -25,6 +25,13 @@ struct wm_adsp_region {
unsigned int base;
};
+struct wm_adsp_alg_region {
+ struct list_head list;
+ unsigned int alg;
+ int type;
+ unsigned int base;
+};
+
struct wm_adsp {
const char *part;
int num;
@@ -33,10 +40,18 @@ struct wm_adsp {
struct regmap *regmap;
int base;
+ int sysclk_reg;
+ int sysclk_mask;
+ int sysclk_shift;
+
+ struct list_head alg_regions;
const struct wm_adsp_region *mem;
int num_mems;
+ int fw;
+ bool running;
+
struct regulator *dvfs;
};
@@ -50,6 +65,9 @@ struct wm_adsp {
.shift = num, .event = wm_adsp2_event, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
+extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
+
+int wm_adsp1_init(struct wm_adsp *adsp);
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);
diff --git a/sound/soc/codecs/wmfw.h b/sound/soc/codecs/wmfw.h
index 5632ded67fdd..ef163360a745 100644
--- a/sound/soc/codecs/wmfw.h
+++ b/sound/soc/codecs/wmfw.h
@@ -93,15 +93,20 @@ struct wmfw_adsp2_alg_hdr {
struct wmfw_coeff_hdr {
u8 magic[4];
__le32 len;
- __le32 ver;
+ union {
+ __be32 rev;
+ __le32 ver;
+ };
+ union {
+ __be32 core;
+ __le32 core_ver;
+ };
u8 data[];
} __packed;
struct wmfw_coeff_item {
- union {
- __be32 type;
- __le32 offset;
- };
+ __le16 offset;
+ __le16 type;
__le32 id;
__le32 ver;
__le32 sr;
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 55e2bf652bef..9321e5c9d8c1 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -626,7 +626,7 @@ static int davinci_config_channel_size(struct davinci_audio_dev *dev,
int word_length)
{
u32 fmt;
- u32 rotate = (32 - word_length) / 4;
+ u32 rotate = (word_length / 4) & 0x7;
u32 mask = (1ULL << word_length) - 1;
/*
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index 1aa51300c564..deb30d59965e 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -210,15 +210,19 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
switch (config->chan_nr) {
case EIGHT_CHANNEL_SUPPORT:
ch_reg = 3;
+ break;
case SIX_CHANNEL_SUPPORT:
ch_reg = 2;
+ break;
case FOUR_CHANNEL_SUPPORT:
ch_reg = 1;
+ break;
case TWO_CHANNEL_SUPPORT:
ch_reg = 0;
break;
default:
dev_err(dev->dev, "channel not supported\n");
+ return -EINVAL;
}
i2s_disable_channels(dev, substream->stream);
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index bf363d8d044a..500f8ce55d78 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -154,26 +154,7 @@ static struct snd_soc_platform_driver imx_soc_platform_mx2 = {
.pcm_free = imx_pcm_free,
};
-static int imx_soc_platform_probe(struct platform_device *pdev)
+int imx_pcm_dma_init(struct platform_device *pdev)
{
return snd_soc_register_platform(&pdev->dev, &imx_soc_platform_mx2);
}
-
-static int imx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver imx_pcm_driver = {
- .driver = {
- .name = "imx-pcm-audio",
- .owner = THIS_MODULE,
- },
- .probe = imx_soc_platform_probe,
- .remove = imx_soc_platform_remove,
-};
-
-module_platform_driver(imx_pcm_driver);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:imx-pcm-audio");
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index 5ec362ae4d01..920f945cb2f4 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -281,7 +281,7 @@ static struct snd_soc_platform_driver imx_soc_platform_fiq = {
.pcm_free = imx_pcm_fiq_free,
};
-static int imx_soc_platform_probe(struct platform_device *pdev)
+int imx_pcm_fiq_init(struct platform_device *pdev)
{
struct imx_ssi *ssi = platform_get_drvdata(pdev);
int ret;
@@ -314,23 +314,3 @@ failed_register:
return ret;
}
-
-static int imx_soc_platform_remove(struct platform_device *pdev)
-{
- snd_soc_unregister_platform(&pdev->dev);
- return 0;
-}
-
-static struct platform_driver imx_pcm_driver = {
- .driver = {
- .name = "imx-fiq-pcm-audio",
- .owner = THIS_MODULE,
- },
-
- .probe = imx_soc_platform_probe,
- .remove = imx_soc_platform_remove,
-};
-
-module_platform_driver(imx_pcm_driver);
-
-MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm.c b/sound/soc/fsl/imx-pcm.c
index d5cd9eff3b48..0d0625bfcb65 100644
--- a/sound/soc/fsl/imx-pcm.c
+++ b/sound/soc/fsl/imx-pcm.c
@@ -104,6 +104,38 @@ void imx_pcm_free(struct snd_pcm *pcm)
}
EXPORT_SYMBOL_GPL(imx_pcm_free);
+static int imx_pcm_probe(struct platform_device *pdev)
+{
+ if (strcmp(pdev->id_entry->name, "imx-fiq-pcm-audio") == 0)
+ return imx_pcm_fiq_init(pdev);
+
+ return imx_pcm_dma_init(pdev);
+}
+
+static int imx_pcm_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_platform(&pdev->dev);
+ return 0;
+}
+
+static struct platform_device_id imx_pcm_devtype[] = {
+ { .name = "imx-pcm-audio", },
+ { .name = "imx-fiq-pcm-audio", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, imx_pcm_devtype);
+
+static struct platform_driver imx_pcm_driver = {
+ .driver = {
+ .name = "imx-pcm",
+ .owner = THIS_MODULE,
+ },
+ .id_table = imx_pcm_devtype,
+ .probe = imx_pcm_probe,
+ .remove = imx_pcm_remove,
+};
+module_platform_driver(imx_pcm_driver);
+
MODULE_DESCRIPTION("Freescale i.MX PCM driver");
MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/fsl/imx-pcm.h b/sound/soc/fsl/imx-pcm.h
index 83c0ed7d55c9..5ae13a13a353 100644
--- a/sound/soc/fsl/imx-pcm.h
+++ b/sound/soc/fsl/imx-pcm.h
@@ -30,4 +30,22 @@ int snd_imx_pcm_mmap(struct snd_pcm_substream *substream,
int imx_pcm_new(struct snd_soc_pcm_runtime *rtd);
void imx_pcm_free(struct snd_pcm *pcm);
+#ifdef CONFIG_SND_SOC_IMX_PCM_DMA
+int imx_pcm_dma_init(struct platform_device *pdev);
+#else
+static inline int imx_pcm_dma_init(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+#endif
+
+#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ
+int imx_pcm_fiq_init(struct platform_device *pdev);
+#else
+static inline int imx_pcm_fiq_init(struct platform_device *pdev)
+{
+ return -ENODEV;
+}
+#endif
+
#endif /* _IMX_PCM_H */
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index ee10e8704e97..13f6dd1ceb00 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -469,7 +469,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
{
int ret = 0;
- ret = s3c_i2sv2_register_dai(&pdev->dev, -1, &s3c2412_i2s_dai);
+ ret = snd_soc_register_dai(&pdev->dev, &s3c24xx_i2s_dai);
if (ret) {
pr_err("failed to register the dai\n");
return ret;
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 5fbfb06e8083..b5b3db71e253 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -33,6 +33,8 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret = 0;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (platform->driver->compr_ops && platform->driver->compr_ops->open) {
ret = platform->driver->compr_ops->open(cstream);
if (ret < 0) {
@@ -61,15 +63,46 @@ static int soc_compr_open(struct snd_compr_stream *cstream)
codec_dai->active++;
rtd->codec->active++;
+ mutex_unlock(&rtd->pcm_mutex);
+
return 0;
machine_err:
if (platform->driver->compr_ops && platform->driver->compr_ops->free)
platform->driver->compr_ops->free(cstream);
out:
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
+/*
+ * Power down the audio subsystem pmdown_time msecs after close is called.
+ * This is to ensure there are no pops or clicks in between any music tracks
+ * due to DAPM power cycling.
+ */
+static void close_delayed_work(struct work_struct *work)
+{
+ struct snd_soc_pcm_runtime *rtd =
+ container_of(work, struct snd_soc_pcm_runtime, delayed_work.work);
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ dev_dbg(rtd->dev, "ASoC: pop wq checking: %s status: %s waiting: %s\n",
+ codec_dai->driver->playback.stream_name,
+ codec_dai->playback_active ? "active" : "inactive",
+ rtd->pop_wait ? "yes" : "no");
+
+ /* are we waiting on this codec DAI stream */
+ if (rtd->pop_wait == 1) {
+ rtd->pop_wait = 0;
+ snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
+ SND_SOC_DAPM_STREAM_STOP);
+ }
+
+ mutex_unlock(&rtd->pcm_mutex);
+}
+
static int soc_compr_free(struct snd_compr_stream *cstream)
{
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -78,6 +111,8 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (cstream->direction == SND_COMPRESS_PLAYBACK) {
cpu_dai->playback_active--;
codec_dai->playback_active--;
@@ -86,7 +121,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
codec_dai->capture_active--;
}
- snd_soc_dai_digital_mute(codec_dai, 1);
+ snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
cpu_dai->active--;
codec_dai->active--;
@@ -112,10 +147,11 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
snd_soc_dapm_stream_event(rtd,
SNDRV_PCM_STREAM_PLAYBACK,
SND_SOC_DAPM_STREAM_STOP);
- } else
+ } else {
rtd->pop_wait = 1;
schedule_delayed_work(&rtd->delayed_work,
msecs_to_jiffies(rtd->pmdown_time));
+ }
} else {
/* capture streams can be powered down now */
snd_soc_dapm_stream_event(rtd,
@@ -123,6 +159,7 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
SND_SOC_DAPM_STREAM_STOP);
}
+ mutex_unlock(&rtd->pcm_mutex);
return 0;
}
@@ -134,17 +171,25 @@ static int soc_compr_trigger(struct snd_compr_stream *cstream, int cmd)
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret = 0;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (platform->driver->compr_ops && platform->driver->compr_ops->trigger) {
ret = platform->driver->compr_ops->trigger(cstream, cmd);
if (ret < 0)
- return ret;
+ goto out;
}
- if (cmd == SNDRV_PCM_TRIGGER_START)
- snd_soc_dai_digital_mute(codec_dai, 0);
- else if (cmd == SNDRV_PCM_TRIGGER_STOP)
- snd_soc_dai_digital_mute(codec_dai, 1);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ snd_soc_dai_digital_mute(codec_dai, 0, cstream->direction);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ snd_soc_dai_digital_mute(codec_dai, 1, cstream->direction);
+ break;
+ }
+out:
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -155,6 +200,8 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
struct snd_soc_platform *platform = rtd->platform;
int ret = 0;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
/* first we call set_params for the platform driver
* this should configure the soc side
* if the machine has compressed ops then we call that as well
@@ -164,18 +211,20 @@ static int soc_compr_set_params(struct snd_compr_stream *cstream,
if (platform->driver->compr_ops && platform->driver->compr_ops->set_params) {
ret = platform->driver->compr_ops->set_params(cstream, params);
if (ret < 0)
- return ret;
+ goto out;
}
if (rtd->dai_link->compr_ops && rtd->dai_link->compr_ops->set_params) {
ret = rtd->dai_link->compr_ops->set_params(cstream);
if (ret < 0)
- return ret;
+ goto out;
}
snd_soc_dapm_stream_event(rtd, SNDRV_PCM_STREAM_PLAYBACK,
SND_SOC_DAPM_STREAM_START);
+out:
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -186,9 +235,12 @@ static int soc_compr_get_params(struct snd_compr_stream *cstream,
struct snd_soc_platform *platform = rtd->platform;
int ret = 0;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (platform->driver->compr_ops && platform->driver->compr_ops->get_params)
ret = platform->driver->compr_ops->get_params(cstream, params);
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -199,9 +251,12 @@ static int soc_compr_get_caps(struct snd_compr_stream *cstream,
struct snd_soc_platform *platform = rtd->platform;
int ret = 0;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (platform->driver->compr_ops && platform->driver->compr_ops->get_caps)
ret = platform->driver->compr_ops->get_caps(cstream, caps);
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -212,9 +267,12 @@ static int soc_compr_get_codec_caps(struct snd_compr_stream *cstream,
struct snd_soc_platform *platform = rtd->platform;
int ret = 0;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (platform->driver->compr_ops && platform->driver->compr_ops->get_codec_caps)
ret = platform->driver->compr_ops->get_codec_caps(cstream, codec);
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -224,9 +282,12 @@ static int soc_compr_ack(struct snd_compr_stream *cstream, size_t bytes)
struct snd_soc_platform *platform = rtd->platform;
int ret = 0;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (platform->driver->compr_ops && platform->driver->compr_ops->ack)
ret = platform->driver->compr_ops->ack(cstream, bytes);
+ mutex_unlock(&rtd->pcm_mutex);
return ret;
}
@@ -236,12 +297,31 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_soc_platform *platform = rtd->platform;
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
if (platform->driver->compr_ops && platform->driver->compr_ops->pointer)
platform->driver->compr_ops->pointer(cstream, tstamp);
+ mutex_unlock(&rtd->pcm_mutex);
return 0;
}
+static int soc_compr_copy(struct snd_compr_stream *cstream,
+ const char __user *buf, size_t count)
+{
+ struct snd_soc_pcm_runtime *rtd = cstream->private_data;
+ struct snd_soc_platform *platform = rtd->platform;
+ int ret = 0;
+
+ mutex_lock_nested(&rtd->pcm_mutex, rtd->pcm_subclass);
+
+ if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
+ ret = platform->driver->compr_ops->copy(cstream, buf, count);
+
+ mutex_unlock(&rtd->pcm_mutex);
+ return ret;
+}
+
/* ASoC Compress operations */
static struct snd_compr_ops soc_compr_ops = {
.open = soc_compr_open,
@@ -259,6 +339,7 @@ static struct snd_compr_ops soc_compr_ops = {
int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
{
struct snd_soc_codec *codec = rtd->codec;
+ struct snd_soc_platform *platform = rtd->platform;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_compr *compr;
@@ -275,20 +356,38 @@ int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
return -ENOMEM;
}
- compr->ops = &soc_compr_ops;
+ compr->ops = devm_kzalloc(rtd->card->dev, sizeof(soc_compr_ops),
+ GFP_KERNEL);
+ if (compr->ops == NULL) {
+ dev_err(rtd->card->dev, "Cannot allocate compressed ops\n");
+ ret = -ENOMEM;
+ goto compr_err;
+ }
+ memcpy(compr->ops, &soc_compr_ops, sizeof(soc_compr_ops));
+
+ /* Add copy callback for not memory mapped DSPs */
+ if (platform->driver->compr_ops && platform->driver->compr_ops->copy)
+ compr->ops->copy = soc_compr_copy;
+
mutex_init(&compr->lock);
ret = snd_compress_new(rtd->card->snd_card, num, direction, compr);
if (ret < 0) {
pr_err("compress asoc: can't create compress for codec %s\n",
codec->name);
- kfree(compr);
- return ret;
+ goto compr_err;
}
+ /* DAPM dai link stream work */
+ INIT_DELAYED_WORK(&rtd->delayed_work, close_delayed_work);
+
rtd->compr = compr;
compr->private_data = rtd;
printk(KERN_INFO "compress asoc: %s <-> %s mapping ok\n", codec_dai->name,
cpu_dai->name);
return ret;
+
+compr_err:
+ kfree(compr);
+ return ret;
}
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 91d592ff67b7..7365afbc5e18 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1107,6 +1107,10 @@ static int soc_probe_codec(struct snd_soc_card *card,
"ASoC: failed to probe CODEC %d\n", ret);
goto err_probe;
}
+ WARN(codec->dapm.idle_bias_off &&
+ codec->dapm.bias_level != SND_SOC_BIAS_OFF,
+ "codec %s can not start from non-off bias"
+ " with idle_bias_off==1\n", codec->name);
}
/* If the driver didn't set I/O up try regmap */
@@ -1255,6 +1259,8 @@ static int soc_post_component_init(struct snd_soc_card *card,
INIT_LIST_HEAD(&rtd->dpcm[SNDRV_PCM_STREAM_CAPTURE].fe_clients);
ret = device_add(rtd->dev);
if (ret < 0) {
+ /* calling put_device() here to free the rtd->dev */
+ put_device(rtd->dev);
dev_err(card->dev,
"ASoC: failed to register runtime device: %d\n", ret);
return ret;
@@ -1554,7 +1560,7 @@ static void soc_remove_aux_dev(struct snd_soc_card *card, int num)
/* unregister the rtd device */
if (rtd->dev_registered) {
device_remove_file(rtd->dev, &dev_attr_codec_reg);
- device_del(rtd->dev);
+ device_unregister(rtd->dev);
rtd->dev_registered = 0;
}
@@ -2917,7 +2923,7 @@ int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
platform_max = mc->platform_max;
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
+ uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
uinfo->value.integer.min = 0;
uinfo->value.integer.max = platform_max - min;
@@ -2941,12 +2947,14 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
+ unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
int min = mc->min;
int max = mc->max;
unsigned int mask = (1 << fls(max)) - 1;
unsigned int invert = mc->invert;
unsigned int val, val_mask;
+ int ret;
val = ((ucontrol->value.integer.value[0] + min) & mask);
if (invert)
@@ -2954,7 +2962,21 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
val_mask = mask << shift;
val = val << shift;
- return snd_soc_update_bits_locked(codec, reg, val_mask, val);
+ ret = snd_soc_update_bits_locked(codec, reg, val_mask, val);
+ if (ret != 0)
+ return ret;
+
+ if (snd_soc_volsw_is_stereo(mc)) {
+ val = ((ucontrol->value.integer.value[1] + min) & mask);
+ if (invert)
+ val = max - val;
+ val_mask = mask << shift;
+ val = val << shift;
+
+ ret = snd_soc_update_bits_locked(codec, rreg, val_mask, val);
+ }
+
+ return ret;
}
EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
@@ -2974,6 +2996,7 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
(struct soc_mixer_control *)kcontrol->private_value;
struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
unsigned int reg = mc->reg;
+ unsigned int rreg = mc->rreg;
unsigned int shift = mc->shift;
int min = mc->min;
int max = mc->max;
@@ -2988,6 +3011,16 @@ int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
ucontrol->value.integer.value[0] =
ucontrol->value.integer.value[0] - min;
+ if (snd_soc_volsw_is_stereo(mc)) {
+ ucontrol->value.integer.value[1] =
+ (snd_soc_read(codec, rreg) >> shift) & mask;
+ if (invert)
+ ucontrol->value.integer.value[1] =
+ max - ucontrol->value.integer.value[1];
+ ucontrol->value.integer.value[1] =
+ ucontrol->value.integer.value[1] - min;
+ }
+
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
@@ -3093,9 +3126,12 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
if (!codec->using_regmap)
return -EINVAL;
- data = ucontrol->value.bytes.data;
len = params->num_regs * codec->val_bytes;
+ data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
/*
* If we've got a mask then we need to preserve the register
* bits. We shouldn't modify the incoming data so take a
@@ -3108,10 +3144,6 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
val &= params->mask;
- data = kmemdup(data, len, GFP_KERNEL);
- if (!data)
- return -ENOMEM;
-
switch (codec->val_bytes) {
case 1:
((u8 *)data)[0] &= ~params->mask;
@@ -3133,8 +3165,7 @@ int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
ret = regmap_raw_write(codec->control_data, params->base,
data, len);
- if (params->mask)
- kfree(data);
+ kfree(data);
return ret;
}
@@ -3511,12 +3542,20 @@ EXPORT_SYMBOL_GPL(snd_soc_dai_set_tristate);
* snd_soc_dai_digital_mute - configure DAI system or master clock.
* @dai: DAI
* @mute: mute enable
+ * @direction: stream to mute
*
* Mutes the DAI DAC.
*/
-int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute)
+int snd_soc_dai_digital_mute(struct snd_soc_dai *dai, int mute,
+ int direction)
{
- if (dai->driver && dai->driver->ops->digital_mute)
+ if (!dai->driver)
+ return -ENOTSUPP;
+
+ if (dai->driver->ops->mute_stream)
+ return dai->driver->ops->mute_stream(dai, mute, direction);
+ else if (direction == SNDRV_PCM_STREAM_PLAYBACK &&
+ dai->driver->ops->digital_mute)
return dai->driver->ops->digital_mute(dai, mute);
else
return -ENOTSUPP;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index 1e36bc81e5af..1d6a9b3ceb27 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -1023,7 +1023,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
if (SND_SOC_DAPM_EVENT_ON(event)) {
if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
- ret = regulator_allow_bypass(w->regulator, true);
+ ret = regulator_allow_bypass(w->regulator, false);
if (ret != 0)
dev_warn(w->dapm->dev,
"ASoC: Failed to bypass %s: %d\n",
@@ -1033,7 +1033,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
return regulator_enable(w->regulator);
} else {
if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
- ret = regulator_allow_bypass(w->regulator, false);
+ ret = regulator_allow_bypass(w->regulator, true);
if (ret != 0)
dev_warn(w->dapm->dev,
"ASoC: Failed to unbypass %s: %d\n",
@@ -3039,6 +3039,14 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
w->name, ret);
return NULL;
}
+
+ if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+ ret = regulator_allow_bypass(w->regulator, true);
+ if (ret != 0)
+ dev_warn(w->dapm->dev,
+ "ASoC: Failed to unbypass %s: %d\n",
+ w->name, ret);
+ }
break;
case snd_soc_dapm_clock_supply:
#ifdef CONFIG_CLKDEV_LOOKUP
@@ -3247,14 +3255,16 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
break;
case SND_SOC_DAPM_POST_PMU:
- ret = snd_soc_dai_digital_mute(sink, 0);
+ ret = snd_soc_dai_digital_mute(sink, 0,
+ SNDRV_PCM_STREAM_PLAYBACK);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(sink->dev, "ASoC: Failed to unmute: %d\n", ret);
ret = 0;
break;
case SND_SOC_DAPM_PRE_PMD:
- ret = snd_soc_dai_digital_mute(sink, 1);
+ ret = snd_soc_dai_digital_mute(sink, 1,
+ SNDRV_PCM_STREAM_PLAYBACK);
if (ret != 0 && ret != -ENOTSUPP)
dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret);
ret = 0;
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index d7711fce119b..73bb8eefa491 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -383,8 +383,7 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
/* Muting the DAC suppresses artifacts caused during digital
* shutdown, for example from stopping clocks.
*/
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
- snd_soc_dai_digital_mute(codec_dai, 1);
+ snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
if (cpu_dai->driver->ops->shutdown)
cpu_dai->driver->ops->shutdown(substream, cpu_dai);
@@ -488,7 +487,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
snd_soc_dapm_stream_event(rtd, substream->stream,
SND_SOC_DAPM_STREAM_START);
- snd_soc_dai_digital_mute(codec_dai, 0);
+ snd_soc_dai_digital_mute(codec_dai, 0, substream->stream);
out:
mutex_unlock(&rtd->pcm_mutex);
@@ -586,7 +585,7 @@ static int soc_pcm_hw_free(struct snd_pcm_substream *substream)
/* apply codec digital mute */
if (!codec->active)
- snd_soc_dai_digital_mute(codec_dai, 1);
+ snd_soc_dai_digital_mute(codec_dai, 1, substream->stream);
/* free any machine hw params */
if (rtd->dai_link->ops && rtd->dai_link->ops->hw_free)
@@ -1243,6 +1242,7 @@ static int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
+ (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PAUSED) &&
(be->dpcm[stream].state != SND_SOC_DPCM_STATE_STOP))
continue;
@@ -1728,20 +1728,16 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
/* startup must always be called for new BEs */
ret = dpcm_be_dai_startup(fe, stream);
- if (ret < 0) {
+ if (ret < 0)
goto disconnect;
- return ret;
- }
/* keep going if FE state is > open */
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_OPEN)
return 0;
ret = dpcm_be_dai_hw_params(fe, stream);
- if (ret < 0) {
+ if (ret < 0)
goto close;
- return ret;
- }
/* keep going if FE state is > hw_params */
if (fe->dpcm[stream].state == SND_SOC_DPCM_STATE_HW_PARAMS)
@@ -1749,10 +1745,8 @@ static int dpcm_run_update_startup(struct snd_soc_pcm_runtime *fe, int stream)
ret = dpcm_be_dai_prepare(fe, stream);
- if (ret < 0) {
+ if (ret < 0)
goto hw_free;
- return ret;
- }
/* run the stream event for each BE */
dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_NOP);
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index ae6990738783..204b899c2311 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -24,7 +24,7 @@
#include "ux500_pcm.h"
#include "ux500_msp_dai.h"
-#include <mop500_ab8500.h>
+#include "mop500_ab8500.h"
/* Define the whole MOP500 soundcard, linking platform to the codec-drivers */
struct snd_soc_dai_link mop500_dai_links[] = {